1 /*
2  * Copyright (c) 2015, 2020, 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 package jdk.internal.loader;
26 
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.lang.module.ModuleReference;
30 import java.net.MalformedURLException;
31 import java.net.URI;
32 import java.net.URL;
33 import java.nio.file.Files;
34 import java.nio.file.Path;
35 import java.security.AccessController;
36 import java.security.PrivilegedAction;
37 import java.util.Arrays;
38 import java.util.Enumeration;
39 import java.util.concurrent.ConcurrentHashMap;
40 import java.util.jar.JarInputStream;
41 import java.util.jar.Manifest;
42 import java.util.stream.Stream;
43 
44 import jdk.internal.access.JavaLangAccess;
45 import jdk.internal.access.SharedSecrets;
46 import jdk.internal.module.Modules;
47 import jdk.internal.module.ServicesCatalog;
48 import jdk.internal.util.StaticProperty;
49 
50 /**
51  * Find resources and packages in modules defined to the boot class loader or
52  * resources and packages on the "boot class path" specified via -Xbootclasspath/a.
53  */
54 
55 public class BootLoader {
BootLoader()56     private BootLoader() { }
57 
58     // The unnamed module for the boot loader
59     private static final Module UNNAMED_MODULE;
60     private static final String JAVA_HOME = StaticProperty.javaHome();
61 
62     static {
63         UNNAMED_MODULE = SharedSecrets.getJavaLangAccess().defineUnnamedModule(null);
64         setBootLoaderUnnamedModule0(UNNAMED_MODULE);
65     }
66 
67     // ServiceCatalog for the boot class loader
68     private static final ServicesCatalog SERVICES_CATALOG = ServicesCatalog.create();
69 
70     // ClassLoaderValue map for the boot class loader
71     private static final ConcurrentHashMap<?, ?> CLASS_LOADER_VALUE_MAP
72         = new ConcurrentHashMap<>();
73 
74     // native libraries loaded by the boot class loader
75     private static final NativeLibraries NATIVE_LIBS
76         = NativeLibraries.jniNativeLibraries(null);
77 
78     /**
79      * Returns the unnamed module for the boot loader.
80      */
getUnnamedModule()81     public static Module getUnnamedModule() {
82         return UNNAMED_MODULE;
83     }
84 
85     /**
86      * Returns the ServiceCatalog for modules defined to the boot class loader.
87      */
getServicesCatalog()88     public static ServicesCatalog getServicesCatalog() {
89         return SERVICES_CATALOG;
90     }
91 
92     /**
93      * Returns the ClassLoaderValue map for the boot class loader.
94      */
getClassLoaderValueMap()95     public static ConcurrentHashMap<?, ?> getClassLoaderValueMap() {
96         return CLASS_LOADER_VALUE_MAP;
97     }
98 
99     /**
100      * Returns NativeLibraries for the boot class loader.
101      */
getNativeLibraries()102     public static NativeLibraries getNativeLibraries() {
103         return NATIVE_LIBS;
104     }
105 
106     /**
107      * Returns {@code true} if there is a class path associated with the
108      * BootLoader.
109      */
hasClassPath()110     public static boolean hasClassPath() {
111         return ClassLoaders.bootLoader().hasClassPath();
112     }
113 
114     /**
115      * Registers a module with this class loader so that its classes
116      * (and resources) become visible via this class loader.
117      */
loadModule(ModuleReference mref)118     public static void loadModule(ModuleReference mref) {
119         ClassLoaders.bootLoader().loadModule(mref);
120     }
121 
122     /**
123      * Loads the Class object with the given name defined to the boot loader.
124      */
loadClassOrNull(String name)125     public static Class<?> loadClassOrNull(String name) {
126         return ClassLoaders.bootLoader().loadClassOrNull(name);
127     }
128 
129     /**
130      * Loads the Class object with the given name in the given module
131      * defined to the boot loader. Returns {@code null} if not found.
132      */
loadClass(Module module, String name)133     public static Class<?> loadClass(Module module, String name) {
134         Class<?> c = loadClassOrNull(name);
135         if (c != null && c.getModule() == module) {
136             return c;
137         } else {
138             return null;
139         }
140     }
141 
142     /**
143      * Loads a native library from the system library path.
144      */
loadLibrary(String name)145     public static void loadLibrary(String name) {
146         if (System.getSecurityManager() == null) {
147             BootLoader.getNativeLibraries().loadLibrary(name);
148         } else {
149             AccessController.doPrivileged(new java.security.PrivilegedAction<>() {
150                 public Void run() {
151                     BootLoader.getNativeLibraries().loadLibrary(name);
152                     return null;
153                 }
154             });
155         }
156     }
157 
158     /**
159      * Returns a URL to a resource in a module defined to the boot loader.
160      */
findResource(String mn, String name)161     public static URL findResource(String mn, String name) throws IOException {
162         return ClassLoaders.bootLoader().findResource(mn, name);
163     }
164 
165     /**
166      * Returns an input stream to a resource in a module defined to the
167      * boot loader.
168      */
findResourceAsStream(String mn, String name)169     public static InputStream findResourceAsStream(String mn, String name)
170         throws IOException
171     {
172         return ClassLoaders.bootLoader().findResourceAsStream(mn, name);
173     }
174 
175     /**
176      * Returns the URL to the given resource in any of the modules
177      * defined to the boot loader and the boot class path.
178      */
findResource(String name)179     public static URL findResource(String name) {
180         return ClassLoaders.bootLoader().findResource(name);
181     }
182 
183     /**
184      * Returns an Iterator to iterate over the resources of the given name
185      * in any of the modules defined to the boot loader.
186      */
findResources(String name)187     public static Enumeration<URL> findResources(String name) throws IOException {
188         return ClassLoaders.bootLoader().findResources(name);
189     }
190 
191     /**
192      * Define a package for the given class to the boot loader, if not already
193      * defined.
194      */
definePackage(Class<?> c)195     public static Package definePackage(Class<?> c) {
196         return getDefinedPackage(c.getPackageName());
197     }
198 
199     /**
200      * Returns the Package of the given name defined to the boot loader or null
201      * if the package has not been defined.
202      */
getDefinedPackage(String pn)203     public static Package getDefinedPackage(String pn) {
204         Package pkg = ClassLoaders.bootLoader().getDefinedPackage(pn);
205         if (pkg == null) {
206             String location = getSystemPackageLocation(pn.replace('.', '/'));
207             if (location != null) {
208                 pkg = PackageHelper.definePackage(pn.intern(), location);
209             }
210         }
211         return pkg;
212     }
213 
214     /**
215      * Returns a stream of the packages defined to the boot loader.
216      */
packages()217     public static Stream<Package> packages() {
218         return Arrays.stream(getSystemPackageNames())
219                      .map(name -> getDefinedPackage(name.replace('/', '.')));
220     }
221 
222     /**
223      * Helper class to define {@code Package} objects for packages in modules
224      * defined to the boot loader.
225      */
226     static class PackageHelper {
227         private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
228 
229         /**
230          * Define the {@code Package} with the given name. The specified
231          * location is a jrt URL to a named module in the run-time image,
232          * a file URL to a module in an exploded run-time image, or a file
233          * path to an entry on the boot class path (java agent Boot-Class-Path
234          * or -Xbootclasspath/a.
235          *
236          * <p> If the given location is a JAR file containing a manifest,
237          * the defined Package contains the versioning information from
238          * the manifest, if present.
239          *
240          * @param name     package name
241          * @param location location where the package is (jrt URL or file URL
242          *                 for a named module in the run-time or exploded image;
243          *                 a file path for a package from -Xbootclasspath/a)
244          */
definePackage(String name, String location)245         static Package definePackage(String name, String location) {
246             Module module = findModule(location);
247             if (module != null) {
248                 // named module from runtime image or exploded module
249                 if (name.isEmpty())
250                     throw new InternalError("empty package in " + location);
251                 return JLA.definePackage(ClassLoaders.bootLoader(), name, module);
252             }
253 
254             // package in unnamed module (-Xbootclasspath/a)
255             URL url = toFileURL(location);
256             Manifest man = url != null ? getManifest(location) : null;
257 
258             return ClassLoaders.bootLoader().defineOrCheckPackage(name, man, url);
259         }
260 
261         /**
262          * Finds the module at the given location defined to the boot loader.
263          * The module is either in runtime image or exploded image.
264          * Otherwise this method returns null.
265          */
findModule(String location)266         private static Module findModule(String location) {
267             String mn = null;
268             if (location.startsWith("jrt:/")) {
269                 // named module in runtime image ("jrt:/".length() == 5)
270                 mn = location.substring(5, location.length());
271             } else if (location.startsWith("file:/")) {
272                 // named module in exploded image
273                 Path path = Path.of(URI.create(location));
274                 Path modulesDir = Path.of(JAVA_HOME, "modules");
275                 if (path.startsWith(modulesDir)) {
276                     mn = path.getFileName().toString();
277                 }
278             }
279 
280             // return the Module object for the module name. The Module may
281             // in the boot layer or a child layer for the case that the module
282             // is loaded into a running VM
283             if (mn != null) {
284                 String name = mn;
285                 return Modules.findLoadedModule(mn)
286                     .orElseThrow(() -> new InternalError(name + " not loaded"));
287             } else {
288                 return null;
289             }
290         }
291 
292         /**
293          * Returns URL if the given location is a regular file path.
294          */
toFileURL(String location)295         private static URL toFileURL(String location) {
296             return AccessController.doPrivileged(new PrivilegedAction<>() {
297                 public URL run() {
298                     Path path = Path.of(location);
299                     if (Files.isRegularFile(path)) {
300                         try {
301                             return path.toUri().toURL();
302                         } catch (MalformedURLException e) {}
303                     }
304                     return null;
305                 }
306             });
307         }
308 
309         /**
310          * Returns the Manifest if the given location is a JAR file
311          * containing a manifest.
312          */
getManifest(String location)313         private static Manifest getManifest(String location) {
314             return AccessController.doPrivileged(new PrivilegedAction<>() {
315                 public Manifest run() {
316                     Path jar = Path.of(location);
317                     try (InputStream in = Files.newInputStream(jar);
318                          JarInputStream jis = new JarInputStream(in, false)) {
319                         return jis.getManifest();
320                     } catch (IOException e) {
321                         return null;
322                     }
323                 }
324             });
325         }
326     }
327 
328     /**
329      * Returns an array of the binary name of the packages defined by
330      * the boot loader, in VM internal form (forward slashes instead of dot).
331      */
332     private static native String[] getSystemPackageNames();
333 
334     /**
335      * Returns the location of the package of the given name, if
336      * defined by the boot loader; otherwise {@code null} is returned.
337      *
338      * The location may be a module from the runtime image or exploded image,
339      * or from the boot class append path (i.e. -Xbootclasspath/a or
340      * BOOT-CLASS-PATH attribute specified in java agent).
341      */
342     private static native String getSystemPackageLocation(String name);
343     private static native void setBootLoaderUnnamedModule0(Module module);
344 }
345