ByteBuddy: Use new defined field in intercept during construction of class - java

I am looking at some ByteBuddy code from someone else. He uses ByteBuddy to generate runtime subclasses which are used as proxies to implement some management code of his runtime into specific objects.
Class<? extends T> newSubClass = new ByteBuddy(ClassFileVersion.ofThisVm())
.subclass(classType)
.defineField("_core", Object.class, Visibility.PUBLIC) //<---
.method(ElementMatchers.isDeclaredBy(classType))
.intercept(InvocationHandlerAdapter.of((proxy, method, m_args) -> {
//TODO: Need to replace core with _core as core is a function argument and will make it bound
return proxyHandler(core, method, m_args); //<--
}))
.make()
.load(roleType.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
T proxy = ReflectionHelper.newInstance(newSubClass, args);
newSubClass.getField("_core").set(proxy, core);
In order to not bind the core object directly into the lambda I want to use the new defined field _coreso I can reuse the generated class (and not regenerate it for every call of the function).
Is there a way to achieve this?
Thanks in advance.

You can define custom constructors just as you define methods. One important point for defining a constructor is that you require another constructor call as its first instruction. You can invoke a constructor using MethodCall::invoke which you can combine with FieldAccessor::ofField.
This way, you can define your class similar to the following:
new ByteBuddy(ClassFileVersion.ofThisVm())
.subclass(classType, ConstructorStrategy.Default.NO_CONSTRUCTORS)
.defineConstructor(Visibility.PUBLIC)
.withParameter(InvocationHandler.class)
.intercept(MethodCall.invoke(classType.getDeclaredConstructor())
.andThen(FieldAccessor.ofField("_core").setsArgumentAt(0)))
.defineField("_core", InvocationHandler.class, Visibility.PUBLIC)
.method(ElementMatchers.isDeclaredBy(classType))
.intercept(InvocationHandlerAdapter.toField("_core"))
.make();
This way, you can set a custom InvocationHandler per instance. If you want to only store the state in the _core field and access this field from your interceptor, have a look at MethodDelegation:
new ByteBuddy(ClassFileVersion.ofThisVm())
.subclass(classType, ConstructorStrategy.Default.NO_CONSTRUCTORS)
.defineConstructor(Visibility.PUBLIC)
.withParameter(Object.class)
.intercept(MethodCall.invoke(classType.getDeclaredConstructor())
.andThen(FieldAccessor.ofField("_core").setsArgumentAt(0)))
.defineField("_core", Object.class, Visibility.PUBLIC)
.method(ElementMatchers.isDeclaredBy(classType))
.intercept(MethodDelegation.to(MyHandler.class))
.make();
public class MyHandler {
#RuntimeType
public static Object intercept(#FieldValue("_core") Object value) { ... }
}
Other annotations you might need are #This, #AllArguments, #Origin and #SuperCall. The less you need, the more efficient your proxy will be. Especially #AllArguments is expensive due to its allocation requirements.
Note that in this case, your field is only set after the super constructor call. Also, you assume that there is a default constructor in the super type. Alternatively, you can implement a custom ConstructorStrategy.
As for caching, have a look at Byte Buddy's TypeCache.

Related

Bytebuddy - proxy private annotated method

I am proxying my objects with ByteBuddy and all works fine. When i annotate a method with #Test then they should be proxied another way. So i decided to separate my InvocationHandler into two. So far so good.
But now, when i want to add private, #Test-annotated methods they wont get proxied/intercepted. Only public methods will be intercepted. Any ideas why?
// return created proxy instance
#SuppressWarnings("unchecked")
Class<T> proxy = (Class<T>) byteBuddy
.subclass(clazz)
.implement(Proxy.class)
.defineField("_origin", Object.class, Visibility.PRIVATE)
.defineConstructor(Visibility.PUBLIC)
.withParameter(Object.class)
.intercept(MethodCall.invoke(clazz.getDeclaredConstructor()).andThen(FieldAccessor.ofField("_origin").setsArgumentAt(0)))
.name(clazz.getSimpleName() + "Proxy")
.method(ElementMatchers.isAnnotatedWith(Test.class))
.intercept(InvocationHandlerAdapter.of(testInvocationHandler))
.method(ElementMatchers.isDeclaredBy(AdditionalTest.class))
.intercept(InvocationHandlerAdapter.of(testInvocationHandler.getAdditionalTestInvocationHandler()))
.method(ElementMatchers.isDeclaredBy(Proxy.class)).intercept(InvocationHandlerAdapter.of(testInvocationHandler.getAdditionalTestInvocationHandler())).make()
.load(clazz.getClassLoader()).getLoaded();
If you define custom methods, your matchers will not be applied to those. You would need to specify the Implementation to also apply these matchers. Alternatively, you can create a class with the additional methods and then intercept this type by creating another proxy.

How to subclass an abstract class with 1+ args constructor using ByteBuddy

I would like to proxy java.net.HttpURLConnection which has one constructor: HttpURLConnection(URL u). How can I subclass such a class with ByteBuddy without creating custom "Empty" class with the non-arg constructor?
new ByteBuddy().subclass(HttpURLConnection.class)
.method(ElementMatchers.any())
.intercept(InvocationHandlerAdapter.of(proxyHandler))
.make()
.load(HttpURLConnection.class.getClassLoader())
.getLoaded()
.newInstance();
Currently, it fails due to
Caused by: java.lang.NoSuchMethodException: net.bytebuddy.renamed.java.net.HttpURLConnection$ByteBuddy$Mr8B9wE2.<init>()
at java.lang.Class.getConstructor0(Class.java:3082)
I would like to delegate a custom URL to that super constructor it is possible.
But if I create a custom class
public class ProxiedHttpURLConnection extends HttpURLConnection{
protected ProxiedHttpURLConnection() {
super(null); // <---
}
}
and use that one in new ByteBuddy().subclass(ProxiedHttpURLConnection.class) it works fine. There is simple issue with a contractor, not quite sure how to do it.
You can define a custom constructor and invoke a specific super constructor using the MethodCall instrumentation, e.g.
builder = builder.defineConstructor(Visibility.PUBLIC)
.intercept(MethodCall.invoke(HttpURLConnection.class.getDeclaredConstructor(URL.class))
.with((Object) null))
By default, Byte Buddy immitates the super class constructors, you can therefore lookup a declared constructor that takes a URL and provide the null argument manually.
You can avoid this creation by defining a ConstructorStrategy as a second argument to subclass.

casting base class to derived class via reflection

Hi I have to construct an object from an object. Since the base class has more than 50 fields i dont want to do things like
//obj1 is an instance of BaseClass
DerivedClass obj2 = new DerivedClass();
obj2.setField1(obj1.getField1());
obj2.setField2(obj1.getField2())
.... so on
As you see from the title i want to downcast my object but java does not allow this. Is there a utility library or smth that provides a method like
Object convert(BaseClass obj1, DerivedClass obj2)
You can use Apache Commons BeanUtils to do this. Using its BeanUtils class you have access to a lot of utility methods for populating JavaBeans properties via reflection.
To copy all the common/inherited properties from a base class object to a derived class object you can use its static copyProperties() method as
BeanUtils.copyProperties(baseClassObj, derivedClassObj);
From the BeanUtils.copyProperties() docs
Copy property values from the origin bean to the destination bean for all cases where the property names are the same.
If you don't want to use a third-party library, your next best option would be to provide a utility method on the derived class that initializes itself with all the properties of an instance of its parent class.
public void initFromParent(BaseClass obj) {
this.propOne = obj.getPropOne();
// ... and so on
}
You can downcast if the cast is valid:
BaseClass instance = new DerivedClass();
if(DerivedClass.class.isAssignableFrom(instance.getClass()) {
DerivedClass dc = DerivedClass.class.cast(instance);
}
But normally one would implement a constructor with the same arguments and call super().
Or even better, use composition, where instead of inheritance you have DerivedClass hold an instance of BaseClass and delegate calls for the fields to that object.

Retrieving an instance using a string literal with Google Guice

I have multiple modules with service interfaces binding to their corresponding types and I am able to get an instance by using
injector.getInstance(MyServiceInterface.class)
I would like to retrieve the instance using
injector.getInstance("MyServiceInterface")
i.e. a string literal instead of the class type
How can I achieve this ?
To elaborate my question further - I can retrieve the Class object from the string literal using a Class.forName(literal) call and then use it to retrieve the instance with a injector.getInstance(clsInstance) .
After retrieving the instance which I receive in my base service type interface I need to use reflection to invoke the method of the service object.
so Service serv = injector.getInstance(MyCustomService.class)
Now I need to invoke myCustomMethod() present in MyCustomService through reflection since this invoker is generic and is intended to work with multiple services without being aware of their actual type.
I will also need the Method interceptors configured on the service interfaces to be invoked transparently when I invoke the method on this instance reflectively.
While I'm not certain if there's functionality for that built into Guice itself, you could try getting the relevant Class<?> object yourself.
Something along the lines of:
Class<?> myServiceInterfaceClass = Class.forName("path.to.MyServiceInterface");
injector.getInstance(myServiceInterfaceClass);
This does however require that the current Classloader can access that specific class, etc.
This can't be done within Guice... because it can't be done, period! Think about it, let's say you have two of the same class name in different packages. Which class would you instantiate?
So at the very least the String would have to have the fully qualified class name, e.g. instead of Integer, it would have java.lang.Integer.
However, if you know which classes you want to support in advance, you can use a MapBinder.
Tweaking their example to match your use case:
public class ServiceModule extends AbstractModule {
protected void configure() {
MapBinder<String, MyServiceInterface> mapbinder
= MapBinder.newMapBinder(binder(), String.class, MyServiceInterface.class);
mapbinder.addBinding("MyServiceInterface").to(MyServiceImpl.class);
bind(MyServiceInterface.class).to(MyServiceImpl.class);
}
}
Now you can inject like this:
class ServiceManager {
#Inject
public ServiceManager(Map<String, MyServiceInterface> services) {
MyServiceInterface service = stacks.get("MyServiceInterface");
// etc.
}
}
Please note when you call inj.getInstance() you do have to know the return type of the Object you're trying to create, unless you are planning on doing:
Object foo = inj.getInstance(myString);

Controlling when object is created

Suppose i need to create an object as follows and set some values
FileMetaData fileMeta = fileContainer.getMetaData();
fileMeta.setFileName("file name");
fileMeta.setServer("my box");
fileMeta.setDirectory("/path/to/dir");
fileMeta.setFileType(FileType.PROPERTIES);
I later intend to use this object reference to do something useful.
I'd like to recognize the fact that it is possible for the user of the system to not set some fields, for instance, one may forget to
fileMeta.setDateModified(12345);
Is it somehow possible to guarantee that all (or some specific) fields are set before making the object available?
There is nothing in the language to enforce this (except for having a lone visible constructor that takes all the required parameters), but you can do it idiomatically, with a variation on the builder pattern and some method chaining:
FileMetaData fileMeta = new FileMetaDataBuilder(fileContainer.getMetaData())
.setFileName("file name")
.setServer("my box")
.setDirectory("/path/to/dir")
.setFileType(FileType.PROPERTIES)
.build();
The build() method can ensure that all the required fields are set before calling the appropriate constructor of FileMetaData with all the required parameters.
Use the builder pattern and pass the reference to the builder around. When you're done adding extras on top, call .build and capture the returned instance of FileMetaData.
You could constrain it by not allowing the build to succeed until all of the pre-requisites are set.
Basically I can classify the following 3 ways.
First is based on the class itself. You can add method isReady() to your class. This method will perform all checks and return true or false.
Other way is to use Factory or Builder pattern and probably objects repository. Both factory and builder guarantee to create object in ready state. Repository can be used to "publish" ready objects there, so other code requests objects and receives them in ready state only.
Other approach is to use Wrapper (Decorator) pattern.
interface Foo {
public void foo(); //business method
}
class FooImpl implements Foo {
public void foo(){} // does the work
}
class FooDecorator implmeents Foo {
Foo foo;
public void foo(){
if (isInitialized()) {
foo.foo();
}
throw IllegalStateException("Not initialized");
}
}
This solutions may be implemented using dynamic proxy or using AOP framework as well.

Categories

Resources