KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > jac > core > JacLoader


1 /*
2   Copyright (C) 2001-2003 Renaud Pawlak, Lionel Seinturier, Fabrice
3   Legond-Aubry.
4
5   This program is free software; you can redistribute it and/or modify
6   it under the terms of the GNU Lesser General Public License as
7   published by the Free Software Foundation; either version 2 of the
8   License, or (at your option) any later version.
9
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13   GNU Lesser General Public License for more details.
14
15   You should have received a copy of the GNU Lesser General Public
16   License along with this program; if not, write to the Free Software
17   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18   USA */

19
20 package org.objectweb.jac.core;
21
22 import java.io.File JavaDoc;
23 import java.io.FileInputStream JavaDoc;
24 import java.io.FileOutputStream JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.io.InputStream JavaDoc;
27 import java.io.InvalidClassException JavaDoc;
28 import java.io.ObjectInputStream JavaDoc;
29 import java.io.ObjectOutputStream JavaDoc;
30 import java.lang.reflect.Field JavaDoc;
31 import java.lang.reflect.Method JavaDoc;
32 import java.net.URL JavaDoc;
33 import java.net.URLClassLoader JavaDoc;
34 import java.security.MessageDigest JavaDoc;
35 import java.security.NoSuchAlgorithmException JavaDoc;
36 import java.util.Arrays JavaDoc;
37 import java.util.HashSet JavaDoc;
38 import java.util.Hashtable JavaDoc;
39 import java.util.Iterator JavaDoc;
40 import java.util.Properties JavaDoc;
41 import org.apache.log4j.Logger;
42 import org.objectweb.jac.core.rtti.ClassInfo;
43 import org.objectweb.jac.core.rtti.LoadtimeRTTI;
44 import org.objectweb.jac.util.ExtArrays;
45 import org.objectweb.jac.util.Streams;
46 import org.objectweb.jac.util.Strings;
47
48 /**
49  * The JAC specific class loader for JAC objects.
50  *
51  * <p>This loader does the following things :</p>
52  * <ul>
53  * <li>load a set of Properties from all the found "jac.prop" files
54  * <li>get from them the choosen wrappeeTranslator
55  * <li>instanciate the choosen wrappeTranslator
56  * <li>replace the system class loader.
57  * <li>then for each loaded class it can delegates the translation of
58  * JAC objects to the choosen <code>WrappeeTranslator</code>.
59  * </ul>
60  * @see org.objectweb.jac.core.WrappeeTranslator */

