Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java: create new implementation of type at runtime?

So, I realize the answer to this is probably "it's hard", but:

I've got a weird idea, and was wondering if it's possible in Java to create a method like:

<T> T wrapInterface (Class<T> interfaceClass, T wrappedObject) {
  if (mClass.isInterface()) {
    //create a new implementation of interfaceClass that, in each method, 
    //does some action before delegating to wrappedObject
    return thatImplementation;
  }
}

So basically, if my interface Foo defined a method foo(), I'd want this method to create a new class that looked about like this, created an instance of that class with wrappedObject as the constructor parameter, and then returned it:

public class GeneratedClass implements Foo {
  private Foo wrapped;
  public GeneratedClass (Foo wrapped) {
    this.wrapped = wrapped;
  }
  @Override
  public void foo () {
    System.out.println("Calling Foo.foo() on wrapped object " + 
                        wrapped.toString());
    wrapped.foo();
  }
}

The application I'm considering is more complicated than just logging the call, but logging is sufficient for the idea. I'd like to do this with a large number of interface types, which is why I wouldn't just write all the GeneratedClasses by hand.

Bonus points for a solution that doesn't require extra-linguistic features (bringing in AspectJ or something along those lines), and double-bonus-points if this is possible with just the standard JDK libraries.

(I don't need a precise, compilable answer; just a pointer to the right sets of tools/libraries/etc that would let me do this.)

Thanks!

like image 935
Sbodd Avatar asked Oct 20 '09 19:10

Sbodd


2 Answers

Here is a very simplistic implementation (not good enough likely for what you want to do, but with some tweaking ... The main thing to look at would be class loader issues, then there might be some validation issues, etc.) I use the code for testing purposes, so it isn't exactly production quality stuff.

    @SuppressWarnings("unchecked")
    public static <T> T generateProxy(Object realObject, Class<?>... interfaces) {
        return (T) Proxy.newProxyInstance(realObject.getClass().getClassLoader(), interfaces, new SimpleInvocationHandler(realObject));
    }


    private static class SimpleInvocationHandler implements InvocationHandler {
        private Object invokee;

        public SimpleInvocationHandler(Object invokee) {
            this.invokee = invokee;
        }

        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
            method = invokee.getClass().getMethod(method.getName(), method.getParameterTypes());
            if (!method.isAccessible()) {
                method.setAccessible(true);
            }
            try {
                return method.invoke(invokee, args);
            } catch (InvocationTargetException e) {
                throw e.getTargetException();
            }
        }
    }
like image 171
Yishai Avatar answered Oct 26 '22 07:10

Yishai


What you need is ASM.

From asm-guide.pdf:
2.2.3 Generating classes
The only required component to generate a class is the ClassWriter component. Let’s take an example to illustrate this. Consider the following interface:

package pkg;
public interface Comparable extends Mesurable {
  int LESS = -1;
  int EQUAL = 0;
  int GREATER = 1;
  int compareTo(Object o);
}

It can be generated with six method calls to a ClassVisitor:

ClassWriter cw = new ClassWriter(0);
cw.visit(V1_5, ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE,
    "pkg/Comparable", null, "java/lang/Object",
    new String[] { "pkg/Mesurable" });
cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "LESS", "I",
    null, new Integer(-1)).visitEnd();
cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "EQUAL", "I",
    null, new Integer(0)).visitEnd();
cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "GREATER", "I",
    null, new Integer(1)).visitEnd();
cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "compareTo",
    "(Ljava/lang/Object;)I", null, null).visitEnd();
cw.visitEnd();
byte[] b = cw.toByteArray();
like image 37
rodrigoap Avatar answered Oct 26 '22 06:10

rodrigoap