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