0

I'm trying to create CGLib Proxy for java.net.SocketImpl class using code like:

Enhancer e = new Enhancer();
e.setSuperclass(SocketImpl.class);
e.setCallback(new MethodInterceptor() {
    @Override
    public Object intercept(Object socketImplInstance, Method method, Object[] arguments, MethodProxy methodProxy) throws Throwable {
        System.out.println("Got call to " + method.getName());
        return methodProxy.invokeSuper(socketImplInstance, arguments);
    }
});
SocketImpl socketImpl = (SocketImpl)e.create();

Method m = SocketImpl.class.getDeclaredMethod("getSocket");
m.setAccessible(true);
System.out.println("getSocket: " + m.invoke(socketImpl));

m = SocketImpl.class.getDeclaredMethod("getLocalPort");
m.setAccessible(true);
System.out.println("getLocalPort: " + m.invoke(socketImpl));

As a result of this code I get an output:

getSocket: null
Got call to getLocalPort
getLocalPort: 0

We have no "Got call to getSocket", so SocketImpl#getSocket() has not been intercepted. This method differs from SocketImpl#getLocalPort() only by access level - SocketImpl#getLocalPort() is protected, while SocketImpl#getSocket() is package-private. I have the same behavior with other package-private method SocketImpl#getServerSocket().

I tried to reproduce this error with user class (which is abstract according to SocketImpl), but everything works as expected, if we have:

package userpackage;

public abstract class Abstract {
    void testMethod() {}
}

package somethingother;

Enhancer e = new Enhancer();
e.setSuperclass(Abstract.class);
e.setCallback(new MethodInterceptor() {
    @Override
    public Object intercept(Object abstractInstance, Method method, Object[] arguments, MethodProxy methodProxy) throws Throwable {
        System.out.println("Got call to " + method.getName());
        return methodProxy.invokeSuper(abstractInstance, arguments);
    }
});
Abstract abstrct = (Abstract)e.create();

Method m = Abstract.class.getDeclaredMethod("testMethod");
m.setAccessible(true);
System.out.println("testMethod: " + m.invoke(abstrct));

We get output:

Got call to testMethod
testMethod: null

Which is totally fine, this package-private method has been intercepted.

Please, can you help me to understand what's going on on this examples and why do we have different behavior. I have only one guess that it can be related to SecurityManager, but in that case, can you point me the specific case why it doesn't work?

I used CGLib 3.1 and 3.2.0 for tests.

Dmitry Spikhalskiy
  • 5,379
  • 1
  • 26
  • 40

1 Answers1

2

Cglib is intercepting methods by creating a subclass that overrides all methods of its superclass. For overriding package-private methods, the subclass must be defined in the same package.

This is not possible for SocketImpl for two reasons. 1. Only the bootstrap class loader is allowed to define packages in the internal name spaces. 2. In order to override a method, the runtime package also needs to be equal. It is not possible to inject a class into the bootstrap class loader, though.

With cglib, this is not possible. You can however look into Byte Buddy which allows for the creation of bootstrap class proxies via instrumentation.

Rafael Winterhalter
  • 42,759
  • 13
  • 108
  • 192
  • Thanks for your answer Rafael, you are right. If do enhancedSocketImpl.getClass() I got `$java.net.SocketImpl$$EnhancerByCGLIB$$f41d9022` on the same time SocketImpl.class is `java.net.SocketImpl`, so CGLib creates subclass in "$java.net" package, not "java.net". – Dmitry Spikhalskiy Nov 19 '15 at 12:53
  • Rafael, I took a look at ByteBuddy, but it looks like it's impossible or much harder to write CGLib-styled MethodInterceptor with own internal variables with values from external context there. So, it's required to rethink implementation in ByteBuddy's abstractions. But I will take a closer look in free time. Thanks for advice. – Dmitry Spikhalskiy Nov 19 '15 at 13:02
  • Ok, found that more or less the same it can be implemented using InvocationHandlerAdapter with java.lang.reflect.InvocationHandler implementation. – Dmitry Spikhalskiy Nov 19 '15 at 13:43
  • Hi Dimitry, you should be able to do about everything with Byte Buddy that is possible with cglib with less code. Byte Buddy allows you to use annotations that mimic cglib's `InvocationHandler`. It is important to use annotations instead of an interface as the bootstrap class loader cannot see interfaces that are defined by cglib what causes `NoClassDefFoundError`s. – Rafael Winterhalter Nov 19 '15 at 13:47