KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > tc > object > bytecode > hook > impl > ClassProcessorHelper


1 /*
2  * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright notice. All rights reserved.
3  */

4 package com.tc.object.bytecode.hook.impl;
5
6 import com.tc.aspectwerkz.transform.TransformationConstants;
7 import com.tc.net.NIOWorkarounds;
8 import com.tc.object.bytecode.Manager;
9 import com.tc.object.bytecode.ManagerUtil;
10 import com.tc.object.bytecode.hook.ClassLoaderPreProcessorImpl;
11 import com.tc.object.bytecode.hook.ClassPostProcessor;
12 import com.tc.object.bytecode.hook.ClassPreProcessor;
13 import com.tc.object.bytecode.hook.DSOContext;
14 import com.tc.object.loaders.ClassProvider;
15 import com.tc.object.loaders.NamedClassLoader;
16 import com.tc.object.loaders.StandardClassProvider;
17 import com.tc.text.Banner;
18
19 import java.io.File JavaDoc;
20 import java.io.FileFilter JavaDoc;
21 import java.io.InputStream JavaDoc;
22 import java.lang.reflect.InvocationTargetException JavaDoc;
23 import java.lang.reflect.Method JavaDoc;
24 import java.net.MalformedURLException JavaDoc;
25 import java.net.URL JavaDoc;
26 import java.net.URLClassLoader JavaDoc;
27 import java.security.ProtectionDomain JavaDoc;
28 import java.util.ArrayList JavaDoc;
29 import java.util.Map JavaDoc;
30 import java.util.WeakHashMap JavaDoc;
31
32 /**
33  * Helper class called by the modified version of java.lang.ClassLoader
34  */

