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