src/java.base/share/classes/java/lang/Package.java

Print this page
rev 10786 : 8060130: Simplify the synchronization of defining and getting java.lang.Package
Reviewed-by: mchung


   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


 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();


 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 }


   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.lang;
  27 
  28 import java.lang.reflect.AnnotatedElement;


  29 

  30 import java.io.File;
  31 import java.io.FileInputStream;

  32 import java.io.IOException;
  33 import java.net.URL;
  34 import java.net.MalformedURLException;
  35 import java.security.AccessController;
  36 import java.security.PrivilegedAction;
  37 
  38 import java.util.concurrent.ConcurrentHashMap;
  39 import java.util.jar.JarInputStream;
  40 import java.util.jar.Manifest;
  41 import java.util.jar.Attributes;
  42 import java.util.jar.Attributes.Name;

  43 import java.util.Map;


  44 
  45 import sun.net.www.ParseUtil;
  46 import sun.reflect.CallerSensitive;
  47 import sun.reflect.Reflection;
  48 
  49 import java.lang.annotation.Annotation;
  50 
  51 /**
  52  * {@code Package} objects contain version information
  53  * about the implementation and specification of a Java package.
  54  * This versioning information is retrieved and made available
  55  * by the {@link ClassLoader} instance that
  56  * loaded the class(es).  Typically, it is stored in the manifest that is
  57  * distributed with the classes.
  58  *
  59  * <p>The set of classes that make up the package may implement a
  60  * particular specification and if so the specification title, version number,
  61  * and vendor strings identify that specification.
  62  * An application can ask if the package is
  63  * compatible with a particular version, see the {@link


 453             URL sealbase, ClassLoader loader)
 454     {
 455         pkgName = name;
 456         implTitle = impltitle;
 457         implVersion = implversion;
 458         implVendor = implvendor;
 459         specTitle = spectitle;
 460         specVersion = specversion;
 461         specVendor = specvendor;
 462         sealBase = sealbase;
 463         this.loader = loader;
 464     }
 465 
 466     /*
 467      * Construct a package using the attributes from the specified manifest.
 468      *
 469      * @param name the package name
 470      * @param man the optional manifest for the package
 471      * @param url the optional code source url for the package
 472      */
 473     private Package(String name, Manifest man, URL url) {
 474         String path = name.replace('.', '/').concat("/");
 475         String sealed = null;
 476         String specTitle= null;
 477         String specVersion= null;
 478         String specVendor= null;
 479         String implTitle= null;
 480         String implVersion= null;
 481         String implVendor= null;
 482         URL sealBase= null;
 483         Attributes attr = man.getAttributes(path);
 484         if (attr != null) {
 485             specTitle   = attr.getValue(Name.SPECIFICATION_TITLE);
 486             specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
 487             specVendor  = attr.getValue(Name.SPECIFICATION_VENDOR);
 488             implTitle   = attr.getValue(Name.IMPLEMENTATION_TITLE);
 489             implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
 490             implVendor  = attr.getValue(Name.IMPLEMENTATION_VENDOR);
 491             sealed      = attr.getValue(Name.SEALED);
 492         }
 493         attr = man.getMainAttributes();


 508                 implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
 509             }
 510             if (implVendor == null) {
 511                 implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR);
 512             }
 513             if (sealed == null) {
 514                 sealed = attr.getValue(Name.SEALED);
 515             }
 516         }
 517         if ("true".equalsIgnoreCase(sealed)) {
 518             sealBase = url;
 519         }
 520         pkgName = name;
 521         this.specTitle = specTitle;
 522         this.specVersion = specVersion;
 523         this.specVendor = specVendor;
 524         this.implTitle = implTitle;
 525         this.implVersion = implVersion;
 526         this.implVendor = implVendor;
 527         this.sealBase = sealBase;
 528         this.loader = null;
 529     }
 530 
 531     /*
 532      * Returns the loaded system package for the specified name.
 533      */
 534     static Package getSystemPackage(String name) {

 535         Package pkg = pkgs.get(name);
 536         if (pkg == null) {
 537             name = name.replace('.', '/') + "/";
 538             String fn = getSystemPackage0(name);
 539             if (fn != null) {
 540                 pkg = defineSystemPackage(name, fn);
 541             }
 542         }
 543         return pkg;
 544     }

 545 
 546     /*
 547      * Return an array of loaded system packages.
 548      */
 549     static Package[] getSystemPackages() {
 550         // First, update the system package map with new package names
 551         String[] names = getSystemPackages0();

 552         for (String name : names) {
 553             if (!pkgs.containsKey(name)) {
 554                 defineSystemPackage(name, getSystemPackage0(name));
 555             }

 556         }
 557         return pkgs.values().toArray(new Package[pkgs.size()]);
 558     }
 559 
 560     private static Package defineSystemPackage(final String iname,
 561                                                final String fn)
 562     {




















 563         // Convert to "."-separated package name
 564         String name = iname.substring(0, iname.length() - 1).replace('/', '.');
 565         // Creates a cached manifest for the file name, allowing
 566         // only-once, lazy reads of manifest from jar files
 567         CachedManifest cachedManifest = createCachedManifest(fn);
 568         pkgs.putIfAbsent(name, new Package(name, cachedManifest.getManifest(),
 569                 cachedManifest.getURL()));
 570         // Ensure we only expose one Package object
 571         return pkgs.get(name);
 572     }
 573 
 574     private static CachedManifest createCachedManifest(String fn) {
 575         if (!manifests.containsKey(fn)) {
 576             manifests.putIfAbsent(fn, new CachedManifest(fn));
 577         }
 578         return manifests.get(fn);
 579     }
 580 
 581     private static final Manifest EMPTY_MANIFEST = new Manifest();
 582 
 583     /*
 584      * Returns the Manifest for the specified JAR file name.
 585      */
 586     private static Manifest loadManifest(String fn) {
 587         try (FileInputStream fis = new FileInputStream(fn);
 588              JarInputStream jis = new JarInputStream(fis, false))
 589         {
 590             return jis.getManifest();
 591         } catch (IOException e) {
 592             return EMPTY_MANIFEST;
 593         }
 594     }
 595 
 596     // The map of loaded system packages
 597     private static final ConcurrentHashMap<String, Package> pkgs
 598             = new ConcurrentHashMap<>();
 599 
 600 
 601     // Maps each directory or zip file name to its corresponding manifest, if
 602     // it exists
 603     private static final ConcurrentHashMap<String, CachedManifest> manifests
 604             = new ConcurrentHashMap<>();
 605 
 606     private static class CachedManifest {
 607         private final String fileName;
 608         private final URL url;
 609         private volatile Manifest manifest = EMPTY_MANIFEST;
 610 
 611         CachedManifest(final String fileName) {
 612             this.fileName = fileName;
 613             this.url = AccessController.doPrivileged(new PrivilegedAction<URL>() {
 614                 public URL run() {
 615                     final File file = new File(fileName);
 616                     if (file.isFile()) {
 617                         try {
 618                             return ParseUtil.fileToEncodedURL(file);
 619                         } catch (MalformedURLException e) {
 620                         }
 621                     }
 622                     return null;
 623                 }
 624             });
 625         }
 626 
 627         public URL getURL() {
 628             return url;
 629         }
 630 
 631         public Manifest getManifest() {
 632             resolveManifest();
 633             return manifest;
 634         }
 635 
 636         private void resolveManifest() {
 637             if (manifest != null || url == null) {
 638                 return;
 639             }
 640             synchronized(this) {
 641                 if (manifest == null) {
 642                     manifest = AccessController.doPrivileged(new PrivilegedAction<Manifest>() {
 643                         public Manifest run() {
 644                             return loadManifest(fileName);
 645                         }
 646                     });
 647                 }
 648             }
 649         }
 650     }
 651 
 652     private static native String getSystemPackage0(String name);
 653     private static native String[] getSystemPackages0();
 654 
 655     /*
 656      * Private storage for the package name and attributes.
 657      */
 658     private final String pkgName;
 659     private final String specTitle;
 660     private final String specVersion;
 661     private final String specVendor;
 662     private final String implTitle;
 663     private final String implVersion;
 664     private final String implVendor;
 665     private final URL sealBase;
 666     private transient final ClassLoader loader;
 667     private transient Class<?> packageInfo;
 668 }