I am trying to replace my CGLib inteceptor with a ByteBuddy implementation but it's not working. In CGLIB I catch every method call and send it to one method in the ObjectProxy. I have readed this and this and a lot's of others but it still don't work.
Here is my draft code: TestByteBuddy.java
import static java.lang.ClassLoader.getSystemClassLoader;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.matcher.ElementMatchers;
/**
*
* @author Marcelo D. Ré {@literal <marcelo.re@gmail.com>}
*/
public class TestByteBuddy {
private final static Logger LOGGER = Logger.getLogger(TestByteBuddy.class .getName());
static {
if (LOGGER.getLevel() == null) {
LOGGER.setLevel(Level.INFO);
}
}
public TestByteBuddy() {
System.out.println("Inite BB test...");
}
public void test() {
try {
Class<?> poClass = new ByteBuddy()
.subclass(BBFoo.class)
.defineField("___ogm___interceptor", ObjectProxy.class, Visibility.PUBLIC)
.implement(ITest.class)
.method(ElementMatchers.any()) // isDeclaredBy(ITest.class)
.intercept(MethodDelegation.toField("___ogm___interceptor")) // MethodDelegation.to(bbi)
.make()
.load(getSystemClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
// crear la instancia
BBFoo po = (BBFoo) poClass.newInstance();
// agregar el interceptor
ObjectProxy bbi = new ObjectProxy();
bbi.setInternalState("primer objeto");
poClass.getField("___ogm___interceptor").set(po, bbi);
// crear la instancia
BBFoo po2 = (BBFoo) poClass.newInstance();
// agregar el interceptor
ObjectProxy bbi2 = new ObjectProxy();
bbi2.setInternalState("segundo objeto");
poClass.getField("___ogm___interceptor").set(po2, bbi2);
// testear los objetos
System.out.println("*************************************");
System.out.println("invocar a setTestString....");
po.setTestString("Objeto 1");
po.sayHello();
System.out.println(""+po.testString);
((ITest)po).testCall();
((ITest)po).incValCall();
System.out.println("ObjectProxy InternalState: "+((ITest)po).getInternalState());
System.out.println("*************************************");
System.out.println("invocar a setTestString....");
po2.setTestString("Objeto 2");
po2.sayHello();
System.out.println(""+po.testString);
((ITest)po2).testCall();
((ITest)po2).incValCall();
System.out.println("ObjectProxy InternalState: "+((ITest)po2).getInternalState());
System.out.println("*************************************");
} catch (SecurityException ex) {
Logger.getLogger(TestByteBuddy.class.getName()).log(Level.SEVERE, null, ex);
} catch (NoSuchFieldException ex) {
Logger.getLogger(TestByteBuddy.class.getName()).log(Level.SEVERE, null, ex);
} catch (InstantiationException ex) {
Logger.getLogger(TestByteBuddy.class.getName()).log(Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
Logger.getLogger(TestByteBuddy.class.getName()).log(Level.SEVERE, null, ex);
}
}
public static void main(String[] args) {
new TestByteBuddy().test();
}
}
ObjectProxy.java
import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperMethod;
import net.bytebuddy.implementation.bind.annotation.This;
/**
*
* @author Marcelo D. Ré {@literal <marcelo.re@gmail.com>}
*/
public class ObjectProxy implements ITest {
private final static Logger LOGGER = Logger.getLogger(ObjectProxy.class .getName());
static {
if (LOGGER.getLevel() == null) {
LOGGER.setLevel(Level.INFO);
}
}
String internalState;
public ObjectProxy() {
}
@Override
public String getInternalState() {
return internalState;
}
@Override
public void setInternalState(String internalState) {
this.internalState = internalState;
}
@Override
public void testCall() {
System.out.println("ObjectProxy TestCall");
}
@Override
public int incValCall() {
System.out.println("ObjectProxy incValCall");
return 0;
}
// a este método se llama cuando se usa MethodDeletation
@RuntimeType
public Object intercept(@This Object self,
@Origin Method method,
@AllArguments Object[] args,
@SuperMethod Method superMethod) throws Throwable {
Object res = null;
System.out.println(">>>>>>>>> Intercepted: "+method.getName());
switch(method.getName()){
case "testCall":
System.out.println("intercept TestCall");
this.testCall();
break;
case "incValCall":
System.out.println("intercept incValCall");
this.incValCall();
break;
default:
System.out.println("intercept default: "+self.getClass().toString() +" : "+ superMethod.getName()+" : "+method.getName());
res = superMethod.invoke(self, args);
}
System.out.println("^^^^^^^^^^^^^^^^");
return res;
}
ITest.java
public interface ITest {
public void testCall();
public int incValCall();
public String getInternalState();
public void setInternalState(String internalState) ;
}
BBFoo.java
public class BBFoo {
private final static Logger LOGGER = Logger.getLogger(BBFoo.class .getName());
static {
if (LOGGER.getLevel() == null) {
LOGGER.setLevel(Level.INFO);
}
}
public String testString;
private int val = 0;
public void sayHello(){
System.out.println("Hola mundo! soy "+this.testString);
}
public int incVal() {
val++;
return val;
}
public void setTestString(String s) {
testString = s;
}
public String getTestString() {
return testString;
}
}
And this is the output that I get:
First edition after the reply of Rafael
As suggested I have re-edited the TestByteBuddy.java and now it look like this:
import static java.lang.ClassLoader.getSystemClassLoader;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.matcher.ElementMatchers;
/**
*
* @author Marcelo D. Ré {@literal <marcelo.re@gmail.com>}
*/
public class TestByteBuddy {
private final static Logger LOGGER = Logger.getLogger(TestByteBuddy.class .getName());
static {
if (LOGGER.getLevel() == null) {
LOGGER.setLevel(Level.INFO);
}
}
public TestByteBuddy() {
System.out.println("Inite BB test...");
}
private <T> T getProxyIntance(Class<T> c) {
T po = null;
try {
Class<?> poClass = new ByteBuddy()
.subclass(c)
.defineField("___ogm___interceptor", ObjectProxy.class, Visibility.PUBLIC)
.implement(ITest.class)
.method(ElementMatchers.any()) // isDeclaredBy(ITest.class)
.intercept(MethodDelegation // This.class,Origin.class,AllArguments.class,SuperMethod.class)
.withDefaultConfiguration() //.withBinders(TargetMethodAnnotationDrivenBinder.ParameterBinder.DEFAULTS)
.filter(ElementMatchers.named("intercept"))
.toField("___ogm___interceptor")) // MethodDelegation.to(bbi)
.make()
.load(getSystemClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
// crear la instancia
po = (T) poClass.newInstance();
// agregar el interceptor
ObjectProxy bbi = new ObjectProxy();
poClass.getField("___ogm___interceptor").set(po, bbi);
} catch (NoSuchFieldException | InstantiationException | IllegalAccessException ex) {
Logger.getLogger(TestByteBuddy.class.getName()).log(Level.SEVERE, null, ex);
}
return po;
}
public void test() {
try {
// crear la instancia
BBFoo po = this.getProxyIntance(BBFoo.class);
// crear la instancia
BBFoo po2 = this.getProxyIntance(BBFoo.class);
// testear los objetos
System.out.println("*************************************");
System.out.println("invocar a setTestString....");
po.setTestString("Objeto 1");
po.sayHello();
System.out.println(""+po.testString);
((ITest)po).testCall();
int i = ((ITest)po).incValCall();
System.out.println("ObjectProxy InternalState: "+((ITest)po).getInternalState());
System.out.println("*************************************");
System.out.println("invocar a setTestString....");
po2.setTestString("Objeto 2");
po2.sayHello();
System.out.println(""+po.testString);
((ITest)po2).testCall();
i = ((ITest)po2).incValCall();
System.out.println("ObjectProxy InternalState: "+((ITest)po2).getInternalState());
System.out.println("*************************************");
BBFooEx poEx = this.getProxyIntance(BBFooEx.class);
BBFooExEx poExEx = this.getProxyIntance(BBFooExEx.class);
} catch (SecurityException ex) {
Logger.getLogger(TestByteBuddy.class.getName()).log(Level.SEVERE, null, ex);
}
}
public static void main(String[] args) {
new TestByteBuddy().test();
}
}
and I have to add the @IgnoreForBinding
annotation at every method in ObjectProxy.java except intercept
.
But now, if I add this two more class, it fail: BBFooEx.java
public class BBFooEx extends BBFoo {
private final static Logger LOGGER = Logger.getLogger(BBFooEx.class .getName());
static {
if (LOGGER.getLevel() == null) {
LOGGER.setLevel(Level.INFO);
}
}
private String svex;
public BBFooEx() {
this(null, "default");
}
public BBFooEx(String s, String svex) {
super(s);
this.svex = svex;
}
public HashMap<String, String> hmString;
public HashMap<String, BBFooEx> ohmSVE;
public void initHashMapString() {
this.hmString = new HashMap<>();
this.hmString.put("hmString 1", "hmString 1");
this.hmString.put("hmString 1", "hmString 2");
this.hmString.put("hmString 1", "hmString 3");
}
}
BBFooExEx.java
public class BBFooExEx extends BBFooEx {
private final static Logger LOGGER = Logger.getLogger(BBFooExEx.class .getName());
static {
if (LOGGER.getLevel() == null) {
LOGGER.setLevel(Level.INFO);
}
}
public BBFooExEx() {
ohmSVE = new HashMap<>();
initHashMapString();
}
}
When ByteBuddy try to instantiate the BBFooExEx it fail with a NullPointerException:
Exception in thread "main" java.lang.NullPointerException
at bb.BBFooExEx$ByteBuddy$bUrgo9zW.initHashMapString(Unknown Source)
at bb.BBFooExEx.<init>(BBFooExEx.java:26)
at bb.BBFooExEx$ByteBuddy$bUrgo9zW.<init>(Unknown Source)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
at java.base/java.lang.Class.newInstance(Class.java:584)
at bb.TestByteBuddy.getProxyIntance(TestByteBuddy.java:50)
at bb.TestByteBuddy.test(TestByteBuddy.java:110)