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