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