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 */ getName()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 */ getSpecificationTitle()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 */ getSpecificationVersion()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 */ getSpecificationVendor()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 */ getImplementationTitle()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 */ getImplementationVersion()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 */ getImplementationVendor()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 */ isSealed()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 */ isSealed(URL url)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 */ isCompatibleWith(String desired)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 getPackage(String name)281 public static Package getPackage(String name) { 282 ClassLoader l = VMStack.getCallingClassLoader(); 283 if (l != null) { 284 return l.getPackage(name); 285 } else { 286 return getSystemPackage(name); 287 } 288 } 289 290 /** 291 * Get all the packages currently known for the caller's {@code ClassLoader} 292 * instance. Those packages correspond to classes loaded via or accessible by 293 * name to that {@code ClassLoader} instance. If the caller's 294 * {@code ClassLoader} instance is the bootstrap {@code ClassLoader} 295 * instance, which may be represented by {@code null} in some implementations, 296 * only packages corresponding to classes loaded by the bootstrap 297 * {@code ClassLoader} instance will be returned. 298 * 299 * @return a new array of packages known to the callers {@code ClassLoader} 300 * instance. An zero length array is returned if none are known. 301 */ 302 @CallerSensitive getPackages()303 public static Package[] getPackages() { 304 ClassLoader l = VMStack.getCallingClassLoader(); 305 if (l != null) { 306 return l.getPackages(); 307 } else { 308 return getSystemPackages(); 309 } 310 } 311 312 /** 313 * Get the package for the specified class. 314 * The class's class loader is used to find the package instance 315 * corresponding to the specified class. If the class loader 316 * is the bootstrap class loader, which may be represented by 317 * {@code null} in some implementations, then the set of packages 318 * loaded by the bootstrap class loader is searched to find the package. 319 * <p> 320 * Packages have attributes for versions and specifications only 321 * if the class loader created the package 322 * instance with the appropriate attributes. Typically those 323 * attributes are defined in the manifests that accompany 324 * the classes. 325 * 326 * @param c the class to get the package of. 327 * @return the package of the class. It may be null if no package 328 * information is available from the archive or codebase. */ getPackage(Class<?> c)329 static Package getPackage(Class<?> c) { 330 String name = c.getName(); 331 int i = name.lastIndexOf('.'); 332 if (i != -1) { 333 name = name.substring(0, i); 334 ClassLoader cl = c.getClassLoader(); 335 if (cl != null) { 336 return cl.getPackage(name); 337 } else { 338 return getSystemPackage(name); 339 } 340 } else { 341 return null; 342 } 343 } 344 345 /** 346 * Return the hash code computed from the package name. 347 * @return the hash code computed from the package name. 348 */ hashCode()349 public int hashCode(){ 350 return pkgName.hashCode(); 351 } 352 353 /** 354 * Returns the string representation of this Package. 355 * Its value is the string "package " and the package name. 356 * If the package title is defined it is appended. 357 * If the package version is defined it is appended. 358 * @return the string representation of the package. 359 */ toString()360 public String toString() { 361 // Android-changed start 362 // Several apps try to parse the output of toString(). This is a really 363 // bad idea - especially when there's a Package.getName() function as well as a 364 // Class.getName() function that can be used instead. 365 // Starting from the API level 25 the proper output is generated. 366 final int targetSdkVersion = VMRuntime.getRuntime().getTargetSdkVersion(); 367 if (targetSdkVersion > 0 && targetSdkVersion <= 24) { 368 return "package " + pkgName; 369 } 370 // Android-changed end 371 372 String spec = specTitle; 373 String ver = specVersion; 374 if (spec != null && spec.length() > 0) 375 spec = ", " + spec; 376 else 377 spec = ""; 378 if (ver != null && ver.length() > 0) 379 ver = ", version " + ver; 380 else 381 ver = ""; 382 return "package " + pkgName + spec + ver; 383 } 384 getPackageInfo()385 private Class<?> getPackageInfo() { 386 if (packageInfo == null) { 387 try { 388 packageInfo = Class.forName(pkgName + ".package-info", false, loader); 389 } catch (ClassNotFoundException ex) { 390 // store a proxy for the package info that has no annotations 391 class PackageInfoProxy {} 392 packageInfo = PackageInfoProxy.class; 393 } 394 } 395 return packageInfo; 396 } 397 398 /** 399 * @throws NullPointerException {@inheritDoc} 400 * @since 1.5 401 */ getAnnotation(Class<A> annotationClass)402 public <A extends Annotation> A getAnnotation(Class<A> annotationClass) { 403 return getPackageInfo().getAnnotation(annotationClass); 404 } 405 406 /** 407 * {@inheritDoc} 408 * @throws NullPointerException {@inheritDoc} 409 * @since 1.5 410 */ 411 @Override isAnnotationPresent(Class<? extends Annotation> annotationClass)412 public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) { 413 return AnnotatedElement.super.isAnnotationPresent(annotationClass); 414 } 415 416 /** 417 * @throws NullPointerException {@inheritDoc} 418 * @since 1.8 419 */ 420 @Override getAnnotationsByType(Class<A> annotationClass)421 public <A extends Annotation> A[] getAnnotationsByType(Class<A> annotationClass) { 422 return getPackageInfo().getAnnotationsByType(annotationClass); 423 } 424 425 /** 426 * @since 1.5 427 */ getAnnotations()428 public Annotation[] getAnnotations() { 429 return getPackageInfo().getAnnotations(); 430 } 431 432 /** 433 * @throws NullPointerException {@inheritDoc} 434 * @since 1.8 435 */ 436 @Override getDeclaredAnnotation(Class<A> annotationClass)437 public <A extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass) { 438 return getPackageInfo().getDeclaredAnnotation(annotationClass); 439 } 440 441 /** 442 * @throws NullPointerException {@inheritDoc} 443 * @since 1.8 444 */ 445 @Override getDeclaredAnnotationsByType(Class<A> annotationClass)446 public <A extends Annotation> A[] getDeclaredAnnotationsByType(Class<A> annotationClass) { 447 return getPackageInfo().getDeclaredAnnotationsByType(annotationClass); 448 } 449 450 /** 451 * @since 1.5 452 */ getDeclaredAnnotations()453 public Annotation[] getDeclaredAnnotations() { 454 return getPackageInfo().getDeclaredAnnotations(); 455 } 456 457 /** 458 * Construct a package instance with the specified version 459 * information. 460 * @param name the name of the package 461 * @param spectitle the title of the specification 462 * @param specversion the version of the specification 463 * @param specvendor the organization that maintains the specification 464 * @param impltitle the title of the implementation 465 * @param implversion the version of the implementation 466 * @param implvendor the organization that maintains the implementation 467 */ Package(String name, String spectitle, String specversion, String specvendor, String impltitle, String implversion, String implvendor, URL sealbase, ClassLoader loader)468 Package(String name, 469 String spectitle, String specversion, String specvendor, 470 String impltitle, String implversion, String implvendor, 471 URL sealbase, ClassLoader loader) 472 { 473 pkgName = name; 474 implTitle = impltitle; 475 implVersion = implversion; 476 implVendor = implvendor; 477 specTitle = spectitle; 478 specVersion = specversion; 479 specVendor = specvendor; 480 sealBase = sealbase; 481 this.loader = loader; 482 } 483 484 /* 485 * Construct a package using the attributes from the specified manifest. 486 * 487 * @param name the package name 488 * @param man the optional manifest for the package 489 * @param url the optional code source url for the package 490 */ Package(String name, Manifest man, URL url, ClassLoader loader)491 private Package(String name, Manifest man, URL url, ClassLoader loader) { 492 String path = name.replace('.', '/').concat("/"); 493 String sealed = null; 494 String specTitle= null; 495 String specVersion= null; 496 String specVendor= null; 497 String implTitle= null; 498 String implVersion= null; 499 String implVendor= null; 500 URL sealBase= null; 501 Attributes attr = man.getAttributes(path); 502 if (attr != null) { 503 specTitle = attr.getValue(Name.SPECIFICATION_TITLE); 504 specVersion = attr.getValue(Name.SPECIFICATION_VERSION); 505 specVendor = attr.getValue(Name.SPECIFICATION_VENDOR); 506 implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE); 507 implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION); 508 implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR); 509 sealed = attr.getValue(Name.SEALED); 510 } 511 attr = man.getMainAttributes(); 512 if (attr != null) { 513 if (specTitle == null) { 514 specTitle = attr.getValue(Name.SPECIFICATION_TITLE); 515 } 516 if (specVersion == null) { 517 specVersion = attr.getValue(Name.SPECIFICATION_VERSION); 518 } 519 if (specVendor == null) { 520 specVendor = attr.getValue(Name.SPECIFICATION_VENDOR); 521 } 522 if (implTitle == null) { 523 implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE); 524 } 525 if (implVersion == null) { 526 implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION); 527 } 528 if (implVendor == null) { 529 implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR); 530 } 531 if (sealed == null) { 532 sealed = attr.getValue(Name.SEALED); 533 } 534 } 535 if ("true".equalsIgnoreCase(sealed)) { 536 sealBase = url; 537 } 538 pkgName = name; 539 this.specTitle = specTitle; 540 this.specVersion = specVersion; 541 this.specVendor = specVendor; 542 this.implTitle = implTitle; 543 this.implVersion = implVersion; 544 this.implVendor = implVendor; 545 this.sealBase = sealBase; 546 this.loader = loader; 547 } 548 549 /* 550 * Returns the loaded system package for the specified name. 551 */ getSystemPackage(String name)552 static Package getSystemPackage(String name) { 553 synchronized (pkgs) { 554 Package pkg = pkgs.get(name); 555 if (pkg == null) { 556 name = name.replace('.', '/').concat("/"); 557 String fn = getSystemPackage0(name); 558 if (fn != null) { 559 pkg = defineSystemPackage(name, fn); 560 } 561 } 562 return pkg; 563 } 564 } 565 566 /* 567 * Return an array of loaded system packages. 568 */ getSystemPackages()569 static Package[] getSystemPackages() { 570 // First, update the system package map with new package names 571 String[] names = getSystemPackages0(); 572 synchronized (pkgs) { 573 for (int i = 0; i < names.length; i++) { 574 defineSystemPackage(names[i], getSystemPackage0(names[i])); 575 } 576 return pkgs.values().toArray(new Package[pkgs.size()]); 577 } 578 } 579 defineSystemPackage(final String iname, final String fn)580 private static Package defineSystemPackage(final String iname, 581 final String fn) 582 { 583 return AccessController.doPrivileged(new PrivilegedAction<Package>() { 584 public Package run() { 585 String name = iname; 586 // Get the cached code source url for the file name 587 URL url = urls.get(fn); 588 if (url == null) { 589 // URL not found, so create one 590 File file = new File(fn); 591 try { 592 url = ParseUtil.fileToEncodedURL(file); 593 } catch (MalformedURLException e) { 594 } 595 if (url != null) { 596 urls.put(fn, url); 597 // If loading a JAR file, then also cache the manifest 598 if (file.isFile()) { 599 mans.put(fn, loadManifest(fn)); 600 } 601 } 602 } 603 // Convert to "."-separated package name 604 name = name.substring(0, name.length() - 1).replace('/', '.'); 605 Package pkg; 606 Manifest man = mans.get(fn); 607 if (man != null) { 608 pkg = new Package(name, man, url, null); 609 } else { 610 pkg = new Package(name, null, null, null, 611 null, null, null, null, null); 612 } 613 pkgs.put(name, pkg); 614 return pkg; 615 } 616 }); 617 } 618 619 /* 620 * Returns the Manifest for the specified JAR file name. 621 */ 622 private static Manifest loadManifest(String fn) { 623 try (FileInputStream fis = new FileInputStream(fn); 624 JarInputStream jis = new JarInputStream(fis, false)) 625 { 626 return jis.getManifest(); 627 } catch (IOException e) { 628 return null; 629 } 630 } 631 632 // The map of loaded system packages 633 private static Map<String, Package> pkgs = new HashMap<>(31); 634 635 // Maps each directory or zip file name to its corresponding url 636 private static Map<String, URL> urls = new HashMap<>(10); 637 638 // Maps each code source url for a jar file to its manifest 639 private static Map<String, Manifest> mans = new HashMap<>(10); 640 641 private static native String getSystemPackage0(String name); 642 private static native String[] getSystemPackages0(); 643 644 /* 645 * Private storage for the package name and attributes. 646 */ 647 private final String pkgName; 648 private final String specTitle; 649 private final String specVersion; 650 private final String specVendor; 651 private final String implTitle; 652 private final String implVersion; 653 private final String implVendor; 654 private final URL sealBase; 655 private transient final ClassLoader loader; 656 private transient Class<?> packageInfo; 657 } 658