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