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