001 /*
002 * This file is part of the Jikes RVM project (http://jikesrvm.org).
003 *
004 * This file is licensed to You under the Eclipse Public License (EPL);
005 * You may not use this file except in compliance with the License. You
006 * may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/eclipse-1.0.php
009 *
010 * See the COPYRIGHT.txt file distributed with this work for information
011 * regarding copyright ownership.
012 */
013 package org.jikesrvm.classloader;
014
015 import java.io.DataInputStream;
016 import java.io.File;
017 import java.io.FileInputStream;
018 import java.io.IOException;
019 import java.io.InputStream;
020 import java.net.URL;
021 import java.util.Enumeration;
022 import java.util.HashMap;
023 import java.util.StringTokenizer;
024 import java.util.Vector;
025 import java.util.zip.ZipEntry;
026 import java.util.zip.ZipFile;
027 import org.jikesrvm.VM;
028 import org.jikesrvm.runtime.Entrypoints;
029 import org.jikesrvm.util.ImmutableEntryHashMapRVM;
030
031 /**
032 * Implements an object that functions as the bootstrap class loader.
033 * This class is a Singleton pattern.
034 */
035 public final class BootstrapClassLoader extends java.lang.ClassLoader {
036
037 private final ImmutableEntryHashMapRVM<String, RVMType> loaded =
038 new ImmutableEntryHashMapRVM<String, RVMType>();
039
040 /** Places whence we load bootstrap .class files. */
041 private static String bootstrapClasspath;
042
043 /**
044 * Set list of places to be searched for vm classes and resources.
045 * @param bootstrapClasspath path specification in standard "classpath"
046 * format
047 */
048 public static void setBootstrapRepositories(String bootstrapClasspath) {
049 BootstrapClassLoader.bootstrapClasspath = bootstrapClasspath;
050 }
051
052 /**
053 * @return List of places to be searched for VM classes and resources,
054 * in standard "classpath" format
055 */
056 public static String getBootstrapRepositories() {
057 return bootstrapClasspath;
058 }
059
060 /**
061 * Initialize for execution.
062 * @param bootstrapClasspath names of directories containing the bootstrap
063 * .class files, and the names of any .zip/.jar files.
064 * These are the ones that implement the VM and its
065 * standard runtime libraries. This may contain several names separated
066 * with colons (':'), just
067 * as a classpath may. (<code>null</code> ==> use the values specified by
068 * {@link #setBootstrapRepositories} when the boot image was created. This
069 * feature is not actually used, but may be helpful in avoiding trouble.)
070 */
071 public static void boot(String bootstrapClasspath) {
072 if (bootstrapClasspath != null) {
073 BootstrapClassLoader.bootstrapClasspath = bootstrapClasspath;
074 }
075 zipFileCache = new HashMap<String, ZipFile>();
076 if (VM.runningVM) {
077 try {
078 /* Here, we have to replace the fields that aren't carried over from
079 * boot image writing time to run time.
080 * This would be the following, if the fields weren't final:
081 *
082 * bootstrapClassLoader.definedPackages = new HashMap();
083 */
084 Entrypoints.classLoaderDefinedPackages.setObjectValueUnchecked(bootstrapClassLoader,
085 new java.util.HashMap<String, Package>());
086 } catch (Exception e) {
087 VM.sysFail("Failed to setup bootstrap class loader");
088 }
089 }
090 }
091
092 /** Prevent other classes from constructing one. */
093 private BootstrapClassLoader() {
094 super(null);
095 }
096
097 /* Interface */
098 private static final BootstrapClassLoader bootstrapClassLoader = new BootstrapClassLoader();
099
100 public static BootstrapClassLoader getBootstrapClassLoader() {
101 return bootstrapClassLoader;
102 }
103
104 /**
105 * Backdoor for use by TypeReference.resolve when !VM.runningVM.
106 * As of this writing, it is not used by any other classes.
107 * @throws NoClassDefFoundError
108 */
109 synchronized RVMType loadVMClass(String className) throws NoClassDefFoundError {
110 try {
111 InputStream is = getResourceAsStream(className.replace('.', File.separatorChar) + ".class");
112 if (is == null) throw new NoClassDefFoundError(className);
113 DataInputStream dataInputStream = new DataInputStream(is);
114 RVMType type = null;
115 try {
116 // Debugging:
117 // VM.sysWriteln("loadVMClass: trying to resolve className " + className);
118 type = RVMClassLoader.defineClassInternal(className, dataInputStream, this);
119 loaded.put(className, type);
120 } finally {
121 try {
122 // Make sure the input stream is closed.
123 dataInputStream.close();
124 } catch (IOException e) { }
125 }
126 return type;
127 } catch (NoClassDefFoundError e) {
128 throw e;
129 } catch (Throwable e) {
130 // We didn't find the class, or it wasn't valid, etc.
131 NoClassDefFoundError ncdf = new NoClassDefFoundError(className);
132 ncdf.initCause(e);
133 throw ncdf;
134 }
135 }
136
137 public synchronized Class<?> loadClass(String className, boolean resolveClass) throws ClassNotFoundException {
138 if (!VM.runningVM) {
139 return super.loadClass(className, resolveClass);
140 }
141 if (className.startsWith("L") && className.endsWith(";")) {
142 className = className.substring(1, className.length() - 2);
143 }
144 RVMType loadedType = loaded.get(className);
145 Class<?> loadedClass;
146 if (loadedType == null) {
147 loadedClass = findClass(className);
148 } else {
149 loadedClass = loadedType.getClassForType();
150 }
151 if (resolveClass) {
152 resolveClass(loadedClass);
153 }
154 return loadedClass;
155 }
156
157 /**
158 * Search the bootstrap class loader's classpath for given class.
159 *
160 * @param className the name of the class to load
161 * @return the class object, if it was found
162 * @exception ClassNotFoundException if the class was not found, or was invalid
163 */
164 public Class<?> findClass(String className) throws ClassNotFoundException {
165 final boolean DBG=false;
166 if (!VM.runningVM) {
167 return super.findClass(className);
168 }
169 if (className.startsWith("[")) {
170 TypeReference typeRef =
171 TypeReference.findOrCreate(this, Atom.findOrCreateAsciiAtom(className.replace('.', '/')));
172 RVMType ans = typeRef.resolve();
173 loaded.put(className, ans);
174 return ans.getClassForType();
175 } else {
176 if (!VM.fullyBooted) {
177 VM.sysWrite("Trying to load a class (");
178 VM.sysWrite(className);
179 VM.sysWrite(") too early in the booting process, before dynamic");
180 VM.sysWriteln(" class loading is enabled; aborting.");
181 VM.sysFail("Trying to load a class too early in the booting process");
182 }
183 // class types: try to find the class file
184 try {
185 if (className.startsWith("L") && className.endsWith(";")) {
186 className = className.substring(1, className.length() - 2);
187 }
188 InputStream is = getResourceAsStream(className.replace('.', File.separatorChar) + ".class");
189 if (is == null) throw new ClassNotFoundException(className);
190 DataInputStream dataInputStream = new DataInputStream(is);
191 Class<?> cls = null;
192 try {
193 RVMType type = RVMClassLoader.defineClassInternal(className, dataInputStream, this);
194 loaded.put(className, type);
195 cls = type.getClassForType();
196 } finally {
197 try {
198 // Make sure the input stream is closed.
199 dataInputStream.close();
200 } catch (IOException e) { }
201 }
202 return cls;
203 } catch (ClassNotFoundException e) {
204 throw e;
205 } catch (Throwable e) {
206 if (DBG) {
207 VM.sysWrite("About to throw ClassNotFoundException(", className, ") because we got this Throwable:");
208 e.printStackTrace();
209 }
210 // We didn't find the class, or it wasn't valid, etc.
211 throw new ClassNotFoundException(className, e);
212 }
213 }
214 }
215
216 /** Keep this a static field, since it's looked at in
217 * {@link MemberReference#parse}. */
218 public static final String myName = "BootstrapCL";
219
220 public String toString() { return myName; }
221
222 private static HashMap<String, ZipFile> zipFileCache;
223
224 private interface Handler<T> {
225 void process(ZipFile zf, ZipEntry ze) throws Exception;
226
227 void process(File f) throws Exception;
228
229 T getResult();
230 }
231
232 public InputStream getResourceAsStream(final String name) {
233 Handler<InputStream> findStream = new Handler<InputStream>() {
234 InputStream stream;
235
236 public InputStream getResult() { return stream; }
237
238 public void process(ZipFile zf, ZipEntry ze) throws Exception {
239 stream = zf.getInputStream(ze);
240 }
241
242 public void process(File file) throws Exception {
243 stream = new FileInputStream(file);
244 }
245 };
246
247 return getResourceInternal(name, findStream, false);
248 }
249
250 public URL findResource(final String name) {
251 Handler<URL> findURL = new Handler<URL>() {
252 URL url;
253
254 public URL getResult() { return url; }
255
256 public void process(ZipFile zf, ZipEntry ze) throws Exception {
257 url = new URL("jar", null, -1, "file:" + zf.getName() + "!/" + name);
258 }
259
260 public void process(File file) throws Exception {
261 url = new URL("file", null, -1, file.getName());
262 }
263 };
264
265 return getResourceInternal(name, findURL, false);
266 }
267
268 public Enumeration<URL> findResources(final String name) {
269 Handler<Enumeration<URL>> findURL = new Handler<Enumeration<URL>>() {
270 Vector<URL> urls;
271
272 public Enumeration<URL> getResult() {
273 if (urls == null) urls = new Vector<URL>();
274 return urls.elements();
275 }
276
277 public void process(ZipFile zf, ZipEntry ze) throws Exception {
278 if (urls == null) urls = new Vector<URL>();
279 urls.addElement(new URL("jar", null, -1, "file:" + zf.getName() + "!/" + name));
280 }
281
282 public void process(File file) throws Exception {
283 if (urls == null) urls = new Vector<URL>();
284 urls.addElement(new URL("file", null, -1, file.getName()));
285 }
286 };
287
288 return getResourceInternal(name, findURL, true);
289 }
290
291 private <T> T getResourceInternal(String name, Handler<T> h, boolean multiple) {
292 if (name.startsWith(File.separator)) {
293 name = name.substring(File.separator.length());
294 }
295
296 StringTokenizer tok = new StringTokenizer(getBootstrapRepositories(), File.pathSeparator);
297
298 while (tok.hasMoreElements()) {
299 try {
300 String path = tok.nextToken();
301 if (path.endsWith(".jar") || path.endsWith(".zip")) {
302 ZipFile zf = zipFileCache.get(path);
303 if (zf == null) {
304 zf = new ZipFile(path);
305 if (zf == null) {
306 continue;
307 } else {
308 zipFileCache.put(path, zf);
309 }
310 }
311 // Zip spec. states that separator must be '/' in the path
312 if (File.separatorChar != '/') {
313 name = name.replace(File.separatorChar, '/');
314 }
315 ZipEntry ze = zf.getEntry(name);
316 if (ze == null) continue;
317
318 h.process(zf, ze);
319 if (!multiple) return h.getResult();
320 } else if (path.endsWith(File.separator)) {
321 File file = new File(path + name);
322 if (file.exists()) {
323 h.process(file);
324 if (!multiple) return h.getResult();
325 }
326 } else {
327 File file = new File(path + File.separator + name);
328 if (file.exists()) {
329 h.process(file);
330 if (!multiple) return h.getResult();
331 }
332 }
333 } catch (Exception e) {
334 }
335 }
336
337 return (multiple) ? h.getResult() : null;
338 }
339 }