KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > easybeans > loader > EasyBeansClassLoader


1 /**
2  * EasyBeans
3  * Copyright (C) 2006 Bull S.A.S.
4  * Contact: easybeans@objectweb.org
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19  * USA
20  *
21  * --------------------------------------------------------------------------
22  * $Id: EasyBeansClassLoader.java 534 2006-05-29 13:46:27Z benoitf $
23  * --------------------------------------------------------------------------
24  */

25
26 package org.objectweb.easybeans.loader;
27
28 import java.io.File JavaDoc;
29 import java.io.FileOutputStream JavaDoc;
30 import java.io.IOException JavaDoc;
31 import java.lang.reflect.InvocationTargetException JavaDoc;
32 import java.lang.reflect.Method JavaDoc;
33 import java.net.URL JavaDoc;
34 import java.net.URLClassLoader JavaDoc;
35 import java.util.HashMap JavaDoc;
36 import java.util.Map JavaDoc;
37
38 import org.objectweb.easybeans.log.JLog;
39 import org.objectweb.easybeans.log.JLogFactory;
40
41 /**
42  * This class defines the EasyBeans classloader. This classloader allows to set
43  * the bytecode for a given class. Then, when the class will be loaded, it will
44  * define the class by using the associated bytecode.
45  * @author Florent Benoit
46  */