61 public class JacLoader extends ClassLoader JavaDoc {
62     static Logger logger = Logger.getLogger("loader");
63     static Logger loggerRes = Logger.getLogger("loader.resource");
64
65     /**
66      * Stores the bytecode of the loaded and translated classes.
67      *
68      * @see isLoaded(String)
69      * @see getLoadedBytecode(String) */

70     private Hashtable JavaDoc classes = new Hashtable JavaDoc();
71     
72     /** If true, caches the tranlated classes on disk. */
73     private boolean write = false;
74     
75     /** If true, clears the disk cache. */
76     private boolean clean = false;
77
78     private ClassLoader JavaDoc parentLoader = ClassLoader.getSystemClassLoader();
79
80     private WrappeeTranslator wt = null;
81     private String JavaDoc bytecodeModifier;
82     
83     /* Packages whose loading we delegate */
84     private String JavaDoc[] ignoredPackages = {
85         "java.", "javax.", "sun.", "org.xml.sax.", "org.w3c.dom.", "org.apache.log4j"
86     };
87
88     /* Classes whose loading we delegate. Those classes are thus shared
89        by all Jac loaders, and untranslated */

90     private HashSet JavaDoc ignoredClasses = new HashSet JavaDoc();
91
92
93     private static ClassLoader JavaDoc otherClassLoader;
94
95     LoadtimeRTTI rtti;
96
97     /**
98      * Create a JacLoader.
99      *
100      * @param write if true, caches the tranlated classes on disk
101      * @param clean if true, clears the disk cache
102      * @param otherClassLoader if not null, indicates another
103      * ClassLoader where to search for classes not in original
104      * ClassPath
105      */

106     public JacLoader(boolean write,
107                      boolean clean,
108                      ClassLoader JavaDoc otherClassLoader)
109         throws Exception JavaDoc
110     {
111         logger.info("building JacLoader...");
112         //super (ClassLoader.getSystemClassLoader());
113
this.write = write;
114         this.clean = clean;
115         if (otherClassLoader != null)
116             JacLoader.otherClassLoader = otherClassLoader;
117
118         ignoredClasses.add("org.objectweb.jac.util.Log");
119         ignoredClasses.add("org.objectweb.jac.core.Jac");
120         ignoredClasses.add("org.objectweb.jac.core.rtti.LoadtimeRTTI");
121         ignoredClasses.add("org.objectweb.jac.core.rtti.ClassInfo");
122         ignoredClasses.add("org.objectweb.jac.core.rtti.MethodInfo");
123         ignoredClasses.add("org.objectweb.jac.core.rtti.InvokeInfo");
124         // load the properties
125
JacPropLoader.loadProps();
126         logger.info("JacPropLoader = "+
127                   Strings.hex(JacPropLoader.class));
128             
129         // get the bytecodeModifier string
130
bytecodeModifier = org.objectweb.jac.core.JacPropLoader.bytecodeModifier;
131         logger.info("jacloader: Selected bytecode modifier = '" +
132                   bytecodeModifier +"'");
133       
134         // instanciate the bytecode translator
135
Class JavaDoc c = Class.forName("org.objectweb.jac.core.translators.WrappeeTranslator_"+
136                                 bytecodeModifier);
137         Class JavaDoc repositoryClass = loadClass("org.objectweb.jac.core.rtti.ClassRepository",true);
138         rtti = (LoadtimeRTTI)
139             repositoryClass.getMethod("get",ExtArrays.emptyClassArray).invoke(null,ExtArrays.emptyObjectArray);
140         wt = (WrappeeTranslator)c.getConstructor(
141             new Class JavaDoc[] {LoadtimeRTTI.class}).newInstance(
142                 new Object JavaDoc[] {rtti});
143       
144         logger.info("Instanciated bytecode modifier is "+wt);
145     }
146
147
148     /**
149      * Create a JacLoader. Same as
150      * <code>JacLoader(boolean,boolean,null)</code>.
151      *
152      * @param write if true, caches the tranlated classes on disk
153      * @param clean if true, clears the disk cache
154      */

155     public JacLoader(boolean write,
156                      boolean clean)
157         throws Exception JavaDoc
158     {
159         this(write, clean, null);
160     }
161
162
163     public void setWrappeeTranslator(WrappeeTranslator wt) {
164         this.wt = wt;
165     }
166
167     private void addIgnoredPkgs(String JavaDoc[] ignoredPackages) {
168         String JavaDoc[] new_p = new String JavaDoc[ignoredPackages.length +
169                                    this.ignoredPackages.length];
170       
171         System.arraycopy(this.ignoredPackages, 0, new_p, 0,
172                          this.ignoredPackages.length);
173         System.arraycopy(ignoredPackages, 0, new_p, this.ignoredPackages.length,
174                          ignoredPackages.length);
175         this.ignoredPackages = new_p;
176     }
177
178     /**
179      * Tells wether to defer loading of a class to the parent ClassLoader
180      * @param classname name of the class
181      * @return true if we should defer the loading of that class
182      */

183     public boolean deferClass(String JavaDoc classname) {
184         if (ignoredClasses.contains(classname))
185             return true;
186         for(int i=0; i<ignoredPackages.length; i++) {
187             if (classname.startsWith(ignoredPackages[i])) {
188                 return true;
189             }
190         }
191         return false;
192     }
193
194     /**
195      * Tells wether a defered class's bytecode should be analyzed
196      * @param classname the name of the class
197      */

198     protected boolean analyzeClass(String JavaDoc classname) {
199         return false;
200     }
201
202     protected Class JavaDoc loadClass(String JavaDoc class_name, boolean resolve)
203         throws ClassNotFoundException JavaDoc
204     {
205         logger.debug("loadClass("+class_name+","+resolve+")");
206         Class JavaDoc cl = null;
207
208         cl = findLoadedClass(class_name);
209         if (cl!=null) {
210             logger.debug("already loaded "+class_name);
211             return cl;
212         }
213         cl = (Class JavaDoc)classes.get(class_name);
214         if (cl==null) {
215             if (deferClass(class_name) && !JacPropLoader.adaptClass(class_name)) {
216                 logger.debug("defer "+class_name);
217                 byte[] bytes = null;
218                 if (analyzeClass(class_name)) {
219                     try {
220                         logger.debug("fill RTTI for "+class_name);
221                         bytes = wt.fillClassRTTI(class_name);
222                     } catch (Exception JavaDoc e) {
223                         logger.error("Failed to fill RTTI for "+class_name,e);
224                         // TODO: we should throw another exception
225
throw new ClassNotFoundException JavaDoc(
226                             "jacloader: cannot find class "+
227                             class_name+" on disk: "+e);
228                     }
229                 }
230                 cl = parentLoader.loadClass(class_name);
231             }
232
233             if (cl == null) {
234                 byte[] bytes;
235                 String JavaDoc resourcePath = "/" + class_name.replace('.', '/') + ".class";
236                 logger.debug("resourcePath = "+resourcePath);
237                 bytes = loadResource(resourcePath);
238
239                 if (wt!=null && classIsToBeAdapted(class_name)) {
240                     logger.info("adapting "+class_name);
241                     String JavaDoc baseName =
242                         Jac.getJacRoot()+"classes_"+bytecodeModifier+"/"+
243                         class_name.replace('.','/');
244                     File JavaDoc cacheFile = new File JavaDoc(baseName+".class");
245                     MessageDigest JavaDoc digest;
246                     boolean useCache = false;
247                     File JavaDoc md5File = new File JavaDoc(baseName+".md5");
248                     File JavaDoc rttiFile = new File JavaDoc(baseName+".rtti");
249                     byte[] orig_md5 = null;
250                     try {
251                         digest = MessageDigest.getInstance("MD5");
252                         digest.update(bytes);
253                         orig_md5 = digest.digest();
254                         if (md5File.exists()) {
255                             byte[] stored_md5 = loadFile(md5File);
256                             useCache = Arrays.equals(orig_md5,stored_md5);
257                         }
258                     } catch (NoSuchAlgorithmException JavaDoc e) {
259                         logger.warn("Could not get an MD5 digest: "+e);
260                     }
261                     boolean loaded = false;
262                     if (useCache && cacheFile.exists() && rttiFile.exists()) {
263                         logger.info("loading class from cache "+cacheFile);
264                         bytes = loadFile(cacheFile);
265                         try {
266                             FileInputStream JavaDoc rttiStream = new FileInputStream JavaDoc(rttiFile);
267                             ObjectInputStream JavaDoc objStream = new ObjectInputStream JavaDoc(rttiStream);
268                             ClassInfo classInfo = (ClassInfo)objStream.readObject();
269                             rttiStream.close();
270                             objStream.close();
271                             rtti.setClassInfo(class_name,classInfo);
272                             loaded = true;
273                         } catch (InvalidClassException JavaDoc e) {
274                             // ignored
275
logger.info("Rtti format must have changed: "+rttiFile);
276                         } catch (Exception JavaDoc e) {
277                             logger.error("Failed to read rtti cache file "+rttiFile+": "+e);
278                         }
279                     }
280                     if (!loaded) {
281                         try {
282                             //bytes = wt.translateClass(defineClass(class_name, bytes, 0, bytes.length));
283
bytes = wt.translateClass(class_name);
284                         } catch (Exception JavaDoc e) {
285                             logger.error("Failed to translate class "+class_name,e);
286                             throw new ClassNotFoundException JavaDoc(
287                                 "jacloader: cannot find class "+
288                                 class_name+" on disk: "+e);
289                         }
290                         if (bytes!=null && write) {
291                             logger.info("writing "+cacheFile);
292                             try {
293                                 cacheFile.getParentFile().mkdirs();
294                                 FileOutputStream JavaDoc out = new FileOutputStream JavaDoc(cacheFile);
295                                 try {
296                                     out.write(bytes);
297                                 } finally {
298                                     out.close();
299                                 }
300                             } catch (Exception JavaDoc e) {
301                                 logger.warn("Failed to write class file "+cacheFile+": "+e);
302                             }
303                             if (orig_md5!=null) {
304                                 logger.info("writing "+md5File);
305                                 try {
306                                     md5File.getParentFile().mkdirs();
307                                     FileOutputStream JavaDoc out = new FileOutputStream JavaDoc(md5File);
308                                     try {
309                                         out.write(orig_md5);
310                                     } finally {
311                                         out.close();
312                                     }
313                                 } catch (Exception JavaDoc e) {
314                                     logger.warn("Failed to write class file "+md5File+": "+e);
315                                 }
316                             }
317                             try {
318                                 FileOutputStream JavaDoc rttiStream = new FileOutputStream JavaDoc(rttiFile);
319                                 ObjectOutputStream JavaDoc objStream = new ObjectOutputStream JavaDoc(rttiStream);
320                                 objStream.writeObject(rtti.getClassInfo(class_name));
321                                 objStream.flush();
322                                 rttiStream.close();
323                                 objStream.close();
324                             } catch (Exception JavaDoc e) {
325                                 logger.error("Failed to write rtti cache file "+rttiFile+": "+e);
326                             }
327                         }
328                     }
329                 }
330                 logger.debug("defineClass "+class_name);
331                 if (bytes==null) {
332                     logger.debug("defer "+class_name);
333                     try {
334                         if (otherClassLoader!=null)
335                             cl = otherClassLoader.loadClass(class_name);
336                         else
337                             cl = parentLoader.loadClass(class_name);
338                     } catch(Exception JavaDoc e) {
339                         logger.error("Failed to load class "+class_name,e);
340                     }
341                 } else {
342                     try {
343                         cl = defineClass(class_name, bytes, 0, bytes.length);
344                     } catch(Exception JavaDoc e1) {
345                         logger.error("Failed to define class "+class_name,e1);
346                         // we are not authorized to load this class, we defer
347
logger.debug("defer "+class_name);
348                         bytes = null;
349                         if (analyzeClass(class_name))
350                             try {
351                                 logger.debug("fill RTTI for "+class_name);
352                                 bytes = wt.fillClassRTTI(class_name);
353                             } catch (Exception JavaDoc e) {
354                                 logger.error("Failed to fill RTTI for "+class_name,e);
355                                 throw new ClassNotFoundException JavaDoc(
356                                     "jacloader: cannot find class "+
357                                     class_name+" on disk: "+e);
358                             }
359                         cl = parentLoader.loadClass(class_name);
360                     }
361                     //cl = defineClass(class_name, bytes, 0, bytes.length);
362
}
363             }
364       
365             if (resolve)
366                 resolveClass(cl);
367         }
368
369         if (cl.getClassLoader()!=null)
370             logger.info(class_name+"@"+Integer.toHexString(cl.hashCode())+
371                         " ["+cl.getClassLoader()+"]");
372
373         logger.debug("----------");
374         classes.put(class_name, cl);
375
376         return cl;
377     }
378
379     public InputStream JavaDoc getResourceAsStream(String JavaDoc name)
380     {
381         loggerRes.debug("getResourceAsStream: "+name);
382
383         if ((name == null) || (name.length() == 0))
384             return null;
385
386         InputStream JavaDoc result = null;
387       
388         result = super.getResourceAsStream(name);
389         if ((result == null) && (otherClassLoader != null)) {
390             loggerRes.debug(" resource not found with system classloader, trying with: "+otherClassLoader);
391             result = otherClassLoader.getResourceAsStream(name);
392         }
393         return result;
394     }
395
396     public URL JavaDoc getResource(String JavaDoc name)
397     {
398         loggerRes.debug("getResource: "+name+" (parent="+getParent()+")");
399
400         URL JavaDoc result = null;
401
402         //result = super.getResource(name);
403
if (getParent() != null) {
404             result = getParent().getResource(name);
405         }
406         if (result == null) {
407             result = findResource(name);
408         }
409
410         // if not in usual CLASSPATH, search in additionnal CLASSPATH
411
if ((result == null) && (otherClassLoader != null)) {
412             loggerRes.debug(" resource not found with "+getParent()+", trying with: "+otherClassLoader);
413             if (otherClassLoader instanceof URLClassLoader JavaDoc)
414                 loggerRes.debug(" classpath="+Arrays.asList(((URLClassLoader JavaDoc)otherClassLoader).getURLs()));
415             result = otherClassLoader.getResource(name);
416         }
417
418         return result;
419     }
420
421     byte[] loadResource(String JavaDoc resourcePath) throws ClassNotFoundException JavaDoc {
422         InputStream JavaDoc in = null;
423
424         logger.debug( "loadResource: " + resourcePath);
425
426         in = super.getClass().getResourceAsStream(resourcePath);
427
428         // if not in usual CLASSPATH, search in additionnal CLASSPATH
429
if ((in == null) && (otherClassLoader != null))
430         {
431             logger.debug(
432                       "loadResource: searching in otherClassLoader ...");
433             in = otherClassLoader.getResourceAsStream(resourcePath.substring(1));
434         }
435
436         if (in == null)
437         {
438             throw new ClassNotFoundException JavaDoc("jacloader: Can not find resource "+resourcePath);
439         }
440         try {
441             return Streams.readStream(in);
442         } catch (IOException JavaDoc e) {
443             throw new ClassNotFoundException JavaDoc(
444                 "jacloader: failed to load resource "+resourcePath+" : "+e);
445         }
446       
447     }
448
449     byte[] loadFile(File JavaDoc file) throws ClassNotFoundException JavaDoc {
450         try {
451             InputStream JavaDoc in = new FileInputStream JavaDoc(file);
452             if (in == null)
453                 throw new ClassNotFoundException JavaDoc("jacloader: Can not find resource "+file);
454             return Streams.readStream(in);
455         } catch (IOException JavaDoc e) {
456             throw new ClassNotFoundException JavaDoc(
457                 "jacloader: failed to load resource "+file+" : "+e);
458         }
459       
460     }
461
462     /**
463      * Tell wether a class is to be adapted or not. All data
464      * are extracted from the jac.prop files located a various
465      * place.
466      *
467      *
468      * @see org.objectweb.jac.core.JacPropLoader
469      * @see org.objectweb.jac.core.JacPropTools
470      * @param name a <code>String</code> value
471      * @return a <code>boolean</code> value (true if to be adpated).
472      *
473      */

474     public static boolean classIsToBeAdapted(String JavaDoc name) {
475         // System.out.println("classIsToBeAdapted "+name+"? "+Strings.hex(JacPropLoader.class));
476
int index = name.lastIndexOf(".");
477         String JavaDoc packagename = "";
478         if (index!=-1)
479             packagename = name.substring(0,index);
480         Iterator JavaDoc it;
481         if (JacPropLoader.adaptClass(name))
482             return true;
483
484         // do not translate classes that follow
485
if ( name.endsWith(".Run") ||
486              name.endsWith(".Main") ||
487              name.endsWith("AC") ||
488              name.endsWith("Wrapper") ||
489              (name.indexOf('$') != -1) ||
490              name.endsWith("Exception") ||
491              name.startsWith("org.objectweb.jac.core") ||
492              name.startsWith("org.objectweb.jac.util") ||
493              name.startsWith("org.objectweb.jac.aspects"))
494             return false;
495       
496         if (JacPropLoader.doNotAdaptClass(name))
497             return false;
498
499         return true;
500     }
501
502     /**
503      * display some information about a class<br>
504      * Only use for debug purpose to verify that the class
505      * has been patched correctly.
506      *
507      * @see java.lang.ClassLoader
508      * @param name the class to display
509      *
510      */

511     public static void displayClassInfo(String JavaDoc name)
512     {
513         System.out.println ("jacloader: Displaying data on class " + name);
514         Class JavaDoc cl = null;
515         try {
516             cl = Class.forName (name);
517         }
518         catch (Exception JavaDoc e)
519         {
520             System.out.println ("\tClass is not available from the loader.");
521             return;
522         }
523         Field JavaDoc[] fl = cl.getDeclaredFields();
524         for (int i=0; i<fl.length; i++)
525         {
526             System.out.println ("\tField "+i+" : name("+
527                                 fl[i].getName()+"), type("+
528                                 fl[i].getType().getName()
529                                 +"), attr("+fl[i].getModifiers()+")");
530         }
531         Method JavaDoc[] fm = cl.getDeclaredMethods();
532         for (int i=0; i<fm.length; i++)
533         {
534             System.out.println ("\tMethod "+i+" ("+
535                                 fm[i].getName()+"), return type is "+
536                                 fm[i].getReturnType().getName());
537             Class JavaDoc[] clexcep = fm[i].getExceptionTypes();
538             for (int j=0; j<clexcep.length;j++)
539                 System.out.println ("\t\tThrow Exception : "+clexcep[j].getName());
540             Class JavaDoc[] clpar = fm[i].getParameterTypes();
541             for (int j=0; j<clpar.length;j++)
542                 System.out.println ("\t\tParameter number "+j+" is a "+clpar[j].getName());
543         }
544     }
545    
546
547     /**
548      * Returns true if the class is already loaded.
549      *
550      * @param classname the class name to test
551      * @return true if already loaded
552      */

553     public boolean isLoaded (String JavaDoc classname) {
554         return (classes.containsKey(classname));
555     }
556     
557     /**
558      * Loads a class and calls <code>main()</code> in that class.
559      *
560      * <p>This method was extracted "as is" from javassist 1.0 from
561      * Shigeru Chiba.
562      *
563      * <p><a HREF="www.csg.is.titech.ac.jp/~chiba/javassist/">Javassist
564      * Homepage</a>
565      *
566      * @param classname the loaded class.
567      * @param args parameters passed to <code>main()</code>. */

568     
569     public void run(String JavaDoc classname, String JavaDoc[] args) throws Throwable JavaDoc {
570         Class JavaDoc c = this.loadClass(classname);
571         ClassLoader JavaDoc saved = Thread.currentThread().getContextClassLoader();
572         try {
573             Thread.currentThread().setContextClassLoader(this);
574             c.getDeclaredMethod("main", new Class JavaDoc[] { String JavaDoc[].class })
575                 .invoke(null, new Object JavaDoc[] { args });
576         }
577         catch (java.lang.reflect.InvocationTargetException JavaDoc e) {
578             throw e.getTargetException();
579         }
580         finally {
581             Thread.currentThread().setContextClassLoader(saved);
582         }
583     }
584
585     /**
586      * Use JacPropLoader to read some JAC properties
587      * @param props the properties to read
588      */

589     public void readProperties(Properties JavaDoc props) {
590         JacPropLoader.addProps(props);
591     }
592
593     /**
594      * Usage: java org.objectweb.jac.core.JacLoader [class]
595      */

596     public static void main(String JavaDoc[] args) throws Exception JavaDoc {
597         JacLoader loader = new JacLoader(true,true, otherClassLoader);
598         for (int i=0; i<args.length; i++) {
599             JacPropLoader.packagesToAdapt.add(args[i]);
600             loader.loadClass(args[i]);
601         }
602     }
603 }
604
Popular Tags