KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > go > trove > util > ClassInjector


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 JavaDoc;
56 import java.io.ByteArrayOutputStream JavaDoc;
57 import java.io.ByteArrayInputStream JavaDoc;
58 import java.io.File JavaDoc;
59 import java.io.InputStream JavaDoc;
60 import java.io.FileInputStream JavaDoc;
61 import java.io.IOException JavaDoc;
62 import java.lang.ref.*;
63 import java.net.URL JavaDoc;
64 import java.net.URLConnection JavaDoc;
65 import java.net.URLStreamHandler JavaDoc;
66 import java.util.*;
67 import java.util.zip.GZIPInputStream JavaDoc;
68 import java.util.zip.GZIPOutputStream JavaDoc;
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:--> 29 <!-- $-->, <!--$$JustDate:--> 7/30/01 <!-- $-->
80  */

81 public class ClassInjector extends ClassLoader JavaDoc {
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 JavaDoc 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 JavaDoc mSuperLoader;
112     
113     private File JavaDoc[] mRootClassDirs;
114     private String JavaDoc 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 JavaDoc 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 JavaDoc[])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 JavaDoc parent) {
140         this(parent, (File JavaDoc[])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 JavaDoc rootClassDir, String JavaDoc rootPackage) {
151         this(null, (rootClassDir == null) ? null : new File JavaDoc[]{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 JavaDoc parent,
162                          File JavaDoc rootClassDir, String JavaDoc rootPackage) {
163         this(parent, (rootClassDir == null) ? null : new File JavaDoc[]{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 JavaDoc[] rootClassDirs, String JavaDoc 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 JavaDoc parent,
187                          File JavaDoc[] rootClassDirs,
188                          String JavaDoc 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 JavaDoc parent,
202                          File JavaDoc[] rootClassDirs,
203                          String JavaDoc 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 JavaDoc[])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 JavaDoc getStream(String JavaDoc name) {
230         return new Stream JavaDoc(name);
231     }
232
233     public URL JavaDoc getResource(String JavaDoc name) {
234         
235         if (mGZippedBytecode != null) {
236             if (mGZippedBytecode.containsKey(name)) {
237                 try {
238                     return new URL JavaDoc("file", null, -1, name, getURLFaker());
239                 }
240                 catch (Exception JavaDoc 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 JavaDoc getURLFaker() {
250         if (mFaker == null) {
251             mFaker = new URLFaker();
252         }
253         return mFaker;
254     }
255
256     protected Class JavaDoc loadClass(String JavaDoc name, boolean resolve)
257         throws ClassNotFoundException JavaDoc {
258
259         Class JavaDoc 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 JavaDoc(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 JavaDoc name, byte[] data) {
292         defineClass(name, data, 0, data.length);
293         if (mGZippedBytecode != null) {
294             try {
295                 ByteArrayOutputStream JavaDoc baos = new ByteArrayOutputStream JavaDoc();
296                 GZIPOutputStream JavaDoc gz = new GZIPOutputStream JavaDoc(baos);
297                 gz.write(data,0,data.length);
298                 gz.close();
299                 mGZippedBytecode.put(name.replace('.','/') + ".class",
300                                      baos.toByteArray());
301             }
302             catch (IOException JavaDoc ioe) {
303                 ioe.printStackTrace();
304             }
305         }
306     }
307     
308     private Class JavaDoc loadFromFile(String JavaDoc name) throws ClassNotFoundException JavaDoc {
309         if (mRootClassDirs == null) {
310             return null;
311         }
312
313         String JavaDoc 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 JavaDoc error = null;
326
327         for (int i=0; i<mRootClassDirs.length; i++) {
328             File JavaDoc file = new File JavaDoc(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 JavaDoc in = new FileInputStream JavaDoc(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 JavaDoc e) {
354                     if (error == null) {
355                         error = new ClassNotFoundException JavaDoc
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 JavaDoc {
371         private String JavaDoc mName;
372         
373         public Stream(String JavaDoc 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 JavaDoc {
389         
390         protected URLConnection JavaDoc openConnection(URL JavaDoc u) throws IOException JavaDoc {
391             return new ClassInjector.ResourceConnection(u);
392         }
393     }
394
395     private class ResourceConnection extends URLConnection JavaDoc {
396
397         String JavaDoc resourceName;
398         public ResourceConnection(URL JavaDoc 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 JavaDoc getInputStream() throws IOException JavaDoc {
407
408             try {
409                 if (mGZippedBytecode != null) {
410                                 
411                     if (mGZippedBytecode.get(resourceName) != null) {
412                         return new GZIPInputStream JavaDoc(new ByteArrayInputStream JavaDoc
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 JavaDoc e) {
424                 e.printStackTrace();
425             }
426      
427             return null;
428         }
429     }
430 }
431
Popular Tags