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