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

Print this page
rev 10764 : 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


 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('.', '/') + "/";
 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         String name = iname;

















 564         // Convert to "."-separated package name
 565         name = name.substring(0, name.length() - 1).replace('/', '.');
 566         // Creates a cached manifest for the file name, allowing
 567         // only-once, lazy reads of manifest from jar files
 568         CachedManifest cachedManifest = createCachedManifest(fn);
 569         Package pkg;
 570         Manifest manifest = cachedManifest.getManifest();
 571         if (manifest != null) {
 572             pkg = new Package(name, manifest,
 573                               cachedManifest.getURL(), null);
 574         } else {
 575             pkg = new Package(name, null, null, null,
 576                               null, null, null, null, null);
 577         }
 578         pkgs.put(name, pkg);
 579         return pkg;
 580     }
 581 
 582     private static CachedManifest createCachedManifest(String fn) {
 583         CachedManifest manifest = manifests.get(fn);
 584         if (manifest != null) {
 585             return manifest;
 586         }
 587         URL url = AccessController.doPrivileged(new PrivilegedAction<URL>() {
 588             public URL run() {
 589                 final File file = new File(fn);
 590                 if (file.isFile()) {
 591                     try {
 592                         return ParseUtil.fileToEncodedURL(file);
 593                     } catch (MalformedURLException e) {
 594                     }
 595                 }
 596                 return null;
 597             }
 598         });
 599         if (url == null) {
 600             return NO_MANIFEST;
 601         }
 602         manifest = new CachedManifest(fn);
 603         CachedManifest oldManifest = manifests.putIfAbsent(fn, manifest);
 604         // return the manifest which was created first to ensure
 605         // only one manifest is ever read for any particular file
 606         return (oldManifest != null) ? oldManifest : manifest;
 607     }
 608 
 609     /*
 610      * Returns the Manifest for the specified JAR file name.
 611      */
 612     private static Manifest loadManifest(String fn) {
 613         try (FileInputStream fis = new FileInputStream(fn);
 614              JarInputStream jis = new JarInputStream(fis, false))
 615         {
 616             return jis.getManifest();
 617         } catch (IOException e) {
 618             return null;
 619         }
 620     }
 621 
 622     // The map of loaded system packages
 623     private static final Map<String, Package> pkgs = new ConcurrentHashMap<>();
 624 
 625     // Maps each directory or zip file name to its corresponding manifest, if
 626     // it exists
 627     private static final Map<String, CachedManifest> manifests =
 628             new ConcurrentHashMap<>();
 629 
 630     private static final CachedManifest NO_MANIFEST = new CachedManifest();

 631 
 632     private static class CachedManifest {
 633         private final String fileName;
 634         private volatile boolean resolved;
 635         private volatile Manifest manifest;
 636         private volatile URL url;
 637 
 638         CachedManifest() {
 639             this.fileName = null;
 640             this.resolved = true;
 641         }
 642 
 643         CachedManifest(String fileName) {
 644             this.fileName = fileName;
 645         }
 646 
 647         public URL getURL() {
 648             resolveManifest();
 649             return url;
 650         }
 651 
 652         public Manifest getManifest() {
 653             resolveManifest();
 654             return manifest;
 655         }
 656 
 657         private void resolveManifest() {
 658             if (resolved) {
 659                 return;
 660             }
 661             synchronized(this) {
 662                 if (resolved) {
 663                     return;
 664                 }
 665                 AccessController.doPrivileged(new PrivilegedAction<Package>() {
 666                     public Package run() {
 667                         final File file = new File(fileName);
 668                         if (file.isFile()) {
 669                             try {
 670                                 url = ParseUtil.fileToEncodedURL(file);
 671                                 manifest = loadManifest(fileName);
 672                             } catch (MalformedURLException e) {
 673                             }
 674                         }
 675                         return null;
 676                     }
 677                 });
 678                 resolved = true;
 679             }
 680         }
 681     }
 682 
 683     private static native String getSystemPackage0(String name);
 684     private static native String[] getSystemPackages0();
 685 
 686     /*
 687      * Private storage for the package name and attributes.
 688      */
 689     private final String pkgName;
 690     private final String specTitle;
 691     private final String specVersion;
 692     private final String specVendor;
 693     private final String implTitle;
 694     private final String implVersion;
 695     private final String implVendor;
 696     private final URL sealBase;
 697     private transient final ClassLoader loader;
 698     private transient Class<?> packageInfo;
 699 }