package org.hotswap.agent.plugin.proxy.hscglib; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; import org.hotswap.agent.plugin.proxy.ParentLastClassLoader; import org.hotswap.agent.plugin.proxy.ProxyBytecodeGenerator; import org.hotswap.agent.util.ReflectionHelper; /** * Creates new bytecode for a Cglib Enhancer proxy. Uses Classes loaded with a new instance of a ParentLastClassLoader. * * @author Erki Ehtla * */ public class CglibEnhancerProxyBytecodeGenerator implements ProxyBytecodeGenerator { private GeneratorParams param; private ClassLoader classLoader; private Class<?> generatorClass; private Object generator; private Class<?> abstractGeneratorClass; /** * * @param param * Parameters of the previous bytecode generation call * @param classLoader * Enhancer classloader */ public CglibEnhancerProxyBytecodeGenerator(GeneratorParams param, ClassLoader classLoader) { this.param = param; this.classLoader = new ParentLastClassLoader(classLoader); this.generator = param.getParam(); this.generatorClass = generator.getClass(); this.abstractGeneratorClass = generatorClass.getSuperclass(); } private static class FieldState { public FieldState(Field field, Object fieldValue) { this.field = field; this.fieldValue = fieldValue; } private Field field; private Object fieldValue; } /** * Generates bytecode for the proxy class * * @return bytecode of the new proxy class * @throws Exception */ @Override public byte[] generate() throws Exception { Collection<FieldState> oldClassValues = getFieldValuesWithClasses(); ClassLoader oldClassLoader = (ClassLoader) ReflectionHelper.get(generator, "classLoader"); Boolean oldUseCache = (Boolean) ReflectionHelper.get(generator, "useCache"); try { ReflectionHelper.set(generator, abstractGeneratorClass, "classLoader", classLoader); ReflectionHelper.set(generator, abstractGeneratorClass, "useCache", Boolean.FALSE); setFieldValuesWithNewLoadedClasses(oldClassValues); byte[] invoke = (byte[]) ReflectionHelper.invoke(param.getGenerator(), param.getGenerator().getClass(), "generate", new Class[] { getGeneratorInterfaceClass() }, generator); return invoke; } finally { ReflectionHelper.set(generator, abstractGeneratorClass, "classLoader", oldClassLoader); ReflectionHelper.set(generator, abstractGeneratorClass, "useCache", oldUseCache); setFieldValues(oldClassValues); } } /** * * @return ClassGenerator interface Class instance */ private Class<?> getGeneratorInterfaceClass() { Class<?>[] interfaces = abstractGeneratorClass.getInterfaces(); for (Class<?> iClass : interfaces) { if (iClass.getName().endsWith(".ClassGenerator")) return iClass; } return null; } private void setFieldValues(Collection<FieldState> fieldStates) throws IllegalAccessException { for (FieldState fieldState : fieldStates) { fieldState.field.set(generator, fieldState.fieldValue); } } /** * replaces fields with Class values with new classes loaded by a ParentLastClassLoader * * @param fieldStates * @throws IllegalAccessException * @throws ClassNotFoundException */ private void setFieldValuesWithNewLoadedClasses(Collection<FieldState> fieldStates) throws IllegalAccessException, ClassNotFoundException { for (FieldState fieldState : fieldStates) { fieldState.field.set(generator, loadFromClassloader(fieldState.fieldValue)); } } private Collection<FieldState> getFieldValuesWithClasses() throws IllegalAccessException { Collection<FieldState> classValueFields = new ArrayList<FieldState>(); Field[] fields = generatorClass.getDeclaredFields(); for (Field field : fields) { if (!Modifier.isStatic(field.getModifiers()) && (field.getType().isInstance(Class.class) || field.getType().isInstance(Class[].class))) { field.setAccessible(true); classValueFields.add(new FieldState(field, field.get(generator))); } } return classValueFields; } /** * Load classes from ParentLastClassLoader * * @param fieldState * @return * @throws ClassNotFoundException */ private Object loadFromClassloader(Object fieldState) throws ClassNotFoundException { if (fieldState instanceof Class[]) { Class<?>[] classes = ((Class[]) fieldState); Class<?>[] newClasses = new Class[classes.length]; for (int i = 0; i < classes.length; i++) { Class<?> loadClass = classLoader.loadClass(classes[i].getName()); newClasses[i] = loadClass; } return newClasses; } else { return classLoader.loadClass(((Class<?>) fieldState).getName()); } } }