/Users/lyon/j4p/src/javagroup/util/URLClassLoader.java
|
1 /*
2 * Copyright (C) 1997, 1998 Luke Gorrie
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
17 * MA 02139, USA.
18 */
19
20 package javagroup.util;
21
22 import java.io.*;
23 import java.net.MalformedURLException;
24 import java.net.URL;
25 import java.util.Enumeration;
26 import java.util.Hashtable;
27 import java.util.StringTokenizer;
28 import java.util.Vector;
29
30 /**
31 * Generic classloader for URL-based classpaths.
32 * <p/>
33 * TODO: add archive support, and rewrite as this is currently a hack
34 *
35 * @author Luke Gorrie
36 */
37 public class URLClassLoader extends ClassLoader {
38
39 protected Vector _urlClassPath;
40 protected Hashtable _classCache;
41
42 /**
43 * Creates a ClassLoader with no classpath.
44 */
45 public URLClassLoader() {
46 _urlClassPath = new Vector();
47 _classCache = new Hashtable();
48 }
49
50 /**
51 * Creates a ClassLoader with a single URL for a classpath.
52 *
53 * @param classpath The base URL to search for classes relative to.
54 */
55 public URLClassLoader(URL classpath) {
56 this();
57 addClassPath(classpath);
58 }
59
60 /**
61 * Creates a ClassLoader with a list of classpath URLs.
62 *
63 * @param classpath The URLs to search for classes relative to.
64 */
65 public URLClassLoader(URL[] classpath) {
66 this();
67 addClassPath(classpath);
68 }
69
70 public void addClassPath(URL[] classpath) {
71 for (int i = 0; i < classpath.length; i++)
72 addClassPath(classpath[i]);
73 }
74
75 /**
76 * Add a URL to the classpath. This URL is searched for for classes.
77 *
78 * @param classpath The base URL to search.
79 */
80 public void addClassPath(URL classpath) {
81 _urlClassPath.addElement(classpath);
82 }
83
84 public Class loadMainClass(String name)
85 throws ClassNotFoundException {
86 return loadClass(name, true, true);
87 }
88
89 public Class loadClass(String name, boolean resolve)
90 throws ClassNotFoundException {
91
92 return loadClass(name, resolve, false);
93 }
94
95 public Class loadClass(String name,
96 boolean resolve,
97 boolean mainClass)
98 throws ClassNotFoundException {
99
100 Class klass = null;
101
102 try {
103
104 // give the vm a chance to find it in the classpath
105 try {
106 klass = findSystemClass(name);
107 if (klass != null)
108 return klass;
109 } catch (ClassNotFoundException e) {
110 }
111
112 // check cache
113 klass = (Class) _classCache.get(name);
114 if (klass != null) {
115 return klass;
116 }
117
118 // read from custom classpath
119 byte[] data = readClassFile(name);
120 if (data != null) {
121 if (mainClass) {
122 forcePublic(data);
123 }
124 klass = defineClass(name, data, 0, data.length);
125 }
126
127 if (klass == null)
128 throw new ClassNotFoundException(
129 "Class not found: " + name);
130
131 _classCache.put(name, klass);
132
133 return klass;
134
135 } finally {
136 // if the class was found, and is to be resolved, resolve
137 if ((klass != null) && resolve)
138 resolveClass(klass);
139 }
140 }
141
142 /**
143 * Try to read the byte[] data for a class file from the classpath.
144 *
145 * @param classname The fully-qualified name of the class.
146 * @return A byte[] containing the classfile data, or null if not
147 * found.
148 */
149 protected byte[] readClassFile(String classname) {
150 classname = classname.replace('.', '/') + ".class";
151 return readFile(classname);
152 }
153
154 public InputStream getResourceAsStream(String name) {
155 byte[] data = readFile(name);
156 return data == null ? null : new ByteArrayInputStream(data);
157 }
158
159 // bit of a hack. :-)
160 public URL getResource(String name) {
161 URL path = null;
162 Enumeration classpath = _urlClassPath.elements();
163 while (classpath.hasMoreElements()) {
164 URL base_path = (URL) classpath.nextElement();
165 try {
166 path = new URL(base_path, name);
167 return path;
168 } catch (IOException e) {
169 // file not accessible or doesn't exist
170 }
171 }
172 return null;
173 }
174
175 protected byte[] readFile(String name) {
176
177 // enumeration of base-urls to try
178 Enumeration classpath = _urlClassPath.elements();
179 byte[] data = null;
180
181 // loop until a class is read, or there are no more paths to try
182 while ((data == null) && (classpath.hasMoreElements())) {
183
184 // pop a path to try
185 URL base_path = (URL) classpath.nextElement();
186
187 try {
188
189 // create a fully qualified class url
190 URL path = new URL(base_path, name);
191
192 // io streams
193 ByteArrayOutputStream out_buffer = new ByteArrayOutputStream();
194 InputStream in = new BufferedInputStream(
195 path.openStream());
196
197 // read into buffer
198 int octet;
199 while ((octet = in.read()) != -1)
200 out_buffer.write(octet);
201
202 // pull class data out of buffer
203 data = out_buffer.toByteArray();
204
205 } catch (IOException e) {
206 // class not found in that path
207 }
208
209 }
210
211 // null if class not found
212 return data;
213
214 }
215
216 /**
217 * Converts a path string into an array of URLs. eg. "foo:http://bar/"
218 * would become a 2-URL array, with "file:///foo/" and "http://bar/".
219 *
220 * @param classpath The path to decode, entries delimited by the
221 * appropriate charactor for the platform.
222 */
223 public static URL[] decodePathString(String classpath) {
224
225 // create a file:/// as a base URL. This was "foo" will be seen as
226 // file:///foo
227 URL base_url = null;
228 try {
229 base_url = new URL("file:/");
230 } catch (MalformedURLException e) {
231 }
232
233 // vector to store URLs in temporarily
234 Vector classpath_urls = new Vector();
235
236 // if a base url is provided and the classpath is non-null
237 if ((base_url != null) && (classpath != null)) {
238
239 // tokenize path
240 StringTokenizer tok = new StringTokenizer(classpath, ",");
241 while (tok.hasMoreTokens()) {
242 String path = tok.nextToken();
243 // create URL for path entry
244
245 URL path_url = null;
246 try {
247 path_url = new URL(path);
248 } catch (MalformedURLException e) {
249 try {
250 path_url = new URL(base_url, path);
251 } catch (Exception e2) {
252 }
253 }
254
255 if (path_url != null)
256 classpath_urls.addElement(path_url);
257 }
258 }
259
260 URL[] paths = null;
261
262 // if there are any valid classpath entries
263 if (!classpath_urls.isEmpty()) {
264 // convert the vector into a URL array
265 paths = new URL[classpath_urls.size()];
266 for (int i = 0; i < paths.length; i++)
267 paths[i] = (URL) classpath_urls.elementAt(i);
268 }
269
270 // return path url array
271 return paths;
272
273 }
274
275 /**
276 * Take a byte array of class data and modify it to ensure that the
277 * class is public. This is used for the "main" classes of
278 * applications.
279 */
280 public void forcePublic(byte[] theClass) {
281 int constant_pool_count = ((theClass[8] & 0xff) << 8)
282 | (theClass[9] & 0xff);
283
284 int currOffset = 10;
285
286 // seek through everything in the way of the access modifiers
287 for (int i = 1; i < constant_pool_count; i++) {
288 switch (theClass[currOffset] & 0xff) {
289 case 7:
290 case 8: // CONSTANT_Class, CONSTANT_String
291 currOffset += 3;
292 break;
293 case 9:
294 case 10:
295 case 11:
296 case 12:
297 case 3:
298 case 4: // CONSTANT_Fieldref, CONSTANT_Methodref
299 currOffset += 5; // CONSTANT_InterfaceMethodref, CONSTANT_NameAndType
300 break; // CONSTANT_Integer, CONSTANT_Float
301 case 5:
302 case 6: // CONSTANT_Long, CONSTANT_Double
303 currOffset += 9;
304 i++;
305 break;
306 case 1:
307 int length = ((theClass[++currOffset] & 0xff) << 8)
308 | (theClass[++currOffset] & 0xff);
309 currOffset += length + 1;
310 break;
311 default:
312 return;
313 }
314 }
315
316 // add PUBLIC flag
317 theClass[currOffset + 1] |= 1;
318 }
319
320 }
321
322