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

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


   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


 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


 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 = loader;
 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('.', '/').concat("/");
 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(), null));
 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     // The map of loaded system packages
 582     private static final ConcurrentHashMap<String, Package> pkgs
 583             = new ConcurrentHashMap<>();
 584 
 585     // Maps each directory or zip file name to its corresponding manifest, if
 586     // it exists
 587     private static final ConcurrentHashMap<String, CachedManifest> manifests
 588             = new ConcurrentHashMap<>();
 589 
 590     private static class CachedManifest {
 591         private static final Manifest EMPTY_MANIFEST = new Manifest();
 592         private final String fileName;
 593         private final URL url;
 594         private volatile Manifest manifest;
 595 
 596         CachedManifest(final String fileName) {
 597             this.fileName = fileName;
 598             this.url = AccessController.doPrivileged(new PrivilegedAction<URL>() {
 599                 public URL run() {
 600                     final File file = new File(fileName);
 601                     if (file.isFile()) {
 602                         try {
 603                             return ParseUtil.fileToEncodedURL(file);
 604                         } catch (MalformedURLException e) {
 605                         }









 606                     }
 607                     return null;

 608                 }
 609             });
 610         }
 611 
 612         public URL getURL() {
 613             return url;
 614         }
 615 
 616         public Manifest getManifest() {
 617             if (url == null) {
 618                 return EMPTY_MANIFEST;
 619             }
 620             Manifest m = manifest;
 621             if (m != null) {
 622                 return m;
 623             }
 624             synchronized (this) {
 625                 m = manifest;
 626                 if (m != null) {
 627                     return m;
 628                 }
 629                 m = AccessController.doPrivileged(new PrivilegedAction<Manifest>() {
 630                     public Manifest run() {
 631                         try (FileInputStream fis = new FileInputStream(fileName);
 632                              JarInputStream jis = new JarInputStream(fis, false)) {
 633                             return jis.getManifest();
 634                         } catch (IOException e) {
 635                             return null;
 636                         }
 637                     }
 638                 });
 639                 manifest = m = (m == null ? EMPTY_MANIFEST : m);
 640             }
 641             return m;
 642         }
 643     }



 644 
 645     private static native String getSystemPackage0(String name);
 646     private static native String[] getSystemPackages0();
 647 
 648     /*
 649      * Private storage for the package name and attributes.
 650      */
 651     private final String pkgName;
 652     private final String specTitle;
 653     private final String specVersion;
 654     private final String specVendor;
 655     private final String implTitle;
 656     private final String implVersion;
 657     private final String implVendor;
 658     private final URL sealBase;
 659     private transient final ClassLoader loader;
 660     private transient Class<?> packageInfo;
 661 }