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&trade; 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 }