DZone Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world
Java Mixins With Code Generation
// Ruby style mixin in Java using dynamic code generation
import net.sf.cglib.core.*;
import net.sf.cglib.asm.ClassVisitor;
import net.sf.cglib.asm.Type;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.*;
public class MixinTest
{
/*
* The Demo
*/
public interface Chimera extends Lion, Goat, Serpent, Animal
{
}
public static void main(String[] args)
{
Chimera chimera = (Chimera) MixinFactory.create(Chimera.class, new AnimalImpl());
System.out.println("The Chimera: ");
chimera.roar();
chimera.stomp();
chimera.slither();
chimera.action("Sing a song");
System.out.println("Reflection Mixin");
timeMixin();
timeMixin();
timeMixin();
System.out.println("CGLib Mixin");
timeCGMixin();
timeCGMixin();
timeCGMixin();
System.out.println("Plain method call");
timeNormal();
timeNormal();
timeNormal();
}
private static void timeMixin()
{
Chimera chimera = (Chimera) MixinFactory.create(Chimera.class, new AnimalImpl());
int sampleSize = 1000000;
long start = System.currentTimeMillis();
for (int i = 0; i < sampleSize; i++)
{
chimera.roar();
}
long total = System.currentTimeMillis() - start;
System.out.println("Total = " + total);
}
private static void timeCGMixin()
{
Chimera chimera = (Chimera) CGMixinFactory.create(Chimera.class, new AnimalImpl());
int sampleSize = 1000000;
long start = System.currentTimeMillis();
for (int i = 0; i < sampleSize; i++)
{
chimera.roar();
}
long total = System.currentTimeMillis() - start;
System.out.println("Total = " + total);
}
private static void timeNormal()
{
int sampleSize = 1000000;
Lion lion = new LionImpl(new AnimalImpl());
long start = System.currentTimeMillis();
for (int i = 0; i < sampleSize; i++)
{
lion.roar();
}
long total = System.currentTimeMillis() - start;
System.out.println("Total = " + total);
}
/*
* The Interfaces
*/
public interface Lion
{
void roar();
}
public interface Goat
{
void stomp();
}
public interface Serpent
{
void slither();
}
public interface Animal
{
void action(String description);
}
/*
* The Implementations
*/
public static class LionImpl implements Lion
{
private Animal animal;
public LionImpl(Animal animal)
{
this.animal = animal;
}
public void roar()
{
animal.action("Roars with lion head");
}
}
public static class GoatImpl implements Goat
{
private Animal animal;
public GoatImpl(Animal animal)
{
this.animal = animal;
}
public void stomp()
{
animal.action("Stomps with goat foot");
}
}
public static class SerpentImpl implements Serpent
{
private Animal animal;
public SerpentImpl(Animal animal)
{
this.animal = animal;
}
public void slither()
{
animal.action("Slithers with serpent tail");
}
}
public static class AnimalImpl implements Animal
{
String last;
public void action(String description)
{
last = description;
//System.out.println(description);
}
}
public static class CGMixinFactory
{
public static Object create(Class interfaceClass, Object core)
{
CGGenerator generator = new CGGenerator(interfaceClass, delegates(interfaceClass, core).toArray());
return generator.create();
}
private static Collection delegates(Class interfaceClass, Object core)
{
Collection delegates = new HashSet();
for (int i = 0; i < interfaceClass.getInterfaces().length; i++)
{
Class anInterface = interfaceClass.getInterfaces()[i];
try
{
Class delegateClass = Class.forName(anInterface.getName() + "Impl");
Constructor delegateConstructor = findBestMatchConstrustor(delegateClass, core.getClass());
if (delegateConstructor != null)
{
delegates.add(delegateConstructor.newInstance(new Object[]{core}));
}
else
{
delegates.add(core);
}
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
return delegates;
}
private static Constructor findBestMatchConstrustor(Class delegateClass, Class coreClass)
{
if (coreClass == null)
{
return null;
}
try
{
return delegateClass.getConstructor(new Class[]{coreClass});
}
catch (NoSuchMethodException e)
{
Class[] interfaces = coreClass.getInterfaces();
for (int i = 0; interfaces != null && i < interfaces.length; i++)
{
Class interfaceClass = interfaces[i];
Constructor delegateConstructor = findBestMatchConstrustor(delegateClass, interfaceClass);
if (delegateConstructor != null)
{
return delegateConstructor;
}
}
Constructor delegateConstructor = findBestMatchConstrustor(delegateClass, coreClass.getSuperclass());
if (delegateConstructor != null)
{
return delegateConstructor;
}
}
return null;
}
}
public static class CGGenerator extends AbstractClassGenerator
{
private static final MixinKey KEY_FACTORY =
(MixinKey)KeyFactory.create(MixinKey.class, KeyFactory.CLASS_BY_NAME);
interface MixinKey
{
public Object newInstance(Class classes, int[] route);
}
private static final Source SOURCE = new Source(CGMixinFactory.class.getName());
private Class interfaceClass;
private Object[] delegates;
private int route[];
private Method methods[];
public CGGenerator(Class interfaceClass, Object delegates[])
{
super(SOURCE);
this.interfaceClass = interfaceClass;
this.delegates = delegates;
methods = interfaceClass.getMethods();
calculateRoute();
}
private void calculateRoute()
{
route = new int[methods.length];
Map delegateIndexes = calculateDelegateIndexes();
for (int i = 0; i < methods.length; i++)
{
route[i] = ((Integer)delegateIndexes.get(methods[i].getDeclaringClass().getName())).intValue();
}
}
private Map calculateDelegateIndexes()
{
Map delegateIndexes = new HashMap();
for (int i = 0; i < delegates.length; i++)
{
delegateIndexes.put(interfaceNameForImpl(delegates[i]), new Integer(i));
}
return delegateIndexes;
}
private String interfaceNameForImpl(Object delegate)
{
return delegate.getClass().getName().replaceAll("Impl", "");
}
public Object create()
{
return super.create(KEY_FACTORY.newInstance(interfaceClass, route));
}
protected ClassLoader getDefaultClassLoader()
{
return interfaceClass.getClassLoader();
}
protected Object firstInstance(Class type) throws Exception
{
return ReflectUtils.newInstance(type, new Class[] {Object[].class}, new Object[] {delegates});
}
protected Object nextInstance(Object instance) throws Exception
{
return firstInstance(instance.getClass());
}
public void generateClass(ClassVisitor classVisitor) throws Exception
{
new CGMixinEmitter(classVisitor, interfaceClass, getClassName(), methods, route);
}
}
public static class CGMixinEmitter extends ClassEmitter
{
private static final String FIELD_NAME = "CGLIB$DELEGATES";
private static final Signature CSTRUCT_OBJECT_ARRAY = TypeUtils.parseConstructor("Object[]");
public CGMixinEmitter(ClassVisitor classVisitor, Class interfaceClass, String className, Method methods[], int route[])
{
super(classVisitor);
begin_class(Constants.V1_3,
Constants.ACC_PUBLIC,
className,
getMixinSuperType(),
TypeUtils.getTypes(new Class[]{interfaceClass}),
Constants.SOURCE_FILE);
declare_field(Constants.ACC_PRIVATE, FIELD_NAME, Constants.TYPE_OBJECT_ARRAY, null, null);
CodeEmitter e = begin_method(Constants.ACC_PUBLIC, CSTRUCT_OBJECT_ARRAY, null, null);
e.load_this();
e.super_invoke_constructor();
e.load_this();
e.load_arg(0);
e.putfield(FIELD_NAME);
e.return_value();
e.end_method();
for (int j = 0; j < methods.length; j++)
{
MethodInfo method = ReflectUtils.getMethodInfo(methods[j]);
e = EmitUtils.begin_method(this, method, Constants.ACC_PUBLIC);
e.load_this();
e.getfield(FIELD_NAME);
e.aaload(route[j]);
e.checkcast(method.getClassInfo().getType());
e.load_args();
e.invoke(method);
e.return_value();
e.end_method();
}
end_class();
}
private Type getMixinSuperType()
{
return TypeUtils.getTypes(new Class[]{Object.class})[0];
}
}
/*
* The Guts
*/
public static class MixinFactory
{
private static class MixinInvocationHandler implements InvocationHandler
{
private Map implMap = new HashMap();
private Map methodCache = new HashMap();
public MixinInvocationHandler(Class clazz, Object core)
{
for (int i = 0; i < clazz.getInterfaces().length; i++)
{
Class anInterface = clazz.getInterfaces()[i];
try
{
Class delegateClass = Class.forName(anInterface.getName() + "Impl");
Constructor delegateConstructor = findBestMatchConstrustor(delegateClass, core.getClass());
if (delegateConstructor != null)
{
implMap.put(anInterface, delegateConstructor.newInstance(new Object[]{core}));
}
else
{
implMap.put(anInterface, core);
}
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
Object delegate = implMap.get(method.getDeclaringClass());
return getImplMethod(delegate.getClass(), method).invoke(delegate, args);
}
private Method getImplMethod(Class implClass, Method method) throws NoSuchMethodException
{
if (methodCache.containsKey(method))
{
return (Method) methodCache.get(method);
}
else
{
Method declaredMethod = implClass.getDeclaredMethod(method.getName(), method.getParameterTypes());
methodCache.put(method, declaredMethod);
return declaredMethod;
}
}
private static Constructor findBestMatchConstrustor(Class delegateClass, Class coreClass)
{
if (coreClass == null)
{
return null;
}
try
{
return delegateClass.getConstructor(new Class[]{coreClass});
}
catch (NoSuchMethodException e)
{
Class[] interfaces = coreClass.getInterfaces();
for (int i = 0; interfaces != null && i < interfaces.length; i++)
{
Class interfaceClass = interfaces[i];
Constructor delegateConstructor = findBestMatchConstrustor(delegateClass, interfaceClass);
if (delegateConstructor != null)
{
return delegateConstructor;
}
}
Constructor delegateConstructor = findBestMatchConstrustor(delegateClass, coreClass.getSuperclass());
if (delegateConstructor != null)
{
return delegateConstructor;
}
}
return null;
}
}
public static Object create(Class clazz, Object core)
{
return Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz},
new MixinInvocationHandler(clazz, core));
}
}
}





