/*
* Copyright 2001-2008 Patrick Lightbody and
* Geert Bevin <gbevin[remove] at uwyn dot com>
* Licensed under the Apache License, Version 2.0 (the "License")
* $Id: BasicContinuableClassLoader.java 3918 2008-04-14 17:35:35Z gbevin $
*/
package com.uwyn.rife.continuations.basic;
import java.lang.reflect.Method;
import com.uwyn.rife.continuations.ContinuationConfigInstrument;
import com.uwyn.rife.continuations.instrument.ContinuableDetector;
import com.uwyn.rife.continuations.instrument.ContinuationsAgent;
import com.uwyn.rife.continuations.instrument.ContinuationsBytecodeTransformer;
import com.uwyn.rife.instrument.ClassBytesProvider;
import com.uwyn.rife.tools.ClassBytesLoader;
import com.uwyn.rife.tools.TerracottaUtils;
/**
* Classloader implementation that will transform bytecode for classes that
* should receive the continuations functionalities.
* <p>Note that this is a basic classloader implementation. For your own
* application you should probably create your own or at least read over
* the source code of this one. It's even better to not use a custom
* classloader and only rely on the {@link ContinuationsAgent}.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @version $Revision: 3918 $
* @since 1.6
*/
public class BasicContinuableClassLoader extends ClassLoader implements ClassBytesProvider
{
private ContinuationConfigInstrument mConfig;
private ClassBytesLoader mBytesLoader;
private ContinuableDetector mContinuableDetector;
private Method mMethodFindloadedclass;
public String __tc_getClassLoaderName() {
return "RIFE:BasicContinuableClassLoader";
}
/**
* Creates a new classloader instance with the context classloader as
* the parent classloader.
*
* @param config the instance of the instrumentation configuration that
* will be used for the transformation
* @since 1.6
*/
public BasicContinuableClassLoader(ContinuationConfigInstrument config)
{
this(Thread.currentThread().getContextClassLoader(), config);
if (TerracottaUtils.isTcPresent()) {
try {
Class classprocessor_helper_class = Class.forName("com.tc.object.bytecode.hook.impl.ClassProcessorHelper");
Class namedclassloader_class = Class.forName("com.tc.object.loaders.NamedClassLoader");
Method method = classprocessor_helper_class.getDeclaredMethod("registerGlobalLoader", new Class[] {namedclassloader_class});
method.invoke(null, new Object[] {this});
} catch (Exception e) {
throw new RuntimeException("Unable to register the engine classloader '"+__tc_getClassLoaderName()+"' with Terracotta.", e);
}
}
}
/**
* Creates a new classloader instance.
*
* @param parent the parent classloader
* @param config the instance of the instrumentation configuration that
* will be used for the transformation
* @since 1.6
*/
public BasicContinuableClassLoader(ClassLoader parent, ContinuationConfigInstrument config)
{
super(parent);
mConfig = config;
mBytesLoader = new ClassBytesLoader(getParent());
mContinuableDetector = new ContinuableDetector(mConfig, this);
try
{
mMethodFindloadedclass = ClassLoader.class.getDeclaredMethod("findLoadedClass", new Class[] {String.class});
}
catch (Exception e)
{
throw new RuntimeException(e);
}
mMethodFindloadedclass.setAccessible(true);
}
public byte[] getClassBytes(String className, boolean reloadAutomatically) throws ClassNotFoundException
{
return mBytesLoader.getClassBytes(className.replace('.', '/') + ".class");
}
public Class loadClass(String name) throws ClassNotFoundException
{
// disable this classloader and delegate to the parent if the continuations
// agent is active
if (Boolean.getBoolean(ContinuationsAgent.AGENT_ACTIVE_PROPERTY))
{
return getParent().loadClass(name);
}
// check if the class wasn't already loaded by the parent classloader and in
// that case, don't instrument it, yes I know this is ugly and a hack
Class parentclassloader_class = null;
try
{
parentclassloader_class = (Class)mMethodFindloadedclass.invoke(getParent(), new Object[] {name});
if (parentclassloader_class != null)
{
return parentclassloader_class;
}
}
catch (Exception e)
{
throw new ClassNotFoundException(name, e);
}
// if the class couldn't be obtained from the parent classloader and it
// wasn't previously loaded by this one, perform the instrumentation
synchronized (name.intern())
{
if (null == findLoadedClass(name))
{
byte[] bytes = getClassBytes(name, false);
if (bytes == null)
{
return super.loadClass(name);
}
if (mContinuableDetector.detect(bytes, false))
{
byte[] resume_bytes = ContinuationsBytecodeTransformer.transformIntoResumableBytes(mConfig, bytes, name);
if (resume_bytes != null)
{
bytes = resume_bytes;
}
return defineClass(name, bytes, 0, bytes.length);
}
}
}
return super.loadClass(name);
}
}