001: /*
002: * Copyright 2001-2007 Patrick Lightbody and
003: * Geert Bevin <gbevin[remove] at uwyn dot com>
004: * Distributed under the terms of either:
005: * - the common development and distribution license (CDDL), v1.0; or
006: * - the GNU Lesser General Public License, v2.1 or later
007: * $Id: BasicContinuableClassLoader.java 3862 2007-07-13 14:37:11Z gbevin $
008: */
009: package com.uwyn.rife.continuations.basic;
010:
011: import java.lang.reflect.Method;
012:
013: import com.tc.object.loaders.NamedClassLoader;
014: import com.uwyn.rife.continuations.ContinuationConfigInstrument;
015: import com.uwyn.rife.continuations.instrument.ContinuableDetector;
016: import com.uwyn.rife.continuations.instrument.ContinuationsAgent;
017: import com.uwyn.rife.continuations.instrument.ContinuationsBytecodeTransformer;
018: import com.uwyn.rife.instrument.ClassBytesProvider;
019: import com.uwyn.rife.tools.ClassBytesLoader;
020: import com.uwyn.rife.tools.TerracottaUtils;
021:
022: /**
023: * Classloader implementation that will transform bytecode for classes that
024: * should receive the continuations functionalities.
025: * <p>Note that this is a basic classloader implementation. For your own
026: * application you should probably create your own or at least read over
027: * the source code of this one. It's even better to not use a custom
028: * classloader and only rely on the {@link ContinuationsAgent}.
029: *
030: * @author Geert Bevin (gbevin[remove] at uwyn dot com)
031: * @version $Revision: 3862 $
032: * @since 1.6
033: */
034: public class BasicContinuableClassLoader extends ClassLoader implements
035: ClassBytesProvider {
036: private ContinuationConfigInstrument mConfig;
037: private ClassBytesLoader mBytesLoader;
038: private ContinuableDetector mContinuableDetector;
039: private Method mMethodFindloadedclass;
040:
041: public String __tc_getClassLoaderName() {
042: return "RIFE:BasicContinuableClassLoader";
043: }
044:
045: /**
046: * Creates a new classloader instance with the context classloader as
047: * the parent classloader.
048: *
049: * @param config the instance of the instrumentation configuration that
050: * will be used for the transformation
051: * @since 1.6
052: */
053: public BasicContinuableClassLoader(
054: ContinuationConfigInstrument config) {
055: this (Thread.currentThread().getContextClassLoader(), config);
056:
057: if (TerracottaUtils.isTcPresent()) {
058: try {
059: Class classprocessor_helper_class = Class
060: .forName("com.tc.object.bytecode.hook.impl.ClassProcessorHelper");
061: Class namedclassloader_class = Class
062: .forName("com.tc.object.loaders.NamedClassLoader");
063: Method method = classprocessor_helper_class
064: .getDeclaredMethod("registerGlobalLoader",
065: new Class[] { namedclassloader_class });
066: method.invoke(null, new Object[] { this });
067: } catch (Exception e) {
068: throw new RuntimeException(
069: "Unable to register the engine classloader '"
070: + __tc_getClassLoaderName()
071: + "' with Terracotta.", e);
072: }
073: }
074: }
075:
076: /**
077: * Creates a new classloader instance.
078: *
079: * @param parent the parent classloader
080: * @param config the instance of the instrumentation configuration that
081: * will be used for the transformation
082: * @since 1.6
083: */
084: public BasicContinuableClassLoader(ClassLoader parent,
085: ContinuationConfigInstrument config) {
086: super (parent);
087:
088: mConfig = config;
089: mBytesLoader = new ClassBytesLoader(getParent());
090: mContinuableDetector = new ContinuableDetector(mConfig, this );
091: try {
092: mMethodFindloadedclass = ClassLoader.class
093: .getDeclaredMethod("findLoadedClass",
094: new Class[] { String.class });
095: } catch (Exception e) {
096: throw new RuntimeException(e);
097: }
098: mMethodFindloadedclass.setAccessible(true);
099: }
100:
101: public byte[] getClassBytes(String className,
102: boolean reloadAutomatically) throws ClassNotFoundException {
103: return mBytesLoader.getClassBytes(className.replace('.', '/')
104: + ".class");
105: }
106:
107: public Class loadClass(String name) throws ClassNotFoundException {
108: // disable this classloader and delegate to the parent if the continuations
109: // agent is active
110: if (Boolean
111: .getBoolean(ContinuationsAgent.AGENT_ACTIVE_PROPERTY)) {
112: return getParent().loadClass(name);
113: }
114:
115: // check if the class wasn't already loaded by the parent classloader and in
116: // that case, don't instrument it, yes I know this is ugly and a hack
117: Class parentclassloader_class = null;
118: try {
119: parentclassloader_class = (Class) mMethodFindloadedclass
120: .invoke(getParent(), new Object[] { name });
121: if (parentclassloader_class != null) {
122: return parentclassloader_class;
123: }
124: } catch (Exception e) {
125: throw new ClassNotFoundException(name, e);
126: }
127:
128: // if the class couldn't be obtained from the parent classloader and it
129: // wasn't previously loaded by this one, perform the instrumentation
130: synchronized (name.intern()) {
131: if (null == findLoadedClass(name)) {
132: byte[] bytes = getClassBytes(name, false);
133: if (bytes == null) {
134: return super .loadClass(name);
135: }
136:
137: if (mContinuableDetector.detect(bytes, false)) {
138: byte[] resume_bytes = ContinuationsBytecodeTransformer
139: .transformIntoResumableBytes(mConfig,
140: bytes, name);
141:
142: if (resume_bytes != null) {
143: bytes = resume_bytes;
144: }
145:
146: return defineClass(name, bytes, 0, bytes.length);
147: }
148: }
149: }
150:
151: return super.loadClass(name);
152: }
153: }
|