1/*
2 * Copyright (c) 2015, 2017, 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
26package jdk.internal.loader;
27
28import java.io.IOException;
29import java.net.URL;
30import java.nio.file.InvalidPathException;
31import java.nio.file.Paths;
32import java.security.CodeSource;
33import java.security.PermissionCollection;
34import java.util.jar.Manifest;
35
36import jdk.internal.misc.JavaLangAccess;
37import jdk.internal.misc.SharedSecrets;
38import jdk.internal.misc.VM;
39
40/**
41 * Creates and provides access to the built-in platform and application class
42 * loaders. It also creates the class loader that is used to locate resources
43 * in modules defined to the boot class loader.
44 */
45
46public class ClassLoaders {
47
48    private ClassLoaders() { }
49
50    private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
51
52    // the built-in class loaders
53    private static final BootClassLoader BOOT_LOADER;
54    private static final PlatformClassLoader PLATFORM_LOADER;
55    private static final AppClassLoader APP_LOADER;
56
57    /**
58     * Creates the built-in class loaders
59     */
60    static {
61
62        // -Xbootclasspath/a or -javaagent with Boot-Class-Path attribute
63        URLClassPath bcp = null;
64        String s = VM.getSavedProperty("jdk.boot.class.path.append");
65        if (s != null && s.length() > 0)
66            bcp = new URLClassPath(s, true);
67
68        // we have a class path if -cp is specified or -m is not specified.
69        // If neither is specified then default to -cp <working directory>
70        // If -cp is not specified and -m is specified, the value of
71        // java.class.path is an empty string, then no class path.
72        String mainMid = System.getProperty("jdk.module.main");
73        String cp = System.getProperty("java.class.path");
74        if (mainMid == null) {
75            // no main module specified so class path required
76            if (cp == null) {
77                cp = "";
78            }
79        } else {
80            // main module specified, ignore empty class path
81            if (cp != null && cp.length() == 0) {
82                cp = null;
83            }
84        }
85        URLClassPath ucp = new URLClassPath(cp, false);
86
87        // create the class loaders
88        BOOT_LOADER = new BootClassLoader(bcp);
89        PLATFORM_LOADER = new PlatformClassLoader(BOOT_LOADER);
90        APP_LOADER = new AppClassLoader(PLATFORM_LOADER, ucp);
91    }
92
93    /**
94     * Returns the class loader that is used to find resources in modules
95     * defined to the boot class loader.
96     *
97     * @apiNote This method is not public, it should instead be used via
98     * the BootLoader class that provides a restricted API to this class
99     * loader.
100     */
101    static BuiltinClassLoader bootLoader() {
102        return BOOT_LOADER;
103    }
104
105    /**
106     * Returns the platform class loader.
107     */
108    public static ClassLoader platformClassLoader() {
109        return PLATFORM_LOADER;
110    }
111
112    /**
113     * Returns the application class loader.
114     */
115    public static ClassLoader appClassLoader() {
116        return APP_LOADER;
117    }
118
119    /**
120     * The class loader that is used to find resources in modules defined to
121     * the boot class loader. It is not used for class loading.
122     */
123    private static class BootClassLoader extends BuiltinClassLoader {
124        BootClassLoader(URLClassPath bcp) {
125            super(null, null, bcp);
126        }
127
128        @Override
129        protected Class<?> loadClassOrNull(String cn) {
130            return JLA.findBootstrapClassOrNull(this, cn);
131        }
132    };
133
134    /**
135     * The platform class loader, a unique type to make it easier to distinguish
136     * from the application class loader.
137     */
138    private static class PlatformClassLoader extends BuiltinClassLoader {
139        static {
140            if (!ClassLoader.registerAsParallelCapable())
141                throw new InternalError();
142        }
143
144        PlatformClassLoader(BootClassLoader parent) {
145            super("platform", parent, null);
146        }
147
148        /**
149         * Called by the VM to support define package for AppCDS.
150         *
151         * Shared classes are returned in ClassLoader::findLoadedClass
152         * that bypass the defineClass call.
153         */
154        private Package definePackage(String pn, Module module) {
155            return JLA.definePackage(this, pn, module);
156        }
157    }
158
159    /**
160     * The application class loader that is a {@code BuiltinClassLoader} with
161     * customizations to be compatible with long standing behavior.
162     */
163    private static class AppClassLoader extends BuiltinClassLoader {
164        static {
165            if (!ClassLoader.registerAsParallelCapable())
166                throw new InternalError();
167        }
168
169        final URLClassPath ucp;
170
171        AppClassLoader(PlatformClassLoader parent, URLClassPath ucp) {
172            super("app", parent, ucp);
173            this.ucp = ucp;
174        }
175
176        @Override
177        protected Class<?> loadClass(String cn, boolean resolve)
178            throws ClassNotFoundException
179        {
180            // for compatibility reasons, say where restricted package list has
181            // been updated to list API packages in the unnamed module.
182            SecurityManager sm = System.getSecurityManager();
183            if (sm != null) {
184                int i = cn.lastIndexOf('.');
185                if (i != -1) {
186                    sm.checkPackageAccess(cn.substring(0, i));
187                }
188            }
189
190            return super.loadClass(cn, resolve);
191        }
192
193        @Override
194        protected PermissionCollection getPermissions(CodeSource cs) {
195            PermissionCollection perms = super.getPermissions(cs);
196            perms.add(new RuntimePermission("exitVM"));
197            return perms;
198        }
199
200        /**
201         * Called by the VM to support dynamic additions to the class path
202         *
203         * @see java.lang.instrument.Instrumentation#appendToSystemClassLoaderSearch
204         */
205        void appendToClassPathForInstrumentation(String path) {
206            ucp.addFile(path);
207        }
208
209        /**
210         * Called by the VM to support define package for AppCDS
211         *
212         * Shared classes are returned in ClassLoader::findLoadedClass
213         * that bypass the defineClass call.
214         */
215        private Package definePackage(String pn, Module module) {
216            return JLA.definePackage(this, pn, module);
217        }
218
219        /**
220         * Called by the VM to support define package for AppCDS
221         */
222        protected Package defineOrCheckPackage(String pn, Manifest man, URL url) {
223            return super.defineOrCheckPackage(pn, man, url);
224        }
225    }
226
227    /**
228     * Attempts to convert the given string to a file URL.
229     *
230     * @apiNote This is called by the VM
231     */
232    @Deprecated
233    private static URL toFileURL(String s) {
234        try {
235            // Use an intermediate File object to construct a URI/URL without
236            // authority component as URLClassPath can't handle URLs with a UNC
237            // server name in the authority component.
238            return Paths.get(s).toRealPath().toFile().toURI().toURL();
239        } catch (InvalidPathException | IOException ignore) {
240            // malformed path string or class path element does not exist
241            return null;
242        }
243    }
244}
245