1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. Oracle designates this 9 * particular file as subject to the "Classpath" exception as provided 10 * by Oracle in the LICENSE file that accompanied this code. 11 * 12 * This code is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 * version 2 for more details (a copy is included in the LICENSE file that 16 * accompanied this code). 17 * 18 * You should have received a copy of the GNU General Public License version 19 * 2 along with this work; if not, write to the Free Software Foundation, 20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 21 * 22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 23 * or visit www.oracle.com if you need additional information or have any 24 * questions. 25 */ 26 27 package java.lang; 28 29 import java.lang.reflect.AnnotatedElement; 30 import java.io.InputStream; 31 import java.util.Enumeration; 32 33 import java.util.StringTokenizer; 34 import java.io.File; 35 import java.io.FileInputStream; 36 import java.io.FileNotFoundException; 37 import java.io.IOException; 38 import java.net.URL; 39 import java.net.MalformedURLException; 40 import java.security.AccessController; 41 import java.security.PrivilegedAction; 42 43 import java.util.jar.JarInputStream; 44 import java.util.jar.Manifest; 45 import java.util.jar.Attributes; 46 import java.util.jar.Attributes.Name; 47 import java.util.jar.JarException; 48 import java.util.Map; 49 import java.util.HashMap; 50 import java.util.Iterator; 51 52 import sun.net.www.ParseUtil; 53 import sun.reflect.CallerSensitive; 54 import dalvik.system.VMRuntime; 55 import dalvik.system.VMStack; 56 57 import java.lang.annotation.Annotation; 58 59 /** 60 * {@code Package} objects contain version information 61 * about the implementation and specification of a Java package. 62 * This versioning information is retrieved and made available 63 * by the {@link ClassLoader} instance that 64 * loaded the class(es). Typically, it is stored in the manifest that is 65 * distributed with the classes. 66 * 67 * <p>The set of classes that make up the package may implement a 68 * particular specification and if so the specification title, version number, 69 * and vendor strings identify that specification. 70 * An application can ask if the package is 71 * compatible with a particular version, see the {@link 72 * #isCompatibleWith isCompatibleWith} 73 * method for details. 74 * 75 * <p>Specification version numbers use a syntax that consists of nonnegative 76 * decimal integers separated by periods ".", for example "2.0" or 77 * "1.2.3.4.5.6.7". This allows an extensible number to be used to represent 78 * major, minor, micro, etc. versions. The version specification is described 79 * by the following formal grammar: 80 * <blockquote> 81 * <dl> 82 * <dt><i>SpecificationVersion:</i> 83 * <dd><i>Digits RefinedVersion<sub>opt</sub></i> 84 85 * <dt><i>RefinedVersion:</i> 86 * <dd>{@code .} <i>Digits</i> 87 * <dd>{@code .} <i>Digits RefinedVersion</i> 88 * 89 * <dt><i>Digits:</i> 90 * <dd><i>Digit</i> 91 * <dd><i>Digits</i> 92 * 93 * <dt><i>Digit:</i> 94 * <dd>any character for which {@link Character#isDigit} returns {@code true}, 95 * e.g. 0, 1, 2, ... 96 * </dl> 97 * </blockquote> 98 * 99 * <p>The implementation title, version, and vendor strings identify an 100 * implementation and are made available conveniently to enable accurate 101 * reporting of the packages involved when a problem occurs. The contents 102 * all three implementation strings are vendor specific. The 103 * implementation version strings have no specified syntax and should 104 * only be compared for equality with desired version identifiers. 105 * 106 * <p>Within each {@code ClassLoader} instance all classes from the same 107 * java package have the same Package object. The static methods allow a package 108 * to be found by name or the set of all packages known to the current class 109 * loader to be found. 110 * 111 * @see ClassLoader#definePackage 112 */ 113 public class Package implements java.lang.reflect.AnnotatedElement { 114 /** 115 * Return the name of this package. 116 * 117 * @return The fully-qualified name of this package as defined in section 6.5.3 of 118 * <cite>The Java™ Language Specification</cite>, 119 * for example, {@code java.lang} 120 */ 121 public String getName() { 122 return pkgName; 123 } 124 125 126 /** 127 * Return the title of the specification that this package implements. 128 * @return the specification title, null is returned if it is not known. 129 */ 130 public String getSpecificationTitle() { 131 return specTitle; 132 } 133 134 /** 135 * Returns the version number of the specification 136 * that this package implements. 137 * This version string must be a sequence of nonnegative decimal 138 * integers separated by "."'s and may have leading zeros. 139 * When version strings are compared the most significant 140 * numbers are compared. 141 * @return the specification version, null is returned if it is not known. 142 */ 143 public String getSpecificationVersion() { 144 return specVersion; 145 } 146 147 /** 148 * Return the name of the organization, vendor, 149 * or company that owns and maintains the specification 150 * of the classes that implement this package. 151 * @return the specification vendor, null is returned if it is not known. 152 */ 153 public String getSpecificationVendor() { 154 return specVendor; 155 } 156 157 /** 158 * Return the title of this package. 159 * @return the title of the implementation, null is returned if it is not known. 160 */ 161 public String getImplementationTitle() { 162 return implTitle; 163 } 164 165 /** 166 * Return the version of this implementation. It consists of any string 167 * assigned by the vendor of this implementation and does 168 * not have any particular syntax specified or expected by the Java 169 * runtime. It may be compared for equality with other 170 * package version strings used for this implementation 171 * by this vendor for this package. 172 * @return the version of the implementation, null is returned if it is not known. 173 */ 174 public String getImplementationVersion() { 175 return implVersion; 176 } 177 178 /** 179 * Returns the name of the organization, 180 * vendor or company that provided this implementation. 181 * @return the vendor that implemented this package.. 182 */ 183 public String getImplementationVendor() { 184 return implVendor; 185 } 186 187 /** 188 * Returns true if this package is sealed. 189 * 190 * @return true if the package is sealed, false otherwise 191 */ 192 public boolean isSealed() { 193 return sealBase != null; 194 } 195 196 /** 197 * Returns true if this package is sealed with respect to the specified 198 * code source url. 199 * 200 * @param url the code source url 201 * @return true if this package is sealed with respect to url 202 */ 203 public boolean isSealed(URL url) { 204 return url.equals(sealBase); 205 } 206 207 /** 208 * Compare this package's specification version with a 209 * desired version. It returns true if 210 * this packages specification version number is greater than or equal 211 * to the desired version number. <p> 212 * 213 * Version numbers are compared by sequentially comparing corresponding 214 * components of the desired and specification strings. 215 * Each component is converted as a decimal integer and the values 216 * compared. 217 * If the specification value is greater than the desired 218 * value true is returned. If the value is less false is returned. 219 * If the values are equal the period is skipped and the next pair of 220 * components is compared. 221 * 222 * @param desired the version string of the desired version. 223 * @return true if this package's version number is greater 224 * than or equal to the desired version number 225 * 226 * @exception NumberFormatException if the desired or current version 227 * is not of the correct dotted form. 228 */ 229 public boolean isCompatibleWith(String desired) 230 throws NumberFormatException 231 { 232 if (specVersion == null || specVersion.length() < 1) { 233 throw new NumberFormatException("Empty version string"); 234 } 235 236 String [] sa = specVersion.split("\\.", -1); 237 int [] si = new int[sa.length]; 238 for (int i = 0; i < sa.length; i++) { 239 si[i] = Integer.parseInt(sa[i]); 240 if (si[i] < 0) 241 throw NumberFormatException.forInputString("" + si[i]); 242 } 243 244 String [] da = desired.split("\\.", -1); 245 int [] di = new int[da.length]; 246 for (int i = 0; i < da.length; i++) { 247 di[i] = Integer.parseInt(da[i]); 248 if (di[i] < 0) 249 throw NumberFormatException.forInputString("" + di[i]); 250 } 251 252 int len = Math.max(di.length, si.length); 253 for (int i = 0; i < len; i++) { 254 int d = (i < di.length ? di[i] : 0); 255 int s = (i < si.length ? si[i] : 0); 256 if (s < d) 257 return false; 258 if (s > d) 259 return true; 260 } 261 return true; 262 } 263 264 /** 265 * Find a package by name in the callers {@code ClassLoader} instance. 266 * The callers {@code ClassLoader} instance is used to find the package 267 * instance corresponding to the named class. If the callers 268 * {@code ClassLoader} instance is null then the set of packages loaded 269 * by the system {@code ClassLoader} instance is searched to find the 270 * named package. <p> 271 * 272 * Packages have attributes for versions and specifications only if the class 273 * loader created the package instance with the appropriate attributes. Typically, 274 * those attributes are defined in the manifests that accompany the classes. 275 * 276 * @param name a package name, for example, java.lang. 277 * @return the package of the requested name. It may be null if no package 278 * information is available from the archive or codebase. 279 */ 280 @CallerSensitive 281 public static Package getPackage(String name) { 282 // Android-changed: Use VMStack.getCallingClassLoader() to obtain the classloader. 283 // ClassLoader l = ClassLoader.getClassLoader(Reflection.getCallerClass()); 284 ClassLoader l = VMStack.getCallingClassLoader(); 285 if (l != null) { 286 return l.getPackage(name); 287 } else { 288 return getSystemPackage(name); 289 } 290 } 291 292 /** 293 * Get all the packages currently known for the caller's {@code ClassLoader} 294 * instance. Those packages correspond to classes loaded via or accessible by 295 * name to that {@code ClassLoader} instance. If the caller's 296 * {@code ClassLoader} instance is the bootstrap {@code ClassLoader} 297 * instance, which may be represented by {@code null} in some implementations, 298 * only packages corresponding to classes loaded by the bootstrap 299 * {@code ClassLoader} instance will be returned. 300 * 301 * @return a new array of packages known to the callers {@code ClassLoader} 302 * instance. An zero length array is returned if none are known. 303 */ 304 @CallerSensitive 305 public static Package[] getPackages() { 306 // Android-changed: Use VMStack.getCallingClassLoader() to obtain the classloader. 307 // ClassLoader l = ClassLoader.getClassLoader(Reflection.getCallerClass()); 308 ClassLoader l = VMStack.getCallingClassLoader(); 309 if (l != null) { 310 return l.getPackages(); 311 } else { 312 return getSystemPackages(); 313 } 314 } 315 316 /** 317 * Get the package for the specified class. 318 * The class's class loader is used to find the package instance 319 * corresponding to the specified class. If the class loader 320 * is the bootstrap class loader, which may be represented by 321 * {@code null} in some implementations, then the set of packages 322 * loaded by the bootstrap class loader is searched to find the package. 323 * <p> 324 * Packages have attributes for versions and specifications only 325 * if the class loader created the package 326 * instance with the appropriate attributes. Typically those 327 * attributes are defined in the manifests that accompany 328 * the classes. 329 * 330 * @param c the class to get the package of. 331 * @return the package of the class. It may be null if no package 332 * information is available from the archive or codebase. */ 333 static Package getPackage(Class<?> c) { 334 String name = c.getName(); 335 int i = name.lastIndexOf('.'); 336 if (i != -1) { 337 name = name.substring(0, i); 338 ClassLoader cl = c.getClassLoader(); 339 if (cl != null) { 340 return cl.getPackage(name); 341 } else { 342 return getSystemPackage(name); 343 } 344 } else { 345 return null; 346 } 347 } 348 349 /** 350 * Return the hash code computed from the package name. 351 * @return the hash code computed from the package name. 352 */ 353 public int hashCode(){ 354 return pkgName.hashCode(); 355 } 356 357 /** 358 * Returns the string representation of this Package. 359 * Its value is the string "package " and the package name. 360 * If the package title is defined it is appended. 361 * If the package version is defined it is appended. 362 * @return the string representation of the package. 363 */ 364 public String toString() { 365 // BEGIN Android-added: Backwards compatibility fix for target API <= 24. 366 // Several apps try to parse the output of toString(). This is a really 367 // bad idea - especially when there's a Package.getName() function as well as a 368 // Class.getName() function that can be used instead. 369 // Starting from the API level 25 the proper output is generated. 370 final int targetSdkVersion = VMRuntime.getRuntime().getTargetSdkVersion(); 371 if (targetSdkVersion > 0 && targetSdkVersion <= 24) { 372 return "package " + pkgName; 373 } 374 // END Android-added: Backwards compatibility fix for target API <= 24. 375 376 String spec = specTitle; 377 String ver = specVersion; 378 if (spec != null && spec.length() > 0) 379 spec = ", " + spec; 380 else 381 spec = ""; 382 if (ver != null && ver.length() > 0) 383 ver = ", version " + ver; 384 else 385 ver = ""; 386 return "package " + pkgName + spec + ver; 387 } 388 389 private Class<?> getPackageInfo() { 390 if (packageInfo == null) { 391 try { 392 packageInfo = Class.forName(pkgName + ".package-info", false, loader); 393 } catch (ClassNotFoundException ex) { 394 // store a proxy for the package info that has no annotations 395 class PackageInfoProxy {} 396 packageInfo = PackageInfoProxy.class; 397 } 398 } 399 return packageInfo; 400 } 401 402 /** 403 * @throws NullPointerException {@inheritDoc} 404 * @since 1.5 405 */ 406 public <A extends Annotation> A getAnnotation(Class<A> annotationClass) { 407 return getPackageInfo().getAnnotation(annotationClass); 408 } 409 410 /** 411 * {@inheritDoc} 412 * @throws NullPointerException {@inheritDoc} 413 * @since 1.5 414 */ 415 @Override 416 public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) { 417 return AnnotatedElement.super.isAnnotationPresent(annotationClass); 418 } 419 420 /** 421 * @throws NullPointerException {@inheritDoc} 422 * @since 1.8 423 */ 424 @Override 425 public <A extends Annotation> A[] getAnnotationsByType(Class<A> annotationClass) { 426 return getPackageInfo().getAnnotationsByType(annotationClass); 427 } 428 429 /** 430 * @since 1.5 431 */ 432 public Annotation[] getAnnotations() { 433 return getPackageInfo().getAnnotations(); 434 } 435 436 /** 437 * @throws NullPointerException {@inheritDoc} 438 * @since 1.8 439 */ 440 @Override 441 public <A extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass) { 442 return getPackageInfo().getDeclaredAnnotation(annotationClass); 443 } 444 445 /** 446 * @throws NullPointerException {@inheritDoc} 447 * @since 1.8 448 */ 449 @Override 450 public <A extends Annotation> A[] getDeclaredAnnotationsByType(Class<A> annotationClass) { 451 return getPackageInfo().getDeclaredAnnotationsByType(annotationClass); 452 } 453 454 /** 455 * @since 1.5 456 */ 457 public Annotation[] getDeclaredAnnotations() { 458 return getPackageInfo().getDeclaredAnnotations(); 459 } 460 461 /** 462 * Construct a package instance with the specified version 463 * information. 464 * @param name the name of the package 465 * @param spectitle the title of the specification 466 * @param specversion the version of the specification 467 * @param specvendor the organization that maintains the specification 468 * @param impltitle the title of the implementation 469 * @param implversion the version of the implementation 470 * @param implvendor the organization that maintains the implementation 471 */ 472 Package(String name, 473 String spectitle, String specversion, String specvendor, 474 String impltitle, String implversion, String implvendor, 475 URL sealbase, ClassLoader loader) 476 { 477 pkgName = name; 478 implTitle = impltitle; 479 implVersion = implversion; 480 implVendor = implvendor; 481 specTitle = spectitle; 482 specVersion = specversion; 483 specVendor = specvendor; 484 sealBase = sealbase; 485 this.loader = loader; 486 } 487 488 /* 489 * Construct a package using the attributes from the specified manifest. 490 * 491 * @param name the package name 492 * @param man the optional manifest for the package 493 * @param url the optional code source url for the package 494 */ 495 private Package(String name, Manifest man, URL url, ClassLoader loader) { 496 String path = name.replace('.', '/').concat("/"); 497 String sealed = null; 498 String specTitle= null; 499 String specVersion= null; 500 String specVendor= null; 501 String implTitle= null; 502 String implVersion= null; 503 String implVendor= null; 504 URL sealBase= null; 505 Attributes attr = man.getAttributes(path); 506 if (attr != null) { 507 specTitle = attr.getValue(Name.SPECIFICATION_TITLE); 508 specVersion = attr.getValue(Name.SPECIFICATION_VERSION); 509 specVendor = attr.getValue(Name.SPECIFICATION_VENDOR); 510 implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE); 511 implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION); 512 implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR); 513 sealed = attr.getValue(Name.SEALED); 514 } 515 attr = man.getMainAttributes(); 516 if (attr != null) { 517 if (specTitle == null) { 518 specTitle = attr.getValue(Name.SPECIFICATION_TITLE); 519 } 520 if (specVersion == null) { 521 specVersion = attr.getValue(Name.SPECIFICATION_VERSION); 522 } 523 if (specVendor == null) { 524 specVendor = attr.getValue(Name.SPECIFICATION_VENDOR); 525 } 526 if (implTitle == null) { 527 implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE); 528 } 529 if (implVersion == null) { 530 implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION); 531 } 532 if (implVendor == null) { 533 implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR); 534 } 535 if (sealed == null) { 536 sealed = attr.getValue(Name.SEALED); 537 } 538 } 539 if ("true".equalsIgnoreCase(sealed)) { 540 sealBase = url; 541 } 542 pkgName = name; 543 this.specTitle = specTitle; 544 this.specVersion = specVersion; 545 this.specVendor = specVendor; 546 this.implTitle = implTitle; 547 this.implVersion = implVersion; 548 this.implVendor = implVendor; 549 this.sealBase = sealBase; 550 this.loader = loader; 551 } 552 553 /* 554 * Returns the loaded system package for the specified name. 555 */ 556 static Package getSystemPackage(String name) { 557 synchronized (pkgs) { 558 Package pkg = pkgs.get(name); 559 if (pkg == null) { 560 name = name.replace('.', '/').concat("/"); 561 String fn = getSystemPackage0(name); 562 if (fn != null) { 563 pkg = defineSystemPackage(name, fn); 564 } 565 } 566 return pkg; 567 } 568 } 569 570 /* 571 * Return an array of loaded system packages. 572 */ 573 static Package[] getSystemPackages() { 574 // First, update the system package map with new package names 575 String[] names = getSystemPackages0(); 576 synchronized (pkgs) { 577 for (int i = 0; i < names.length; i++) { 578 defineSystemPackage(names[i], getSystemPackage0(names[i])); 579 } 580 return pkgs.values().toArray(new Package[pkgs.size()]); 581 } 582 } 583 584 private static Package defineSystemPackage(final String iname, 585 final String fn) 586 { 587 return AccessController.doPrivileged(new PrivilegedAction<Package>() { 588 public Package run() { 589 String name = iname; 590 // Get the cached code source url for the file name 591 URL url = urls.get(fn); 592 if (url == null) { 593 // URL not found, so create one 594 File file = new File(fn); 595 try { 596 url = ParseUtil.fileToEncodedURL(file); 597 } catch (MalformedURLException e) { 598 } 599 if (url != null) { 600 urls.put(fn, url); 601 // If loading a JAR file, then also cache the manifest 602 if (file.isFile()) { 603 mans.put(fn, loadManifest(fn)); 604 } 605 } 606 } 607 // Convert to "."-separated package name 608 name = name.substring(0, name.length() - 1).replace('/', '.'); 609 Package pkg; 610 Manifest man = mans.get(fn); 611 if (man != null) { 612 pkg = new Package(name, man, url, null); 613 } else { 614 pkg = new Package(name, null, null, null, 615 null, null, null, null, null); 616 } 617 pkgs.put(name, pkg); 618 return pkg; 619 } 620 }); 621 } 622 623 /* 624 * Returns the Manifest for the specified JAR file name. 625 */ 626 private static Manifest loadManifest(String fn) { 627 try (FileInputStream fis = new FileInputStream(fn); 628 JarInputStream jis = new JarInputStream(fis, false)) 629 { 630 return jis.getManifest(); 631 } catch (IOException e) { 632 return null; 633 } 634 } 635 636 // The map of loaded system packages 637 private static Map<String, Package> pkgs = new HashMap<>(31); 638 639 // Maps each directory or zip file name to its corresponding url 640 private static Map<String, URL> urls = new HashMap<>(10); 641 642 // Maps each code source url for a jar file to its manifest 643 private static Map<String, Manifest> mans = new HashMap<>(10); 644 645 private static native String getSystemPackage0(String name); 646 private static native String[] getSystemPackages0(); 647 648 /* 649 * Private storage for the package name and attributes. 650 */ 651 private final String pkgName; 652 private final String specTitle; 653 private final String specVersion; 654 private final String specVendor; 655 private final String implTitle; 656 private final String implVersion; 657 private final String implVendor; 658 private final URL sealBase; 659 private transient final ClassLoader loader; 660 private transient Class<?> packageInfo; 661 } 662