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 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 class 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.5 391 */ 392 public boolean isAnnotationPresent( 393 Class<? extends Annotation> annotationClass) { 394 return getPackageInfo().isAnnotationPresent(annotationClass); 395 } 396 397 /** 398 * @since 1.5 399 */ 400 public Annotation[] getAnnotations() { 401 return getPackageInfo().getAnnotations(); 402 } 403 404 /** 405 * @since 1.5 406 */ 407 public Annotation[] getDeclaredAnnotations() { 408 return getPackageInfo().getDeclaredAnnotations(); 409 } 410 411 /** 412 * Construct a package instance with the specified version 413 * information. 414 * @param pkgName the name of the package 415 * @param spectitle the title of the specification 416 * @param specversion the version of the specification 417 * @param specvendor the organization that maintains the specification 418 * @param impltitle the title of the implementation 419 * @param implversion the version of the implementation 420 * @param implvendor the organization that maintains the implementation 421 * @return a new package for containing the specified information. 422 */ 423 Package(String name, 424 String spectitle, String specversion, String specvendor, 425 String impltitle, String implversion, String implvendor, 426 URL sealbase, ClassLoader loader) 427 { 428 pkgName = name; 429 implTitle = impltitle; 430 implVersion = implversion; 431 implVendor = implvendor; 432 specTitle = spectitle; 433 specVersion = specversion; 434 specVendor = specvendor; 435 sealBase = sealbase; 436 this.loader = loader; 437 } 438 439 /* 440 * Construct a package using the attributes from the specified manifest. 441 * 442 * @param name the package name 443 * @param man the optional manifest for the package 444 * @param url the optional code source url for the package 445 */ 446 private Package(String name, Manifest man, URL url, ClassLoader loader) { 447 String path = name.replace('.', '/').concat("/"); 448 String sealed = null; 449 String specTitle= null; 450 String specVersion= null; 451 String specVendor= null; 452 String implTitle= null; 453 String implVersion= null; 454 String implVendor= null; 455 URL sealBase= null; 456 Attributes attr = man.getAttributes(path); 457 if (attr != null) { 458 specTitle = attr.getValue(Name.SPECIFICATION_TITLE); 459 specVersion = attr.getValue(Name.SPECIFICATION_VERSION); 460 specVendor = attr.getValue(Name.SPECIFICATION_VENDOR); 461 implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE); 462 implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION); 463 implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR); 464 sealed = attr.getValue(Name.SEALED); 465 } 466 attr = man.getMainAttributes(); 467 if (attr != null) { 468 if (specTitle == null) { 469 specTitle = attr.getValue(Name.SPECIFICATION_TITLE); 470 } 471 if (specVersion == null) { 472 specVersion = attr.getValue(Name.SPECIFICATION_VERSION); 473 } 474 if (specVendor == null) { 475 specVendor = attr.getValue(Name.SPECIFICATION_VENDOR); 476 } 477 if (implTitle == null) { 478 implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE); 479 } 480 if (implVersion == null) { 481 implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION); 482 } 483 if (implVendor == null) { 484 implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR); 485 } 486 if (sealed == null) { 487 sealed = attr.getValue(Name.SEALED); 488 } 489 } 490 if ("true".equalsIgnoreCase(sealed)) { 491 sealBase = url; 492 } 493 pkgName = name; 494 this.specTitle = specTitle; 495 this.specVersion = specVersion; 496 this.specVendor = specVendor; 497 this.implTitle = implTitle; 498 this.implVersion = implVersion; 499 this.implVendor = implVendor; 500 this.sealBase = sealBase; 501 this.loader = loader; 502 } 503 504 /* 505 * Returns the loaded system package for the specified name. 506 */ 507 static Package getSystemPackage(String name) { 508 synchronized (pkgs) { 509 Package pkg = pkgs.get(name); 510 if (pkg == null) { 511 name = name.replace('.', '/').concat("/"); 512 String fn = getSystemPackage0(name); 513 if (fn != null) { 514 pkg = defineSystemPackage(name, fn); 515 } 516 } 517 return pkg; 518 } 519 } 520 521 /* 522 * Return an array of loaded system packages. 523 */ 524 static Package[] getSystemPackages() { 525 // First, update the system package map with new package names 526 String[] names = getSystemPackages0(); 527 synchronized (pkgs) { 528 for (int i = 0; i < names.length; i++) { 529 defineSystemPackage(names[i], getSystemPackage0(names[i])); 530 } 531 return pkgs.values().toArray(new Package[pkgs.size()]); 532 } 533 } 534 535 private static Package defineSystemPackage(final String iname, 536 final String fn) 537 { 538 return AccessController.doPrivileged(new PrivilegedAction<Package>() { 539 public Package run() { 540 String name = iname; 541 // Get the cached code source url for the file name 542 URL url = urls.get(fn); 543 if (url == null) { 544 // URL not found, so create one 545 File file = new File(fn); 546 try { 547 url = ParseUtil.fileToEncodedURL(file); 548 } catch (MalformedURLException e) { 549 } 550 if (url != null) { 551 urls.put(fn, url); 552 // If loading a JAR file, then also cache the manifest 553 if (file.isFile()) { 554 mans.put(fn, loadManifest(fn)); 555 } 556 } 557 } 558 // Convert to "."-separated package name 559 name = name.substring(0, name.length() - 1).replace('/', '.'); 560 Package pkg; 561 Manifest man = mans.get(fn); 562 if (man != null) { 563 pkg = new Package(name, man, url, null); 564 } else { 565 pkg = new Package(name, null, null, null, 566 null, null, null, null, null); 567 } 568 pkgs.put(name, pkg); 569 return pkg; 570 } 571 }); 572 } 573 574 /* 575 * Returns the Manifest for the specified JAR file name. 576 */ 577 private static Manifest loadManifest(String fn) { 578 try (FileInputStream fis = new FileInputStream(fn); 579 JarInputStream jis = new JarInputStream(fis, false)) 580 { 581 return jis.getManifest(); 582 } catch (IOException e) { 583 return null; 584 } 585 } 586 587 // The map of loaded system packages 588 private static Map<String, Package> pkgs = new HashMap<>(31); 589 590 // Maps each directory or zip file name to its corresponding url 591 private static Map<String, URL> urls = new HashMap<>(10); 592 593 // Maps each code source url for a jar file to its manifest 594 private static Map<String, Manifest> mans = new HashMap<>(10); 595 596 private static native String getSystemPackage0(String name); 597 private static native String[] getSystemPackages0(); 598 599 /* 600 * Private storage for the package name and attributes. 601 */ 602 private final String pkgName; 603 private final String specTitle; 604 private final String specVersion; 605 private final String specVendor; 606 private final String implTitle; 607 private final String implVersion; 608 private final String implVendor; 609 private final URL sealBase; 610 private transient final ClassLoader loader; 611 private transient Class<?> packageInfo; 612 }