3

I'm trying to port some C# code to Java and I'm having trouble with a proxy implementation.

I have this class:

public class Comic
{
    public int ComicID;

    private List<Volume> _volumes;
    public List<Volume> GetVolumes()
    {
        return _volumes;
    }
    public void SetVolumes(List<Volume> volumes)
    {
        _volumes = volumes;
    }
}

And I'm trying to add an interceptor to a specific Method call for this entity, but I also need access to it's fields since I'll be using them.

After looking around for how to implement proxies in Java, I got this:

public void Load(Class<?> type)
{
    // - type is a Comic.class 

    ClassLoader appLoader = this.getClass().getClassLoader();

    MyProxyHandler proxyHandler = new MyProxyHandler();
    Object proxy = Proxy.newProxyInstance(appLoader, new Class[] { ComicInterface.class }, proxyHandler);
}

The problem is, the type is dynamic so I don't know which will be, and I don't want the code to have a requirement of having interfaces of everything, so I looked up on how to build a dynamic interface:

public class InterfaceLoader extends ClassLoader
{
    public InterfaceLoader(ClassLoader loader)
    {
        super(loader);
    }

    public Class<?> GetInterface(Class<?> type) throws Exception
    {
        String interfaceName = type.getName() + "$Proxy";
        Class<?> interfaceType = null;
        interfaceType = findLoadedClass(interfaceName);
        if (interfaceType != null) { return interfaceType; }

        // - According to the link
        byte[] classData = new InterfaceBuilder().BuildInterface(interfaceName, type.getDeclaredMethods());
        return defineClass(interfaceName, classBytes, 0, classBytes.length);
    }
}

And then I'd use it with the Proxy:

public void Load(Class<?> type)
{
    // - type is a Comic.class 
    ClassLoader appLoader = this.getClass().getClassLoader();

    InterfaceLoader loader = new InterfaceLoader(appLoader);
    Class<?> dynamicInterface = loader.GetInterface(type);
    // - dynamicInterface on debug is "console.Comic$Proxy", seems fine

    MyProxyHandler proxyHandler = new MyProxyHandler();
    Object proxy = Proxy.newProxyInstance(appLoader, new Class[] { dynamicInterface }, proxyHandler);
}

The exception I get is

java.lang.IllegalArgumentException: interface console.Comic$Proxy is not visible from class loader

I've looked up the exception but only found two solutions, which is to make sure the name doesn't conflict with any type (and I'm sure it doesn't) and to use this.getClass().getClassLoader() instead of type.getClass().getClassLoader() (same error).

What am I doing wrong?

And another issue is, how can I get the "original" object so I can get/set values from fields like ComicID? Using the above proxy method I can intercept methods fine, but I lose access to it's fields.

I've read about it here to get the InvocationHandler, but I don't know how to get the object from the handler and I couldn't find an example with it.

Community
  • 1
  • 1
Danicco
  • 1,573
  • 2
  • 23
  • 49
  • Don't bury the actual problem in the code. – user207421 Oct 25 '15 at 01:34
  • What are you *actually* trying to accomplish here? There's very likely a simpler and/or preexisting way to handle it. – chrylis -cautiouslyoptimistic- Oct 25 '15 at 01:58
  • @chrylis It's a port from a C# code which I generate a "copy" of the original object, inheriting the original, but adding the ability to intercept any method call (for logging or lazy loading for example). Thinking about it now, I found it nice that Java had a dynamic Proxy builder but since it only works with interfaces, it's not the same as creating a dynamic class inheriting the original... – Danicco Oct 25 '15 at 02:02
  • @Daniichi Sounds like what you're looking for is something like AspectJ, or possibly Spring AOP if proxies are acceptable. Both AspectJ and cglib-based AOP can work with actual classes, as long as the classes and methods in question aren't final. – chrylis -cautiouslyoptimistic- Oct 25 '15 at 05:12

1 Answers1

0

There is only one classLoader that can see your dynamically generated interface, and that is the InterfaceLoader loader. You can probably make the error go away by passing that to newProxyInstance.

But, I'm pretty sure this won't do you any good. None of the java code you are writing is loaded by that class loader, so you won't be able to call through that interface except by reflection.

If you want to use a proxy, then you'll have to make Comic implement an interface, and then use that interface everywhere. In that case, the answer to your next question is that you would pass an instance of the original object to your proxy handler constructor.

If you really want to do this dynamic interception thing, you probably want to look at using ASM (http://asm.ow2.org/) or Javaassist to build a dynamic subclass of Comic that overrides the method you want to intercept, instead of using a proxy.

Matt Timmermans
  • 53,709
  • 3
  • 46
  • 87