Mercurial > hg > blitz_condensed
comparison src/com/go/trove/util/ClassInjector.java @ 0:3dc0c5604566
Initial checkin of blitz 2.0 fcs - no installer yet.
author | Dan Creswell <dan.creswell@gmail.com> |
---|---|
date | Sat, 21 Mar 2009 11:00:06 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:3dc0c5604566 |
---|---|
1 /* ==================================================================== | |
2 * Trove - Copyright (c) 1997-2000 Walt Disney Internet Group | |
3 * ==================================================================== | |
4 * The Tea Software License, Version 1.1 | |
5 * | |
6 * Copyright (c) 2000 Walt Disney Internet Group. All rights reserved. | |
7 * | |
8 * Redistribution and use in source and binary forms, with or without | |
9 * modification, are permitted provided that the following conditions | |
10 * are met: | |
11 * | |
12 * 1. Redistributions of source code must retain the above copyright | |
13 * notice, this list of conditions and the following disclaimer. | |
14 * | |
15 * 2. Redistributions in binary form must reproduce the above copyright | |
16 * notice, this list of conditions and the following disclaimer in | |
17 * the documentation and/or other materials provided with the | |
18 * distribution. | |
19 * | |
20 * 3. The end-user documentation included with the redistribution, | |
21 * if any, must include the following acknowledgment: | |
22 * "This product includes software developed by the | |
23 * Walt Disney Internet Group (http://opensource.go.com/)." | |
24 * Alternately, this acknowledgment may appear in the software itself, | |
25 * if and wherever such third-party acknowledgments normally appear. | |
26 * | |
27 * 4. The names "Tea", "TeaServlet", "Kettle", "Trove" and "BeanDoc" must | |
28 * not be used to endorse or promote products derived from this | |
29 * software without prior written permission. For written | |
30 * permission, please contact opensource@dig.com. | |
31 * | |
32 * 5. Products derived from this software may not be called "Tea", | |
33 * "TeaServlet", "Kettle" or "Trove", nor may "Tea", "TeaServlet", | |
34 * "Kettle", "Trove" or "BeanDoc" appear in their name, without prior | |
35 * written permission of the Walt Disney Internet Group. | |
36 * | |
37 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED | |
38 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
39 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
40 * DISCLAIMED. IN NO EVENT SHALL THE WALT DISNEY INTERNET GROUP OR ITS | |
41 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
42 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
43 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
44 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
45 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
46 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
47 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
48 * ==================================================================== | |
49 * | |
50 * For more information about Tea, please see http://opensource.go.com/. | |
51 */ | |
52 | |
53 package com.go.trove.util; | |
54 | |
55 import java.io.OutputStream; | |
56 import java.io.ByteArrayOutputStream; | |
57 import java.io.ByteArrayInputStream; | |
58 import java.io.File; | |
59 import java.io.InputStream; | |
60 import java.io.FileInputStream; | |
61 import java.io.IOException; | |
62 import java.lang.ref.*; | |
63 import java.net.URL; | |
64 import java.net.URLConnection; | |
65 import java.net.URLStreamHandler; | |
66 import java.util.*; | |
67 import java.util.zip.GZIPInputStream; | |
68 import java.util.zip.GZIPOutputStream; | |
69 | |
70 /****************************************************************************** | |
71 * A special ClassLoader that allows classes to be defined by directly | |
72 * injecting the bytecode. All classes other than those injected are loaded | |
73 * from the parent ClassLoader, which is the ClassLoader that loaded this | |
74 * class. If a directory is passed in, the ClassInjector looks there for | |
75 * non-injected class files before asking the parent ClassLoader for a class. | |
76 * | |
77 * @author Brian S O'Neill | |
78 * @version | |
79 * <!--$$Revision: 1.1 $-->, <!--$$JustDate:--> 7/30/01 <!-- $--> | |
80 */ | |
81 public class ClassInjector extends ClassLoader { | |
82 private static Map cShared = new NullKeyMap(new IdentityMap()); | |
83 | |
84 /** | |
85 * Returns a shared ClassInjector instance. | |
86 */ | |
87 public static ClassInjector getInstance() { | |
88 return getInstance(null); | |
89 } | |
90 | |
91 /** | |
92 * Returns a shared ClassInjector instance for the given ClassLoader. | |
93 */ | |
94 public static ClassInjector getInstance(ClassLoader loader) { | |
95 ClassInjector injector = null; | |
96 | |
97 synchronized (cShared) { | |
98 Reference ref = (Reference)cShared.get(loader); | |
99 if (ref != null) { | |
100 injector = (ClassInjector)ref.get(); | |
101 } | |
102 if (injector == null) { | |
103 injector = new ClassInjector(loader); | |
104 cShared.put(loader, new WeakReference(injector)); | |
105 } | |
106 return injector; | |
107 } | |
108 } | |
109 | |
110 // Parent ClassLoader, used to load classes that aren't defined by this. | |
111 private ClassLoader mSuperLoader; | |
112 | |
113 private File[] mRootClassDirs; | |
114 private String mRootPackage; | |
115 | |
116 // A set of all the classes defined by the ClassInjector. | |
117 private Map mDefined = Collections.synchronizedMap(new HashMap()); | |
118 | |
119 // A map to store raw bytecode for future use in getResourceAsStream(). | |
120 private Map mGZippedBytecode; | |
121 | |
122 private URLStreamHandler mFaker; | |
123 | |
124 /** | |
125 * Construct a ClassInjector that uses the ClassLoader that loaded this | |
126 * class as a parent, and it has no root class directory or root package. | |
127 */ | |
128 public ClassInjector() { | |
129 this(null, (File[])null, null); | |
130 } | |
131 | |
132 /** | |
133 * Construct a ClassInjector that has no root class directory or root | |
134 * package. | |
135 * | |
136 * @param parent optional parent ClassLoader to default to when a class | |
137 * cannot be loaded with this ClassInjector. | |
138 */ | |
139 public ClassInjector(ClassLoader parent) { | |
140 this(parent, (File[])null, null); | |
141 } | |
142 | |
143 /** | |
144 * Construct a ClassInjector that uses the ClassLoader that loaded this | |
145 * class as a parent. | |
146 * | |
147 * @param rootClassDir optional directory to look for non-injected classes | |
148 * @param rootPackage optional package name for the root directory | |
149 */ | |
150 public ClassInjector(File rootClassDir, String rootPackage) { | |
151 this(null, (rootClassDir == null) ? null : new File[]{rootClassDir}, | |
152 rootPackage); | |
153 } | |
154 | |
155 /** | |
156 * @param parent optional parent ClassLoader to default to when a class | |
157 * cannot be loaded with this ClassInjector. | |
158 * @param rootClassDir optional directory to look for non-injected classes | |
159 * @param rootPackage optional package name for the root directory | |
160 */ | |
161 public ClassInjector(ClassLoader parent, | |
162 File rootClassDir, String rootPackage) { | |
163 this(parent, (rootClassDir == null) ? null : new File[]{rootClassDir}, | |
164 rootPackage); | |
165 } | |
166 | |
167 /** | |
168 * Construct a ClassInjector that uses the ClassLoader that loaded this | |
169 * class as a parent. | |
170 * | |
171 * @param rootClassDirs optional directories to look for non-injected | |
172 * classes | |
173 * @param rootPackage optional package name for the root directory | |
174 */ | |
175 public ClassInjector(File[] rootClassDirs, String rootPackage) { | |
176 this(null, rootClassDirs, rootPackage); | |
177 } | |
178 | |
179 /** | |
180 * @param parent optional parent ClassLoader to default to when a class | |
181 * cannot be loaded with this ClassInjector. | |
182 * @param rootClassDirs optional directories to look for non-injected | |
183 * classes | |
184 * @param rootPackage optional package name for the root directory | |
185 */ | |
186 public ClassInjector(ClassLoader parent, | |
187 File[] rootClassDirs, | |
188 String rootPackage) { | |
189 this(parent, rootClassDirs, rootPackage, false); | |
190 } | |
191 | |
192 /** | |
193 * @param parent optional parent ClassLoader to default to when a class | |
194 * cannot be loaded with this ClassInjector. | |
195 * @param rootClassDirs optional directories to look for non-injected | |
196 * classes | |
197 * @param rootPackage optional package name for the root directory | |
198 * @param keepRawBytecode if true, will cause the ClassInjector to store | |
199 * the raw bytecode of defined classes. | |
200 */ | |
201 public ClassInjector(ClassLoader parent, | |
202 File[] rootClassDirs, | |
203 String rootPackage, | |
204 boolean keepRawBytecode) { | |
205 super(); | |
206 if (parent == null) { | |
207 parent = getClass().getClassLoader(); | |
208 } | |
209 mSuperLoader = parent; | |
210 if (rootClassDirs != null) { | |
211 mRootClassDirs = (File[])rootClassDirs.clone(); | |
212 } | |
213 if (rootPackage != null && !rootPackage.endsWith(".")) { | |
214 rootPackage += '.'; | |
215 } | |
216 mRootPackage = rootPackage; | |
217 | |
218 if (keepRawBytecode) { | |
219 mGZippedBytecode = Collections.synchronizedMap(new HashMap()); | |
220 } | |
221 } | |
222 | |
223 /** | |
224 * Get a stream used to define a class. Close the stream to finish the | |
225 * definition. | |
226 * | |
227 * @param the fully qualified name of the class to be defined. | |
228 */ | |
229 public OutputStream getStream(String name) { | |
230 return new Stream(name); | |
231 } | |
232 | |
233 public URL getResource(String name) { | |
234 | |
235 if (mGZippedBytecode != null) { | |
236 if (mGZippedBytecode.containsKey(name)) { | |
237 try { | |
238 return new URL("file", null, -1, name, getURLFaker()); | |
239 } | |
240 catch (Exception e) { | |
241 e.printStackTrace(); | |
242 } | |
243 System.out.println("created URL for " + name); | |
244 } | |
245 } | |
246 return mSuperLoader.getResource(name); | |
247 } | |
248 | |
249 private URLStreamHandler getURLFaker() { | |
250 if (mFaker == null) { | |
251 mFaker = new URLFaker(); | |
252 } | |
253 return mFaker; | |
254 } | |
255 | |
256 protected Class loadClass(String name, boolean resolve) | |
257 throws ClassNotFoundException { | |
258 | |
259 Class clazz = findLoadedClass(name); | |
260 | |
261 if (clazz == null) { | |
262 synchronized (this) { | |
263 clazz = findLoadedClass(name); | |
264 | |
265 if (clazz == null) { | |
266 clazz = loadFromFile(name); | |
267 | |
268 if (clazz == null) { | |
269 if (mSuperLoader != null) { | |
270 clazz = mSuperLoader.loadClass(name); | |
271 } | |
272 else { | |
273 clazz = findSystemClass(name); | |
274 } | |
275 | |
276 if (clazz == null) { | |
277 throw new ClassNotFoundException(name); | |
278 } | |
279 } | |
280 } | |
281 } | |
282 } | |
283 | |
284 if (resolve) { | |
285 resolveClass(clazz); | |
286 } | |
287 | |
288 return clazz; | |
289 } | |
290 | |
291 protected void define(String name, byte[] data) { | |
292 defineClass(name, data, 0, data.length); | |
293 if (mGZippedBytecode != null) { | |
294 try { | |
295 ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |
296 GZIPOutputStream gz = new GZIPOutputStream(baos); | |
297 gz.write(data,0,data.length); | |
298 gz.close(); | |
299 mGZippedBytecode.put(name.replace('.','/') + ".class", | |
300 baos.toByteArray()); | |
301 } | |
302 catch (IOException ioe) { | |
303 ioe.printStackTrace(); | |
304 } | |
305 } | |
306 } | |
307 | |
308 private Class loadFromFile(String name) throws ClassNotFoundException { | |
309 if (mRootClassDirs == null) { | |
310 return null; | |
311 } | |
312 | |
313 String fileName = name; | |
314 | |
315 if (mRootPackage != null) { | |
316 if (fileName.startsWith(mRootPackage)) { | |
317 fileName = fileName.substring(mRootPackage.length()); | |
318 } | |
319 else { | |
320 return null; | |
321 } | |
322 } | |
323 | |
324 fileName = fileName.replace('.', File.separatorChar); | |
325 ClassNotFoundException error = null; | |
326 | |
327 for (int i=0; i<mRootClassDirs.length; i++) { | |
328 File file = new File(mRootClassDirs[i], fileName + ".class"); | |
329 | |
330 if (file.exists()) { | |
331 try { | |
332 byte[] buffer = new byte[(int)file.length()]; | |
333 int avail = buffer.length; | |
334 int offset = 0; | |
335 InputStream in = new FileInputStream(file); | |
336 | |
337 int len = -1; | |
338 while ( (len = in.read(buffer, offset, avail)) > 0 ) { | |
339 offset += len; | |
340 | |
341 if ( (avail -= len) <= 0 ) { | |
342 avail = buffer.length; | |
343 byte[] newBuffer = new byte[avail * 2]; | |
344 System.arraycopy(buffer, 0, newBuffer, 0, avail); | |
345 buffer = newBuffer; | |
346 } | |
347 } | |
348 | |
349 in.close(); | |
350 | |
351 return defineClass(name, buffer, 0, offset); | |
352 } | |
353 catch (IOException e) { | |
354 if (error == null) { | |
355 error = new ClassNotFoundException | |
356 (fileName + ": " + e.toString()); | |
357 } | |
358 } | |
359 } | |
360 } | |
361 | |
362 if (error != null) { | |
363 throw error; | |
364 } | |
365 else { | |
366 return null; | |
367 } | |
368 } | |
369 | |
370 private class Stream extends ByteArrayOutputStream { | |
371 private String mName; | |
372 | |
373 public Stream(String name) { | |
374 super(1024); | |
375 mName = name; | |
376 } | |
377 | |
378 public void close() { | |
379 synchronized (mDefined) { | |
380 if (mDefined.get(mName) == null) { | |
381 define(mName, toByteArray()); | |
382 mDefined.put(mName, mName); | |
383 } | |
384 } | |
385 } | |
386 } | |
387 | |
388 private class URLFaker extends URLStreamHandler { | |
389 | |
390 protected URLConnection openConnection(URL u) throws IOException { | |
391 return new ClassInjector.ResourceConnection(u); | |
392 } | |
393 } | |
394 | |
395 private class ResourceConnection extends URLConnection { | |
396 | |
397 String resourceName; | |
398 public ResourceConnection(URL u) { | |
399 super(u); | |
400 resourceName = u.getFile(); | |
401 } | |
402 | |
403 // not really needed here but it was abstract. | |
404 public void connect() {} | |
405 | |
406 public InputStream getInputStream() throws IOException { | |
407 | |
408 try { | |
409 if (mGZippedBytecode != null) { | |
410 | |
411 if (mGZippedBytecode.get(resourceName) != null) { | |
412 return new GZIPInputStream(new ByteArrayInputStream | |
413 ((byte[])mGZippedBytecode.get(resourceName))); | |
414 } | |
415 else { | |
416 System.out.println(resourceName + " not found in bytecode map."); | |
417 } | |
418 } | |
419 else { | |
420 System.out.println("no bytecode map configured in "+ ClassInjector.this); | |
421 } | |
422 } | |
423 catch (Exception e) { | |
424 e.printStackTrace(); | |
425 } | |
426 | |
427 return null; | |
428 } | |
429 } | |
430 } |