package org.jboss.proxy.compiler;
import java.lang.reflect.Method;
import org.apache.bcel.Constants;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.generic.ClassGen;
import org.apache.bcel.generic.BasicType;
import org.apache.bcel.generic.Type;
import org.jboss.logging.Logger;
public class ProxyCompiler
{
private static final Logger log = Logger.getLogger(ProxyCompiler.class);
public final static String CLASS_DUMP_PATH =
System.getProperty(ProxyCompiler.class.getName() + ".dumpPath", null);
public final static String IMPL_SUFFIX = "$Proxy";
Runtime runtime;
Class superclass;
Class targetTypes[];
Method methods[];
Class proxyType;
public ProxyCompiler(final ClassLoader parent,
final Class superclass,
final Class targetTypes[],
final Method methods[])
throws Exception
{
this.superclass = superclass;
this.targetTypes = targetTypes;
this.methods = methods;
this.runtime = new Runtime(parent);
this.runtime.targetTypes = targetTypes;
this.runtime.methods = methods;
runtime.makeProxyType(this);
}
public Class getProxyType() {
return proxyType;
}
public String getProxyClassName() {
return targetTypes[0].getName() + IMPL_SUFFIX;
}
public byte[] getCode()
{
boolean trace = log.isTraceEnabled();
final String proxyClassName = getProxyClassName();
final String superClassName = superclass.getName();
int icount = 1; for (int i = 0; i < targetTypes.length; i++) {
Class targetType = targetTypes[i];
if (targetType.isInterface()) {
icount++;
}
}
String interfaceNames[] = new String[icount];
interfaceNames[0] = Proxies.ProxyTarget.class.getName();
icount = 1;
for (int i = 0; i < targetTypes.length; i++) {
Class targetType = targetTypes[i];
if (targetType.isInterface()) {
interfaceNames[icount++] = targetType.getName();
}
else if (!superclass.isAssignableFrom(targetType)) {
throw new RuntimeException("unexpected: " + targetType);
}
}
ClassGen cg = new ClassGen(proxyClassName,
superClassName,
"<generated>",
Constants.ACC_PUBLIC | Constants.ACC_FINAL,
interfaceNames);
ProxyImplementationFactory factory =
new ProxyImplementationFactory(superClassName, proxyClassName, cg);
cg.addField(factory.createInvocationHandlerField());
cg.addField(factory.createRuntimeField());
cg.addMethod(factory.createConstructor());
cg.addMethod(factory.createGetInvocationHandler());
cg.addMethod(factory.createGetTargetTypes());
boolean haveToString = false;
if (trace) log.trace("Creating proxy methods...");
for (int i = 0; i < methods.length; i++)
{
Method m = methods[i];
if (trace) log.trace("Reflected method: " + m);
String name = m.getName();
Class rTypeClass = m.getReturnType();
String rTypeName = rTypeClass.getName();
Type rType = Utility.getType(rTypeClass);
Type[] pTypes = Utility.getTypes(m.getParameterTypes());
String[] exceptionNames = getNames(m.getExceptionTypes());
if (name.equals("toString") && pTypes.length == 0) {
haveToString = true;
}
org.apache.bcel.classfile.Method proxyMethod =
factory.createProxyMethod(name, i, rType, pTypes, exceptionNames);
if (trace) log.trace("Created proxy method: " + proxyMethod);
cg.addMethod(proxyMethod);
}
if (!haveToString) {
cg.addMethod(factory.createToString());
}
JavaClass jclass = cg.getJavaClass();
if (trace) log.trace("Generated Java class: " + jclass);
if (CLASS_DUMP_PATH != null) {
try {
String filename = CLASS_DUMP_PATH + java.io.File.separator + proxyClassName + ".class";
log.info("Dumping generated proxy class to " + filename);
jclass.dump(filename);
}
catch (Exception e) {
log.error("Failed to dump class file", e);
}
}
return jclass.getBytes();
}
private String[] getNames(Class[] classes) {
String[] names = new String[classes.length];
for ( int i = 0; i < classes.length; i++ ) {
names[i] = classes[i].getName();
}
return names;
}
}