Invoke method on a argument with a cast using Byte Buddy - java

I'm very new to Byte Buddy, and I'm trying to use it to create implementations of an interface that execute getter methods on objects. My interface looks like this:
public interface Executor {
Object execute(final Object target);
}
And the idea is that if I have a class such as:
public class User {
...
public String getName() { return this.name; }
public String getSurname() { return this.surname; }
}
I need to be able to create one implementation of the Executor interface which execute(obj) method assumes obj is a User and calls its getName(), then another implementation which does the same for getSurname(), etc. Equivalent java code would therefore be:
public class MyHypotheticalByteBuddyExecutorImpl implements Executor {
#Override
Object execute(final Object target) {
return ((User) target).getName();
}
}
So the idea is to be able to create classes like the above for any combination of class + getter, like in this case User + getName().
I (think I) know how to make Byte Buddy create a class that almost does that:
final Method nameMethod = User.class.getMethod("getName", null);
final Class<?> myHypotheticalByteBuddyExecutorImpl =
new ByteBuddy()
.subclass(Object.class)
.implement(Executor.class)
.method(ElementMatchers.named("execute"))
.intercept(MethodCall.invoke(nameMethod).onArgument(0))
.make()
.load(ByteBuddyTest.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
...but then Byte Buddy rightly throws an Exception saying that I cannot execute method getName() on an Object. I'm assuming therefore that I'm lacking the ((User) target) cast:
Exception in thread "main" java.lang.IllegalStateException: Cannot invoke public java.lang.String com.example.User.getName() on class java.lang.Object
at net.bytebuddy.implementation.MethodCall$TargetHandler$ForMethodParameter$Resolved.toStackManipulation(MethodCall.java:2527)
at net.bytebuddy.implementation.MethodCall$Appender.toStackManipulation(MethodCall.java:3541)
at net.bytebuddy.implementation.MethodCall$Appender.apply(MethodCall.java:3502)
...
I believe this could be defined as a StackManipulation (I might be totally wrong), something like:
final StackManipulation typeCasting =
TypeCasting.to(TypeDescription.ForLoadedType.of(User.class));
But I cannot find anywhere in the Byte Buddy API how I can apply this cast (or any other code I might need for casting) to the argument of the execute(Object) method before executing the getter.
How can I implement this?

This should work by using dynamic typing which you can configure by:
MethodCall.invoke(nameMethod)
.onArgument(0)
.withAssigner(Assigner.DEFAULT, Assigner.Typing.DYNAMIC);
The stack manipulation is used for creating custom byte code, I do not think that this is what you want to do here.

Related

Does it make sense to create a `Copyable` type interface instead of using `Cloneable`?

I have a bit of code that requires a copy of an object be sent in. This requirement is because a service (runtime library) that is called modifies the object sent. This object also needs to expose setters, in case the doThing method below needs to set any field in the ImportantObj class. This implementation is pending change, but does not have a reasonable expectation to be changed in the near future. My workaround is to provide a class that does as follows:
public class DangerousCallWrapper<T> implements DangerousCaller<T> {
public T doThing(T dataObject) {
T cloneOfDataObject = #Clone of dataObject
// This service modifies the cloneOfDataObject... dangerous!
Optional<T> result = service.doThing(cloneOfDataObject);
return result.orElseThrow(() -> new RuntimeException("No data object returned");
}
}
public interface DangerousCaller<T> {
/**
* Performs the functionality of the DangerousService
*/
public T doThing(T);
}
public DangerousService<T> {
public T doThing(T data) {
data.importantField = null;
data.thing = "Done!";
return data;
}
}
public static void main() {
DangerousService service = new DangerousService<ImportantObj>();
ImportantObj important = new ImportantObj().setImportantField("Password for my bank account").setThing("Undone");
service.doThing(important);
//would fail this check
assertNotNull(important.importantField);
DangerousCallWrapper wrapper = new DangerousCallWrapper<ImportantObj>();
ImportantObj important = new ImportantObj().setImportantField("Password for my bank account").setThing("Undone");
service.doThing(important);
//would not fail this check
assertNotNull(important.importantField);
}
So the first line of that method is where I am stuck. It is a generic type, so I can't explicitly call some cloning utility like Jackson, or similar.
So I thought I would just add T extends Cloneable to the method... but I opened the can of worms that Cloneable is beyond taboo (https://www.artima.com/intv/bloch13.html). I have also read that copy constructors are probably the best way to handle this... However, I am unsure of how to denote that using the generics.
So my thought was to provide an interface Copyable that does what you would expect Cloneable to do: expose a method, copy() that will create a new instance of the class.
Does this constitute a viable approach?
To solve your problem you need to polymorphically make a copy of dataObject like this:
T cloneOfDataObject = dataObject.clone();
and the issue is that Cloneable does not have a clone() method, so the above does not compile.
Given this premise, it does make sense to create your own Copyable interface that defines a clone() method so you can leverage already-implemented clone() methods (if they exist) on the classes of your data object. For maximum effectiveness this interface would need to be generic as well:
interface Copyable<T> {
public T clone();
}
and the type bound:
public class DangerousCallWrapper<T extends Copyable<T>>
implements DangerousCaller<T> {

How to pass a method as an argument to a different method?

I have a method let's say in ClassA. I want to pass a method from ClassB as an argument to that method in ClassA. In this case I want to pass the getCode method from ClassB. I don't have any instance of ClassB and I'd like to achieve this without having to create one.
I've tried using simple method reference, but it does not work this way.
I don't want to make getCode a static method either.
public class ClassA {
public void validate() {
Validation validation = new Validation(ClassB::getCode, code);
//...
}
}
My final goal is to have a RequestValidator class to which add validations, each validation will be created with a specific method and a string in its constructor, in this case getCode from classB and code. Please note I only want one instance of RequestValidator. Something like this:
RequestValidator validator = new RequestValidator<>()
.addValidation(new Validation(ClassB::getCode, code))
.addValidation(new Validation(ClassB::getName, name));
getCode needs to be a static function, and the syntax would be ClassB.getCode. You would need ClassB to be imported into ClassA.
See:
Calling static method from another java class
Your use of a method reference will work just fine as long as you define the method arguments properly. You haven't given a lot of information, so I'm going to make some assumptions here. Please correct me if this isn't what you had in mind:
public class B {
public static String getCode() {
return "foobar"; // Replace with your own functionality
}
}
public class Validation {
Validation(Supplier<String> supplier, String code) {
String suppliedCode = supplier.get();
// Do your validation logic
}
}
public static void validate() {
Validation validation = new Validation(ClassB::getCode, code);
}
But this frankly feels like overkill. Why can't you just make your Validation constructor take two String arguments (or whatever types you happen to be using), and then do this?
public static void validate() {
Validation validation = new Validation(ClassB.getCode(), code);
}
Do you have a legitimate need to pass in a method reference instead of simply passing in the return value from the method call?

ByteBuddy: How to implement field access interceptor?

I'am trying to make a OGM to translate object to Vertex for the OrientDB. Currently i'am using GCLib but i read that ByteBuddy could implements two critical things that if work, it will improve the OGM speed.
Could ByteBuddy implement field access control? I read the doc but it's not clear or I do not understand it.
Dinamically add default empty constructor.
The current problem is this: We do not know the class definition that will be passed as a parameter. The idea is to redefine the class and implement the empty constructor if it not have one, add a field named __BB__Dirty to set the object as dirty if an assign operation was detected and force the implementation of an interface to talk with the object.
Example:
A generic class:
public class Example {
int i = 0;
String stringField;
public Example(Strinf s) {
stringField = s;
}
public void addToI(){
i++;
}
}
Now we have an interface like this:
public interface DirtyCheck {
public boolean isDirty();
}
So, I want to force the Example class to implement the interface, the method isDirty(), a field to work on and a default contructor so the class should be translated to:
public class Example implements DirtyCheck {
int i = 0;
String stringField;
boolean __BB__dirty = false;
public Example() {
}
public Example(Strinf s) {
stringField = s;
}
public void addToI(){
i++;
}
public boolean isDirty() {
return this.__BB__dirty;
}
}
and the some magically assigner so if any field (except __BB__dirty) is modified, the __BB__dirty field is set to True;
I have tried the first part of this but I fail :(
...
ByteBuddyAgent.install();
Example ex = new ByteBuddy()
.redefine(Example.class)
.defineField("__BB__Dirty", boolean.class, Visibility.PUBLIC)
.make()
.load(Example.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent())
.getLoaded().newInstance();
....
ex.addToI(); // <--- this should set __BB__dirty to true since it
// assign a value to i.
But i get this error:
Exception in thread "main" java.lang.UnsupportedOperationException: class redefinition failed: attempted to change the schema (add/remove fields)
at sun.instrument.InstrumentationImpl.redefineClasses0(Native Method)
at sun.instrument.InstrumentationImpl.redefineClasses(InstrumentationImpl.java:170)
at net.bytebuddy.dynamic.loading.ClassReloadingStrategy$Strategy$1.apply(ClassReloadingStrategy.java:297)
at net.bytebuddy.dynamic.loading.ClassReloadingStrategy.load(ClassReloadingStrategy.java:173)
at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:4350)
at Test.TestBB.<init>(TestBB.java:33)
at Test.TestBB.main(TestBB.java:23)
I'am stuck in the very first stage to solve the problem with BB.
Thanks
The Java virtual machine does not support changing the layout of classes that are already loaded when redefining a class. This is not a limitation of Byte Buddy but the VM implementation.
In order to do what you want, you should look at the AgentBuilder API which allows you to modify classes before they are loaded. Creating an agent does however require you to add it explicitly as an agent on startup (opposed to adding the library to the class path.
You can implement the interface by calling:
.implement(DirtyCheck.class).intercept(FieldAccessor.of("__dirty__");
You can also add a default constructor by simply defining one:
.defineConstructor(Visibility.PUBLIC).intercept(SuperMethodCall.INSTANCE)
The latter definition requires the super class to define a default constructor.

How to make a java proxy object to java.nio.ByteBuffer instance?

I have a public abstract class java.nio.ByteBuffer instance which is actually an instance of private class java.nio.HeapByteBuffer and I need to make a proxy object which would call some invocation method handler to check access permissions and then call the invoked method on the actual instance.
The problem is that the java.nio.ByteBuffer class has only private constructors and also has some final methods, thus I can not create proxy instances with javassist.util.proxy.ProxyFactory class.
So, how can I make a proxy object to control the invocation of a java.nio.ByteBuffer instance including those final methods invocation?
Please be aware that I am presenting a solution based on my own (FOSS) framework Byte Buddy which is however already mentioned as a potential solution in one of the comments.
Here is a simple proxy approach which creates a subclass. First, we introduce a type for creating proxies for ByteBuffers:
interface ByteBufferProxy {
ByteBuffer getOriginal();
void setOriginal(ByteBuffer byteBuffer);
}
Furthermore, we need to introduce an interceptor to use with a MethodDelegation:
class Interceptor {
#RuntimeType
public static Object intercept(#Origin(cacheMethod = true) Method method,
#This ByteBufferProxy proxy,
#AllArguments Object[] arguments)
throws Exception {
// Do stuff here such as:
System.out.println("Calling " + method + " on " + proxy.getOriginal());
return method.invoke(proxy.getOriginal(), arguments);
}
}
This interceptor is capable of intercepting any method as the #RuntimeType casts the return type in case that it does not fit the Object signature. As you are merely delegating, you are safe. Plase read the documentation for details. As you can see from the annotations, this interceptor is only applicable for instances of ByteBufferProxy. Bases on this assumption, we want to:
Create a subclass of ByteBuffer.
Add a field to store the original (proxied) instance.
Implement ByteBufferProxy and implement the interface methods to access the field for the stored instance.
Override all other methods to call the interceptor that we defined above.
This we can do as follows:
#Test
public void testProxyExample() throws Exception {
// Create proxy type.
Class<? extends ByteBuffer> proxyType = new ByteBuddy()
.subclass(ByteBuffer.class)
.method(any()).intercept(MethodDelegation.to(Interceptor.class))
.defineField("original", ByteBuffer.class, Visibility.PRIVATE)
.implement(ByteBufferProxy.class).intercept(FieldAccessor.ofBeanProperty())
.make()
.load(getClass().getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
// Create fake constructor, works only on HotSpot. (Use Objenesis!)
Constructor<? extends ByteBufferProxy> constructor = ReflectionFactory
.getReflectionFactory()
.newConstructorForSerialization(proxyType,
Object.class.getDeclaredConstructor());
// Create a random instance which we want to proxy.
ByteBuffer byteBuffer = ByteBuffer.allocate(42);
// Create a proxy and set its proxied instance.
ByteBufferProxy proxy = constructor.newInstance();
proxy.setOriginal(byteBuffer);
// Example: demonstrates interception.
((ByteBuffer) proxy).get();
}
final methods are obviously not intercepted. However as the final methods in ByteBuffer only serve as convenience methods (e.g. put(byte[]) calls put(byte[],int,int) with the additional arguments 0 and the array length), you are still able to intercept any method invocation eventually as these "most general" methods are still overridable. You could even trace the original invocation via Thread.currentCallStack().
Byte Buddy normally copies all constructors of its super class if you do not specify another ConstructorStrategy. With no accessible constructor, it simply creates a class without constructors what is perfectly legal in the Java class file format. You cannot define a constructor because, by definition, this constructor would need to call another constructor what is impossible. If you defined a constructor without this property, you would get a VerifierError as long as you do not disable the verifier altogether (what is a terrible solution as it makes Java intrinsically unsafe to run).
Instead, for instantiation, we call a popular trick that is used by many mocking frameworks but which requires an internal call into the JVM. Note that you should probably use a library such as Objenesis instead of directly using the ReflectionFactory because Objenesis is more robust when code is run on a different JVM than HotSpot. Also, rather use this in non-prduction code. Do however not worry about performance. When using a reflective Method that can be cached by Byte Buddy for you (via cacheMethod = true), the just-in-time compiler takes care of the rest and there is basically no performance overhead (see the benchmark on bytebuddy.net for details.) While reflective lookup is expensive, reflective invocation is not.
I just released Byte Buddy version 0.3 and I am currently working on documentation. In Byte Buddy 0.4, I plan to introduce an agent builder which allows you to redefine classes during load-time without knowing a thing about agents or byte code.
I can suggest you 2 solutions.
First, simple, not universal, but probably useful for you.
As far as I can see ByteBuffer has several package-private constructors that allow its subclassing and the following final methods:
public final ByteBuffer put(byte[] src) {
public final boolean hasArray() {
public final byte[] array() {
public final int arrayOffset() {
public final ByteOrder order() {
ByteBuffer extends Buffer that declares some of these methods:
public final boolean hasArray() {
public final Object array() {
public final int arrayOffset() {
As you can see, put() and order() are absent here, return type of array() is a little bit confusing, but still can be used.
So, if you use only these 3 methods you can subclass Buffer and create universal wrapper that wraps any other Buffer including ByteBuffers. If you want you can use javaassist's proxy although IMHO it is not necessarily here.
Second, more universal but more tricky solution. You can create agent that removes final modifiers from speicific class (ByteBuffer in your case) during class loading. Then you can create javassist proxy.
Variation of second solution is following. Copy ByteBuffer soruce code to separate project. Remove final modifiers and compile it. Then push it into bootstrap classpath. This solutions is probably easier than second.
Good luck anyway.
Thanks to #raphw I have managed to make a proxy object construction class which makes a proxy for java.nio.ByteBuffer but that class has final methods which I can not overcome and they are extensively used in the required code, those final methods are Buffer.remaining() and Buffer.hasRemaining(), thus they just can not be proxy mapped.
But I would like to share the classes I have made, just as a report.
public final class CacheReusableCheckerUtils {
private static ByteBuddy buddy = new ByteBuddy();
private static Objenesis objenesis = new ObjenesisStd();
public static <T> T createChecker(T object) {
return createChecker(new CacheReusableCheckerInterceptor<>(object));
}
public static <T> T createChecker(CacheReusableCheckerInterceptor<T> interceptor) {
return objenesis.getInstantiatorOf(createCheckerClass(interceptor)).newInstance();
}
private static <T> Class<? extends T> createCheckerClass(CacheReusableCheckerInterceptor<T> interceptor) {
Class<T> objectClass = interceptor.getObjectClass();
Builder<? extends T> builder = buddy.subclass(objectClass);
builder = builder.implement(CacheReusableChecker.class).intercept(StubMethod.INSTANCE);
builder = builder.method(MethodMatchers.any()).intercept(MethodDelegation.to(interceptor));
return builder.make().load(getClassLoader(objectClass, interceptor), Default.WRAPPER).getLoaded();
}
private static <T> ClassLoader getClassLoader(Class<T> objectClass, CacheReusableCheckerInterceptor<T> interceptor) {
ClassLoader classLoader = objectClass.getClassLoader();
if (classLoader == null) {
return interceptor.getClass().getClassLoader();
} else {
return classLoader;
}
}
}
public class CacheReusableCheckerInterceptor<T> {
private T object;
private boolean allowAccess;
private Throwable denyThrowable;
public CacheReusableCheckerInterceptor(#NotNull T object) {
this.object = object;
}
#SuppressWarnings("unchecked")
public Class<T> getObjectClass() {
return (Class<T>) object.getClass();
}
#RuntimeType
public final Object intercept(#Origin(cacheMethod = true) Method method, #This T proxy, #AllArguments Object[] arguments) {
try {
switch (method.getName()) {
case "allowAccess":
allowAccess();
return null;
case "denyAccess":
denyAccess();
return null;
default:
return invokeMethod(method, arguments);
}
} catch (Exception e) {
throw new CacheReusableCheckerException(method, object, proxy, e);
}
}
private Object invokeMethod(Method method, Object[] arguments) throws IllegalAccessException, InvocationTargetException {
checkMethodAccess(method.getName());
return method.invoke(object, arguments);
}
private void allowAccess() {
if (allowAccess) {
error("double use");
}
allowAccess = true;
onAccessAllowedAfter(object);
}
private void denyAccess() {
if (!allowAccess) {
error("double free");
}
onAccessDeniedBefore(object);
allowAccess = false;
denyThrowable = new Throwable();
}
private void checkMethodAccess(String name) {
if (!allowAccess) {
switch (name) {
case "hash":
case "equals":
case "toString":
case "finalize":
break;
default:
error("use after free");
}
}
}
private void error(String message) {
throw new CacheReusableCheckerException(message, denyThrowable);
}
protected void onAccessAllowedAfter(T object) {
}
protected void onAccessDeniedBefore(T object) {
}
}
public interface CacheReusableChecker {
void allowAccess();
void denyAccess();
}

how to determine the subclass type in a factory pattern other than using if else and switch?

I wanted to try the factory pattern and was able to implement it, but when
generating for more than a few classes, i thought this will be ugly!! so any clarity or suggestions would be really appreciated...
My Superclass:
public abstract class Output {
public abstract void generate(Data dat); }
i got my other classes extending from Output like
public class generateXML extends Output{
.
.
.
}
My question is related to here:
public class generatorFactory(){
public Output generate(String str){
// or getting an Object as an argument like (Object obj)
if(str.equals("xml"){
return new generateXML();
}
else if.........
......
}
Is there any way we can determine the subclass type avoiding checking for each type??
You should consider replacing your if-else chain with a map.
Rather than having to write the code that checks for all the strings you want to support you just have a copule of lines to retrieve the element from the map.
You will, of course, need some more configuration code to put the items in the map, but that should be trivial.
Here it is a nice post about this topic (in PHP)
You can use newInstance() to instanciate a generator whose classname you've built from the parameter:
public Generator getGenerator (final String type)
{
final Class generatorClass = ClassLoader.getSystemClassLoader().loadClass("Generator"+type);
final Generator generator = (Generator) (generatorClass.newInstance());
return generator;
}
PS: I highly rate you to follow the rules of Java: if generateXML is a class, it should be written GenerateXML.
More over: take care by naming your classes. (1) An Object generateXML shouln'd extend Output, because it isnt' an output. (2) "GenerateXML" is a verb, i.e. an action. It is therefore not a correct word to name an object, but a method. You could name the object per example "XMLGenerator".
You can use Reflection.
Object generated = getClass().getMethod("generate" + type.toUpperCase()).invoke(this);
public Object generateXML();
public Object generateJSON();
public Object generateCSV();
You can use enum which can be passed to factory and return factory object based on enum passed. The only thing is you can not export it as API.
enum Type
{
XML {
#Override
public Object getFactory() {
// TODO Auto-generated method stub
return null;
}
};
public abstract Object getFactory();
}
If you have to expose it like API then you can do something like below.
interface IType {
public abstract Object getTypeFactory();
}
enum Type implements IType {
XML {
#Override
public Object getTypeFactory() {
// TODO Auto-generated method stub
return null;
}
};
}
And change Factory method implemetation to
public static Object getFactoryByType(String name) {
Type type = Type.valueOf(name);
return type.getTypeFactory();
}
Since you have to call new everytime I'm not sure you can bypass the branching process. Someone has to know what to give you back.
If it was for singletons you could initialize an HashMap "xml"=>generateXML singleton
After second though, you may modify your String attribute for differents Type classes MyTypeXML, MyTypeJSON, ...
and then use method with the same name but different type.
public Output generate(MyTypeXML xml) { // This will go for XML }
public Output generate(MyTypeJSON json) { // This will go for JSON }
But for factories, I'm not really against the if...else coding.

Categories

Resources