47 public class EasyBeansClassLoader extends URLClassLoader JavaDoc implements Cloneable JavaDoc {
48
49     /**
50      * Logger.
51      */

52     private static JLog logger = JLogFactory.getLog(EasyBeansClassLoader.class);
53
54     /**
55      * Need to recompute toString() value ? (urls have changed)
56      * True by default (not done).
57      * Then, compute is done only when required and if needed
58      */

59     private boolean recomputeToString = true;
60
61     /**
62      * String representation used by toString() method.
63      */

64     private String JavaDoc toStringValue = null;
65
66     /**
67      * java.lang.ClassLoader class.
68      */

69     private Class JavaDoc javaLangClassLoaderClass = null;
70
71     /**
72      * Method of the classloader allowing to define some classes.
73      */

74     private Method JavaDoc defineClassMethod = null;
75
76     /**
77      * Map between class name and the bytecode associated to the given
78      * classname.
79      */

80     private Map JavaDoc<String JavaDoc, byte[]> mapDefined = new HashMap JavaDoc<String JavaDoc, byte[]>();
81
82     /**
83      * Use the same constructors as parent class.
84      * @param urls the URLs from which to load classes and resources
85      * @param parent the parent class loader for delegation
86      */

87     public EasyBeansClassLoader(final URL JavaDoc[] urls, final ClassLoader JavaDoc parent) {
88         super(urls, parent);
89     }
90
91     /**
92      * Use the same constructors as parent class.
93      * @param urls the URLs from which to load classes and resources
94      */

95     public EasyBeansClassLoader(final URL JavaDoc[] urls) {
96         super(urls);
97     }
98
99     /**
100      * Finds and loads the class with the specified name from the URL search
101      * path. If this classloader has the bytecode for the associated class, it
102      * defines the class.
103      * @param name the name of the class
104      * @return the resulting class
105      * @exception ClassNotFoundException if the class could not be found
106      */

107     @Override JavaDoc
108     protected Class JavaDoc<?> findClass(final String JavaDoc name) throws ClassNotFoundException JavaDoc {
109         Class JavaDoc clazz = searchingDefinedClass(name);
110         if (clazz != null) {
111             return clazz;
112         }
113         // else, delegates to super class
114
return super.findClass(name);
115     }
116
117     /**
118      * Defines the class by using the bytecode of the given classname.
119      * @param className the name of the class.
120      * @param bytecode the bytes of the given class.
121      * @return the class which is now defined
122      */

123     private Class JavaDoc<?> defineInternalClass(final String JavaDoc className, final byte[] bytecode) {
124         // TODO: remove this ?
125
// if (logger.isDebugEnabled()) {
126
FileOutputStream JavaDoc fos = null;
127             try {
128                 String JavaDoc fName = System.getProperty("java.io.tmpdir") + File.separator + className + ".class";
129                 fos = new FileOutputStream JavaDoc(fName);
130                 fos.write(bytecode);
131             } catch (IOException JavaDoc ioe) {
132                 throw new RuntimeException JavaDoc(ioe);
133             }
134         //}
135

136         // override classDefine (as it is protected) and define the class.
137
ClassLoader JavaDoc loader = this;
138         if (javaLangClassLoaderClass == null) {
139             try {
140                 javaLangClassLoaderClass = Class.forName("java.lang.ClassLoader");
141             } catch (ClassNotFoundException JavaDoc e) {
142                 logger.error("Cannot use the ClassLoader class", e);
143                 return null;
144             }
145         }
146         if (defineClassMethod == null) {
147             try {
148                 defineClassMethod = javaLangClassLoaderClass.getDeclaredMethod("defineClass", new Class JavaDoc[] {
149                         String JavaDoc.class, byte[].class, int.class, int.class});
150             } catch (SecurityException JavaDoc e) {
151                 logger.error("Method defineClass not available in the classloader class", e);
152                 return null;
153             } catch (NoSuchMethodException JavaDoc e) {
154                 logger.error("Method defineClass not available in the classloader class", e);
155                 return null;
156             }
157         }
158         // protected method invocaton
159
defineClassMethod.setAccessible(true);
160         try {
161             Object JavaDoc[] args = new Object JavaDoc[] {className, bytecode, new Integer JavaDoc(0), new Integer JavaDoc(bytecode.length)};
162             try {
163                 return (Class JavaDoc) defineClassMethod.invoke(loader, args);
164             } catch (IllegalArgumentException JavaDoc e) {
165                 logger.error("Cannot invoke the defineClass method on the classloader", e);
166             } catch (IllegalAccessException JavaDoc e) {
167                 logger.error("Cannot invoke the defineClass method on the classloader", e);
168             } catch (InvocationTargetException JavaDoc e) {
169                 logger.error("Cannot invoke the defineClass method on the classloader", e);
170             }
171         } finally {
172             defineClassMethod.setAccessible(false);
173         }
174         return null;
175     }
176
177     /**
178      * Adds the bytecode for a given class. It will be used when class will be
179      * loaded.
180      * @param className the name of the class.
181      * @param bytecode the bytes of the given class.
182      */

183     public void addClassDefinition(final String JavaDoc className, final byte[] bytecode) {
184         // check override ?
185
if (mapDefined.get(className) != null) {
186             logger.warn("There is already a bytecode defined for the class named '" + className
187                     + "'. Not replacing. This could be due to a duplicated class in the given package.");
188         }
189         mapDefined.put(className, bytecode);
190     }
191
192     /**
193      * When trying to load a class, look if this class needs to be defined.
194      * @param name the class name to load.
195      * @return the class if it was found.
196      * @throws ClassNotFoundException if the class is not found.
197      */

198     @Override JavaDoc
199     public Class JavaDoc<?> loadClass(final String JavaDoc name) throws ClassNotFoundException JavaDoc {
200         searchingDefinedClass(name);
201         return super.loadClass(name, false);
202     }
203
204     /**
205      * Search a class in the local repository of classes to define.
206      * @param className the name of the class to search and define if found.
207      * @return the class if it was defined and loaded.
208      */

209     private Class JavaDoc searchingDefinedClass(final String JavaDoc className) {
210         // Defines the class if the bytecode is here.
211
if (mapDefined != null) {
212             byte[] defined = mapDefined.get(className);
213             if (defined != null) {
214                 Class JavaDoc clazz = defineInternalClass(className, defined);
215                 if (clazz != null) {
216                     // remove defined class.
217
mapDefined.remove(className);
218                     return clazz;
219                 }
220
221             }
222         }
223         return null;
224     }
225
226
227     /**
228      * Displays useful information.
229      * @return information
230      */

231     @Override JavaDoc
232     public String JavaDoc toString() {
233         // urls have changed, need to build value
234
if (recomputeToString) {
235             computeToString();
236         }
237         return toStringValue;
238     }
239
240     /**
241      * Compute a string representation used by toString() method.
242      */

243     private void computeToString() {
244         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
245         sb.append(this.getClass().getName());
246         sb.append("[");
247         sb.append("urls=");
248         URL JavaDoc[] urls = getURLs();
249         for (int u = 0; u < urls.length; u++) {
250             sb.append(urls[u]);
251             if (u != urls.length - 1) {
252                 sb.append(";");
253             }
254         }
255         sb.append("]");
256         toStringValue = sb.toString();
257
258         // value is updated, no need to do it again.
259
recomputeToString = false;
260    }
261
262     /**
263      * Creates and returns a copy of this object.
264      * It is used for example when Persistence Provider needs a new Temp classloader to load some temporary classes.
265      * @return a copy of this object
266      */

267     @Override JavaDoc
268     public Object JavaDoc clone() {
269         return new EasyBeansClassLoader(getURLs(), this.getParent());
270     }
271 }
272
Popular Tags