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