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