1 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 ; 20 import java.io.FileFilter ; 21 import java.io.InputStream ; 22 import java.lang.reflect.InvocationTargetException ; 23 import java.lang.reflect.Method ; 24 import java.net.MalformedURLException ; 25 import java.net.URL ; 26 import java.net.URLClassLoader ; 27 import java.security.ProtectionDomain ; 28 import java.util.ArrayList ; 29 import java.util.Map ; 30 import java.util.WeakHashMap ; 31 32 35 public class ClassProcessorHelper { 36 37 private static final String TC_INSTALL_ROOT_SYSPROP = "tc.install-root"; 39 40 private static final String 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 inStaticInitializer = new ThreadLocal (); 51 52 private static final String tcInstallRootSysProp = System.getProperty(TC_INSTALL_ROOT_SYSPROP); 53 54 private static final Map contextMap = new WeakHashMap (); 57 58 private static final StandardClassProvider globalProvider = new StandardClassProvider(); 59 60 private static final URL [] tcClassPath; 61 62 private static URLClassLoader tcLoader; 63 private static DSOContext gloalContext; 64 65 static { 66 inStaticInitializer.set(new Object ()); 67 68 try { 69 String 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 java.security.Security.getProviders(); 78 79 NIOWorkarounds.solaris10Workaround(); 81 82 tcClassPath = buildTerracottaClassPath(); 83 } catch (Throwable t) { 84 Util.exit(t); 85 throw new AssertionError (); } finally { 87 inStaticInitializer.set(null); 88 } 89 } 90 91 private static URLClassLoader createTCLoader() { 92 return new URLClassLoader (tcClassPath, null); 93 } 94 95 public static URL getTCResource(String name, ClassLoader cl) { 96 if (!isAWRuntimeDependency(name.replace('/', '.'))) { return null; } 97 98 try { 99 URL u = tcLoader.findResource(name); return u; 102 } catch (Exception e) { 103 return null; 105 } 106 } 107 108 public static byte[] getTCClass(String name, ClassLoader cl) throws ClassNotFoundException { 109 if (!isAWRuntimeDependency(name)) { return null; } 110 111 114 URL url = tcLoader.findResource(name.replace('.', '/') + ".class"); if (url == null) return null; 116 117 InputStream 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 e) { 137 throw new ClassNotFoundException ("Unable to load " + url.toString() + "; " + e.toString(), e); 138 139 } finally { 140 try { 141 is.close(); 142 } catch (Exception ex) { 143 } 145 146 } 147 } 148 149 private static boolean isAWRuntimeDependency(String name) { 150 return name.startsWith("com.tcspring."); 151 } 155 156 private static void handleError(Throwable t) { 157 if (t instanceof InvocationTargetException ) { 158 t = ((InvocationTargetException ) t).getTargetException(); 159 } 160 161 if (t instanceof RuntimeException ) { throw (RuntimeException ) t; } 162 if (t instanceof Error ) { throw (Error ) t; } 163 164 throw new RuntimeException (t); 165 } 166 167 static File 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 cnfe) { 174 } 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 tcInstallDir = new File (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 [] buildTerracottaClassPath() throws MalformedURLException { 195 if (System.getProperty(TC_CLASSPATH_SYSPROP) != null) { return buildDevClassPath(); } 196 197 File tcHomeDir = getTCInstallDir(true); 198 if (tcHomeDir == null) { 199 URL [] systemURLS = ((URLClassLoader ) ClassLoader.getSystemClassLoader()).getURLs(); 200 return (URL []) systemURLS.clone(); 201 } 202 203 File tcLib = new File (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 [] 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 [] rv = new URL [entries.length]; 221 for (int i = 0; i < entries.length; i++) { 222 String jar = entries[i].getAbsolutePath().replace(File.separatorChar, '/'); 223 rv[i] = new URL ("file", "", jar); 224 } 225 226 return rv; 227 } 228 229 private static URL [] buildDevClassPath() throws MalformedURLException { 230 String [] parts = System.getProperty(TC_CLASSPATH_SYSPROP).split(File.pathSeparator); 233 ArrayList urls = new ArrayList (); 234 235 for (int i = 0; i < parts.length; i++) { 236 String part = parts[i]; 237 if (part.length() > 0) { 238 239 File file = new File (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 ("file", "", part)); 253 } 254 } 255 256 return (URL []) urls.toArray(new URL [urls.size()]); 257 } 258 259 private static Method getContextMethod(String name, Class [] args) throws ClassNotFoundException , 260 NoSuchMethodException { 261 Class 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 initTCLogging(); 272 273 if (USE_GLOBAL_CONTEXT) { 274 gloalContext = createGlobalContext(); 275 } 276 277 initState.initialized(); 278 } catch (Throwable t) { 279 t.printStackTrace(); 280 handleError(t); 281 throw new AssertionError (); } 283 } 284 } 285 286 private static void initTCLogging() throws ClassNotFoundException , NoSuchMethodException , IllegalAccessException , 287 InvocationTargetException { 288 298 String oldDefaultInitOverrideValue = null; 299 300 try { 301 oldDefaultInitOverrideValue = System.setProperty("log4j.defaultInitOverride", "true"); 302 Class loggerClass = tcLoader.loadClass("org.apache.log4j.Logger"); 303 Method theMethod = loggerClass.getDeclaredMethod("getRootLogger", new Class [0]); 304 theMethod.invoke(null, (Object []) 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 ("Not global DSO mode"); } 316 globalProvider.registerNamedLoader(loader); 317 } 318 319 public static void shutdown() { 320 if (!USE_GLOBAL_CONTEXT) { throw new IllegalStateException ("Not global DSO mode"); } 321 try { 322 if (gloalContext != null) { 323 gloalContext.getManager().stop(); 324 } 325 } catch (Throwable t) { 326 t.printStackTrace(); 327 } 328 } 329 330 public static boolean isDSOSessions(String appName) { 331 appName = ("/".equals(appName)) ? "ROOT" : appName; 332 init(); 333 try { 334 Method m = getContextMethod("isDSOSessions", new Class [] { String .class }); 335 boolean rv = ((Boolean ) m.invoke(null, new Object [] { appName })).booleanValue(); 336 return rv; 337 } catch (Throwable t) { 338 handleError(t); 339 throw new AssertionError (); } 341 } 342 343 public static void setContext(ClassLoader loader, DSOContext context) { 345 if (USE_GLOBAL_CONTEXT) { throw new IllegalStateException ("DSO Context is global in this VM"); } 346 347 if ((loader == null) || (context == null)) { 348 throw new IllegalArgumentException ("Loader and/or context may not be null"); 350 } 351 352 synchronized (contextMap) { 353 contextMap.put(loader, context); 354 } 355 } 356 357 public static Manager getManager(ClassLoader 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 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 m = getContextMethod("createGlobalContext", new Class [] { ClassProvider.class }); 380 DSOContext context = (DSOContext) m.invoke(null, new Object [] { globalProvider }); 381 context.getManager().init(); 382 return context; 383 } catch (Throwable t) { 384 t.printStackTrace(); 385 System.exit(-1); 386 throw new AssertionError (); } 388 } 389 390 398 public static byte[] defineClass0Pre(ClassLoader caller, String name, byte[] b, int off, int len, ProtectionDomain pd) { 399 if (inStaticInitializer()) { return b; } 400 if (skipClass(caller)) { return b; } 401 402 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 caller) { 420 return (caller == tcLoader); 421 } 422 423 public static void defineClass0Post(Class clazz, ClassLoader 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 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 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 className) { 461 return (className == null) 462 || className.endsWith("_AWFactory") || 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"); } 469 470 public static boolean isDSODependency(final String className) { 471 return false; 472 } 478 479 public static int getSessionLockType(String appName) { 480 return gloalContext.getSessionLockType(appName); 481 } 482 483 public static class JarFilter implements FileFilter { 484 public boolean accept(File 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 ("State was " + state); } 505 state = INITIALIZED; 506 } 507 508 public synchronized boolean isInitialized() { 509 return state == INITIALIZED; 510 } 511 } 512 513 } 514 | Popular Tags |