35 public class ClassProcessorHelper {
36
37   // Directory where Terracotta jars (and dependencies) can be found
38
private static final String JavaDoc TC_INSTALL_ROOT_SYSPROP = "tc.install-root";
39
40   // NOTE: This is not intended to be a public/documented system property,
41
// it is for dev use only. It is NOT for QA or customer use
42
private static final String JavaDoc TC_CLASSPATH_SYSPROP = "tc.classpath";
43
44   private static final boolean GLOBAL_MODE_DEFAULT = true;
45
46   public static final boolean USE_GLOBAL_CONTEXT;
47
48   private static final State initState = new State();
49
50   private static final ThreadLocal JavaDoc inStaticInitializer = new ThreadLocal JavaDoc();
51
52   private static final String JavaDoc tcInstallRootSysProp = System.getProperty(TC_INSTALL_ROOT_SYSPROP);
53
54   // This map should only hold a weak reference to the loader (key).
55
// If we didn't we'd prevent loaders from being GC'd
56
private static final Map JavaDoc contextMap = new WeakHashMap JavaDoc();
57
58   private static final StandardClassProvider globalProvider = new StandardClassProvider();
59
60   private static final URL JavaDoc[] tcClassPath;
61
62   private static URLClassLoader JavaDoc tcLoader;
63   private static DSOContext gloalContext;
64
65   static {
66     inStaticInitializer.set(new Object JavaDoc());
67
68     try {
69       String JavaDoc global = System.getProperty("tc.dso.globalmode", null);
70       if (global != null) {
71         USE_GLOBAL_CONTEXT = Boolean.valueOf(global).booleanValue();
72       } else {
73         USE_GLOBAL_CONTEXT = GLOBAL_MODE_DEFAULT;
74       }
75
76       // This avoids a deadlock (see LKC-853, LKC-1387)
77
java.security.Security.getProviders();
78
79       // Workaround bug in NIO on solaris 10
80
NIOWorkarounds.solaris10Workaround();
81
82       tcClassPath = buildTerracottaClassPath();
83     } catch (Throwable JavaDoc t) {
84       Util.exit(t);
85       throw new AssertionError JavaDoc(); // this has to be here to make the compiler happy
86
} finally {
87       inStaticInitializer.set(null);
88     }
89   }
90
91   private static URLClassLoader JavaDoc createTCLoader() {
92     return new URLClassLoader JavaDoc(tcClassPath, null);
93   }
94
95   public static URL JavaDoc getTCResource(String JavaDoc name, ClassLoader JavaDoc cl) {
96     if (!isAWRuntimeDependency(name.replace('/', '.'))) { return null; }
97
98     try {
99       URL JavaDoc u = tcLoader.findResource(name); // getResource() would cause an endless loop
100
// System.err.println("### ClassProcessorHelper.getTCResource() "+name+" "+u);
101
return u;
102     } catch (Exception JavaDoc e) {
103       // System.err.println("!!! ClassProcessorHelper.getTCResource() "+name+"; "+e.toString());
104
return null;
105     }
106   }
107
108   public static byte[] getTCClass(String JavaDoc name, ClassLoader JavaDoc cl) throws ClassNotFoundException JavaDoc {
109     if (!isAWRuntimeDependency(name)) { return null; }
110
111     // System.err.println("### ClassProcessorHelper.getTCClass() "+name+" "+(cl==null ? "null" :
112
// cl.getClass().getName()));
113

114     URL JavaDoc url = tcLoader.findResource(name.replace('.', '/') + ".class"); // getResource() would cause an endless loop
115
if (url == null) return null;
116
117     InputStream JavaDoc is = null;
118     try {
119       is = url.openStream();
120       byte[] b = new byte[is.available()];
121       int len = 0;
122       int n;
123       while ((n = is.read(b, len, b.length - len)) > 0) {
124         len += n;
125         if (len < b.length) {
126           byte[] c = new byte[b.length + 1000];
127           System.arraycopy(b, 0, c, 0, len);
128           b = c;
129         }
130       }
131       if (len == b.length) { return b; }
132       byte[] c = new byte[len];
133       System.arraycopy(b, 0, c, 0, len);
134       return c;
135
136     } catch (Exception JavaDoc e) {
137       throw new ClassNotFoundException JavaDoc("Unable to load " + url.toString() + "; " + e.toString(), e);
138
139     } finally {
140       try {
141         is.close();
142       } catch (Exception JavaDoc ex) {
143         // ignore
144
}
145
146     }
147   }
148
149   private static boolean isAWRuntimeDependency(String JavaDoc name) {
150     return name.startsWith("com.tcspring.");
151     // || name.startsWith("com.tc.aspectwerkz.definition.deployer.AspectModule")
152
// || name.equals("com.tc.aspectwerkz.aspect.AspectContainer")
153
// || name.equals("com.tc.aspectwerkz.aspect.AbstractAspectContainer");
154
}
155
156   private static void handleError(Throwable JavaDoc t) {
157     if (t instanceof InvocationTargetException JavaDoc) {
158       t = ((InvocationTargetException JavaDoc) t).getTargetException();
159     }
160
161     if (t instanceof RuntimeException JavaDoc) { throw (RuntimeException JavaDoc) t; }
162     if (t instanceof Error JavaDoc) { throw (Error JavaDoc) t; }
163
164     throw new RuntimeException JavaDoc(t);
165   }
166
167   static File JavaDoc getTCInstallDir(boolean systemClassPathAllowed) {
168     if (tcInstallRootSysProp == null) {
169       if (systemClassPathAllowed) {
170         try {
171           ClassLoader.getSystemClassLoader().loadClass("com.tc.object.NotInBootJar");
172           return null;
173         } catch (ClassNotFoundException JavaDoc cnfe) {
174           // ignore
175
}
176       }
177
178       Banner.errorBanner("Terracotta home directory is not set. Please set it with -D" + TC_INSTALL_ROOT_SYSPROP
179                          + "=<path-to-Terracotta-install>");
180       Util.exit();
181     }
182
183     File JavaDoc tcInstallDir = new File JavaDoc(tcInstallRootSysProp);
184
185     if (!tcInstallDir.exists() || !tcInstallDir.isDirectory() || !tcInstallDir.canRead()) {
186       Banner.errorBanner("Terracotta install directory [" + tcInstallDir.getAbsolutePath()
187                          + "] is not accessible. This value is set via system property " + TC_INSTALL_ROOT_SYSPROP);
188       Util.exit();
189     }
190
191     return tcInstallDir;
192   }
193
194   private static URL JavaDoc[] buildTerracottaClassPath() throws MalformedURLException JavaDoc {
195     if (System.getProperty(TC_CLASSPATH_SYSPROP) != null) { return buildDevClassPath(); }
196
197     File JavaDoc tcHomeDir = getTCInstallDir(true);
198     if (tcHomeDir == null) {
199       URL JavaDoc[] systemURLS = ((URLClassLoader JavaDoc) ClassLoader.getSystemClassLoader()).getURLs();
200       return (URL JavaDoc[]) systemURLS.clone();
201     }
202
203     File JavaDoc tcLib = new File JavaDoc(tcHomeDir, "lib");
204     if (!tcLib.exists() || !tcLib.isDirectory() || !tcLib.canRead()) {
205       Banner.errorBanner("Terracotta lib directory [" + tcLib.getAbsolutePath()
206                          + "] is not accessible. This value is based off of the system property "
207                          + TC_INSTALL_ROOT_SYSPROP);
208       Util.exit();
209     }
210
211     File JavaDoc[] entries = tcLib.listFiles(new JarFilter());
212
213     if (entries.length == 0) {
214       Banner.errorBanner("Absolutely no .jar files found in Terracotta common lib directory ["
215                          + tcLib.getAbsolutePath() + "]. Please check the value of your " + TC_INSTALL_ROOT_SYSPROP
216                          + " system property");
217       Util.exit();
218     }
219
220     URL JavaDoc[] rv = new URL JavaDoc[entries.length];
221     for (int i = 0; i < entries.length; i++) {
222       String JavaDoc jar = entries[i].getAbsolutePath().replace(File.separatorChar, '/');
223       rv[i] = new URL JavaDoc("file", "", jar);
224     }
225
226     return rv;
227   }
228
229   private static URL JavaDoc[] buildDevClassPath() throws MalformedURLException JavaDoc {
230     // For development use only. This is handy since you can put the eclipse/ant build output directories
231
// here and not bother creating a tc.jar every time you change some source
232
String JavaDoc[] parts = System.getProperty(TC_CLASSPATH_SYSPROP).split(File.pathSeparator);
233     ArrayList JavaDoc urls = new ArrayList JavaDoc();
234
235     for (int i = 0; i < parts.length; i++) {
236       String JavaDoc part = parts[i];
237       if (part.length() > 0) {
238
239         File JavaDoc file = new File JavaDoc(part);
240         part = file.getAbsolutePath().replace(File.separatorChar, '/');
241
242         if (!part.startsWith("/")) {
243           part = "/" + part;
244         }
245
246         if (file.isDirectory()) {
247           if (!part.endsWith("/")) {
248             part = part + "/";
249           }
250         }
251
252         urls.add(new URL JavaDoc("file", "", part));
253       }
254     }
255
256     return (URL JavaDoc[]) urls.toArray(new URL JavaDoc[urls.size()]);
257   }
258
259   private static Method JavaDoc getContextMethod(String JavaDoc name, Class JavaDoc[] args) throws ClassNotFoundException JavaDoc,
260       NoSuchMethodException JavaDoc {
261     Class JavaDoc c = tcLoader.loadClass("com.tc.object.bytecode.hook.impl.DSOContextImpl");
262     return c.getDeclaredMethod(name, args);
263   }
264
265   private static void init() {
266     if (initState.attemptInit()) {
267       try {
268         tcLoader = createTCLoader();
269
270         // do this before doing anything with the TC loader
271
initTCLogging();
272
273         if (USE_GLOBAL_CONTEXT) {
274           gloalContext = createGlobalContext();
275         }
276
277         initState.initialized();
278       } catch (Throwable JavaDoc t) {
279         t.printStackTrace();
280         handleError(t);
281         throw new AssertionError JavaDoc(); // shouldn't get here
282
}
283     }
284   }
285
286   private static void initTCLogging() throws ClassNotFoundException JavaDoc, NoSuchMethodException JavaDoc, IllegalAccessException JavaDoc,
287       InvocationTargetException JavaDoc {
288     // This code is here because users can set various Log4J properties that will, for example, cause Log4J to try
289
// to use arbitrary classes as appenders. If users try to use one of their own classes as an appender, we'll try
290
// to load it in our classloader and fail in fairly unpleasant ways.
291
//
292
// Yes, saving and restoring a system property really sucks, but there isn't really a better way to do it. Users
293
// can request that Log4J read config from an arbitrary URL otherwise, and there's no way to intercept that at
294
// all. As a result, this seems like a better solution.
295
//
296
// See LKC-1974 for more details.
297

298     String JavaDoc oldDefaultInitOverrideValue = null;
299
300     try {
301       oldDefaultInitOverrideValue = System.setProperty("log4j.defaultInitOverride", "true");
302       Class JavaDoc loggerClass = tcLoader.loadClass("org.apache.log4j.Logger");
303       Method JavaDoc theMethod = loggerClass.getDeclaredMethod("getRootLogger", new Class JavaDoc[0]);
304       theMethod.invoke(null, (Object JavaDoc[]) null);
305     } finally {
306       if (oldDefaultInitOverrideValue == null) {
307         System.getProperties().remove("log4j.defaultInitOverride");
308       } else {
309         System.setProperty("log4j.defaultInitOverride", oldDefaultInitOverrideValue);
310       }
311     }
312   }
313
314   public static void registerGlobalLoader(NamedClassLoader loader) {
315     if (!USE_GLOBAL_CONTEXT) { throw new IllegalStateException JavaDoc("Not global DSO mode"); }
316     globalProvider.registerNamedLoader(loader);
317   }
318
319   public static void shutdown() {
320     if (!USE_GLOBAL_CONTEXT) { throw new IllegalStateException JavaDoc("Not global DSO mode"); }
321     try {
322       if (gloalContext != null) {
323         gloalContext.getManager().stop();
324       }
325     } catch (Throwable JavaDoc t) {
326       t.printStackTrace();
327     }
328   }
329
330   public static boolean isDSOSessions(String JavaDoc appName) {
331     appName = ("/".equals(appName)) ? "ROOT" : appName;
332     init();
333     try {
334       Method JavaDoc m = getContextMethod("isDSOSessions", new Class JavaDoc[] { String JavaDoc.class });
335       boolean rv = ((Boolean JavaDoc) m.invoke(null, new Object JavaDoc[] { appName })).booleanValue();
336       return rv;
337     } catch (Throwable JavaDoc t) {
338       handleError(t);
339       throw new AssertionError JavaDoc(); // shouldn't get here
340
}
341   }
342
343   // used by test framework only
344
public static void setContext(ClassLoader JavaDoc loader, DSOContext context) {
345     if (USE_GLOBAL_CONTEXT) { throw new IllegalStateException JavaDoc("DSO Context is global in this VM"); }
346
347     if ((loader == null) || (context == null)) {
348       // bad dog
349
throw new IllegalArgumentException JavaDoc("Loader and/or context may not be null");
350     }
351
352     synchronized (contextMap) {
353       contextMap.put(loader, context);
354     }
355   }
356
357   // used by test framework only
358
public static Manager getManager(ClassLoader JavaDoc caller) {
359     if (USE_GLOBAL_CONTEXT) { return gloalContext.getManager(); }
360
361     DSOContext context;
362     synchronized (contextMap) {
363       context = (DSOContext) contextMap.get(caller);
364     }
365     if (context == null) { return null; }
366     return context.getManager();
367   }
368
369   public static DSOContext getContext(ClassLoader JavaDoc cl) {
370     if (USE_GLOBAL_CONTEXT) return gloalContext;
371
372     synchronized (contextMap) {
373       return (DSOContext) contextMap.get(cl);
374     }
375   }
376
377   private static DSOContext createGlobalContext() {
378     try {
379       Method JavaDoc m = getContextMethod("createGlobalContext", new Class JavaDoc[] { ClassProvider.class });
380       DSOContext context = (DSOContext) m.invoke(null, new Object JavaDoc[] { globalProvider });
381       context.getManager().init();
382       return context;
383     } catch (Throwable JavaDoc t) {
384       t.printStackTrace();
385       System.exit(-1);
386       throw new AssertionError JavaDoc(); // shouldn't get here
387
}
388   }
389
390   /**
391    * byte code instrumentation of class loaded <br>
392    * XXX::NOTE:: Donot optimize to return same input byte array if the class is instrumented (I cant imagine why we
393    * would). ClassLoader checks the returned byte array to see if the class is instrumented or not to maintain the
394    * offset.
395    *
396    * @see ClassLoaderPreProcessorImpl
397    */

398   public static byte[] defineClass0Pre(ClassLoader JavaDoc caller, String JavaDoc name, byte[] b, int off, int len, ProtectionDomain JavaDoc pd) {
399     if (inStaticInitializer()) { return b; }
400     if (skipClass(caller)) { return b; }
401
402     // needed for JRockit
403
name = (name != null) ? name.replace('/', '.') : null;
404
405     if (isAWDependency(name)) { return b; }
406     if (isDSODependency(name)) { return b; }
407
408     init();
409     if (!initState.isInitialized()) { return b; }
410
411     ManagerUtil.enable();
412
413     ClassPreProcessor preProcessor = getPreProcessor(caller);
414     if (preProcessor == null) { return b; }
415
416     return preProcessor.preProcess(name, b, off, len, caller);
417   }
418
419   private static boolean skipClass(ClassLoader JavaDoc caller) {
420     return (caller == tcLoader);
421   }
422
423   public static void defineClass0Post(Class JavaDoc clazz, ClassLoader JavaDoc caller) {
424     if (inStaticInitializer()) { return; }
425
426     ClassPostProcessor postProcessor = getPostProcessor(caller);
427     if (!initState.isInitialized()) { return; }
428
429     if (skipClass(caller)) { return; }
430
431     if (postProcessor == null) { return; }
432
433     postProcessor.postProcess(clazz, caller);
434   }
435
436   private static boolean inStaticInitializer() {
437     return inStaticInitializer.get() != null;
438   }
439
440   public static Manager getGlobalManager() {
441     return gloalContext.getManager();
442   }
443
444   private static ClassPreProcessor getPreProcessor(ClassLoader JavaDoc caller) {
445     if (USE_GLOBAL_CONTEXT) { return gloalContext; }
446
447     synchronized (contextMap) {
448       return (ClassPreProcessor) contextMap.get(caller);
449     }
450   }
451
452   private static ClassPostProcessor getPostProcessor(ClassLoader JavaDoc caller) {
453     if (USE_GLOBAL_CONTEXT) { return gloalContext; }
454
455     synchronized (contextMap) {
456       return (ClassPostProcessor) contextMap.get(caller);
457     }
458   }
459
460   public static boolean isAWDependency(final String JavaDoc className) {
461     return (className == null)
462            || className.endsWith("_AWFactory")// TODO AVF refactor
463
|| className.endsWith(TransformationConstants.JOIN_POINT_CLASS_SUFFIX)
464            || className.startsWith("com.tc.aspectwerkz.") || className.startsWith("com.tc.asm.")
465            || className.startsWith("com.tc.jrexx.") || className.startsWith("org.dom4j.")
466            || className.startsWith("org.xml.sax.") || className.startsWith("javax.xml.parsers.")
467            || className.startsWith("sun.reflect.Generated"); // issue on J2SE 5 reflection - AW-245
468
}
469
470   public static boolean isDSODependency(final String JavaDoc className) {
471     return false;
472     // return (className == null) || className.startsWith("DO_NOT_USE.") || className.startsWith("com.tc.")
473
// || className.startsWith("org.w3c.dom.") || className.startsWith("org.apache.log4j.")
474
// || className.startsWith("org.apache.commons.io.") || className.startsWith("org.apache.commons.lang.")
475
// || className.startsWith("org.apache.commons.logging.") || className.startsWith("javax.xml.")
476
// || className.startsWith("org.apache.xmlbeans.") || className.startsWith("org.apache.xerces.");
477
}
478   
479   public static int getSessionLockType(String JavaDoc appName) {
480     return gloalContext.getSessionLockType(appName);
481   }
482
483   public static class JarFilter implements FileFilter JavaDoc {
484     public boolean accept(File JavaDoc pathname) {
485       return pathname.isFile() && pathname.getAbsolutePath().toLowerCase().endsWith(".jar");
486     }
487   }
488
489   public static class State {
490     private final int NOT_INTIALIZED = 0;
491     private final int INITIALIZING = 1;
492     private final int INITIALIZED = 2;
493     private int state = NOT_INTIALIZED;
494
495     public synchronized boolean attemptInit() {
496       if (state == NOT_INTIALIZED) {
497         state = INITIALIZING;
498         return true;
499       }
500       return false;
501     }
502
503     public synchronized void initialized() {
504       if (state != INITIALIZING) { throw new IllegalStateException JavaDoc("State was " + state); }
505       state = INITIALIZED;
506     }
507
508     public synchronized boolean isInitialized() {
509       return state == INITIALIZED;
510     }
511   }
512
513 }
514
Popular Tags