I am trying to dynamically create a class using ByteBuddy with my custom constructor.
I have read the Intercepting default constructor with Byte Buddy and I have written the following code base on that.
Class<?> dynamicType = new ByteBuddy().subclass(Object.class, ConstructorStrategy.Default.NO_CONSTRUCTORS)
.name("foo").defineConstructor(Modifier.PUBLIC).withParameters(int.class)
.intercept(
to(new Object() {
public void construct() throws Exception {
System.out.println("before constructor");
}
})
.andThen(MethodCall.invoke(Object.class.getConstructor()))
.andThen(to(new Object() {
public void construct() throws Exception {
System.out.println("after constructor");
}})
))
.make()
.load(Main.class.getClassLoader(), INJECTION)
.getLoaded();
dynamicType.getConstructor(int.class).newInstance(3);
My question is how can I access the integer argument of 'foo' constructor in the custom codes that I have added before and after calling the super constructor.
Sure, simply define a parameter with an annotation #Argument(0).
I'd recommend against using anonymous classes as their package-private visibility might render tricky outcomes.
Related
I'm trying to add a mutator for an existing private final field. I can transform the field modifiers to remove the final specification and add an accessor method:
// accessor interface
public interface UniqueIdAccessor {
Serializable getUniqueId();
}
// mutator interface
public interface UniqueIdMutator {
void setUniqueId(Serializable uniqueId);
}
...
// fragment of Java agent implementation
return new AgentBuilder.Default()
.type(hasSuperType(named("org.junit.runner.Description")))
.transform(new Transformer() {
#Override
public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription,
ClassLoader classLoader, JavaModule module) {
return builder.field(named("fUniqueId")).transform(ForField.withModifiers(FieldManifestation.PLAIN))
.implement(UniqueIdAccessor.class).intercept(FieldAccessor.ofField("fUniqueId"))
// .implement(UniqueIdMutator.class).intercept(FieldAccessor.ofField("fUniqueId"))
.implement(Hooked.class);
}
})
.installOn(instrumentation);
...
Here's a method that uses reflection to check the modifiers of the target field and calls the accessor to get the value of the field.
private static void injectProxy(Description description) {
try {
Field bar = Description.class.getDeclaredField("fUniqueId");
System.out.println("isFinal: " + ((bar.getModifiers() & Modifier.FINAL) != 0));
} catch (NoSuchFieldException | SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Serializable uniqueId = ((UniqueIdAccessor) description).getUniqueId();
System.out.println("uniqueId: " + uniqueId);
}
// isFinal: false
// uniqueId: <description-unique-id>
... but if I uncomment the second "implement" expression to add the mutator, the transform blows up:
// isFinal: true
// java.lang.ClassCastException:
// class org.junit.runner.Description cannot be cast to class com.nordstrom.automation.junit.UniqueIdAccessor
// (org.junit.runner.Description and com.nordstrom.automation.junit.UniqueIdAccessor
// are in unnamed module of loader 'app')
I could set the field value with reflection, but that defeats the purpose of using Byte Buddy in the first place!
The problem with this approach is that the field accessor considers the input type prior to the modification. Byte Buddy prohibits this as it does not consider the mutation to be legal, not knowing about the removed modifier. As a result, the transformation fails in its entirety and you get the error you are seeing. (Register a listener to see this error.)
To avoid this, you can implement a custom Implementation using FieldAccess (without or). You can have a look at the more convenient FieldAccessor to see how this is implemented, only that you need to drop the validity checks.
Thanks for pointing me in the right direction! I assemble the StackManipulation object that defines the mutator method with this:
final TypeDescription description = TypePool.Default.ofSystemLoader().describe("org.junit.runner.Description").resolve();
final Generic _void_ = TypeDescription.VOID.asGenericType();
final Generic serializable = TypePool.Default.ofSystemLoader().describe("java.io.Serializable").resolve().asGenericType();
final MethodDescription.Token setUniqueIdToken = new MethodDescription.Token("setUniqueId", Modifier.PUBLIC, _void_, Arrays.asList(serializable));
final MethodDescription setUniqueId = new MethodDescription.Latent(description, setUniqueIdToken);
final Token fUniqueIdToken = new FieldDescription.Token("fUniqueId", Modifier.PRIVATE, serializable);
final FieldDescription fUniqueId = new FieldDescription.Latent(description, fUniqueIdToken);
final StackManipulation setUniqueIdImpl = new StackManipulation.Compound(
MethodVariableAccess.loadThis(),
MethodVariableAccess.load(setUniqueId.getParameters().get(0)),
Assigner.DEFAULT.assign(serializable, serializable, Typing.STATIC),
FieldAccess.forField(fUniqueId).write(),
MethodReturn.VOID
);
... and I transform the target class with this:
return new AgentBuilder.Default()
.type(hasSuperType(named("org.junit.runner.Description")))
.transform(new Transformer() {
#Override
public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription,
ClassLoader classLoader, JavaModule module) {
return builder.field(named("fUniqueId")).transform(ForField.withModifiers(FieldManifestation.PLAIN))
.implement(AnnotationsAccessor.class).intercept(FieldAccessor.ofField("fAnnotations"))
.implement(UniqueIdAccessor.class).intercept(FieldAccessor.ofField("fUniqueId"))
.implement(UniqueIdMutator.class).intercept(new Implementation.Simple(setUniqueIdImpl));
}
})
.installOn(instrumentation);
Here are the definitions of the three interfaces used in the transform:
// annotations accessor interface
public interface AnnotationsAccessor {
Annotation[] annotations();
}
// unique ID accessor interface
public interface UniqueIdAccessor {
Serializable getUniqueId();
}
// unique ID mutator interface
public interface UniqueIdMutator {
void setUniqueId(Serializable uniqueId);
}
The following is my learning code, when I executing the code, an exception happened:
Exception in thread "main"
java.lang.AbstractMethodError: org.learning.UserRepository$ByteBuddy$etz0xUhc.$$_pharos_set_interceptor(Lorg/learning/Interceptor;)V**
ByteBuddy version: 1.10.14
final TypeCache.SimpleKey cacheKey = getCacheKey(learningClazz, interceptor.getClass());
Class proxyClass = load(learningClazz, proxyCache, cacheKey, byteBuddy ->
byteBuddy
.subclass(learningClazz)
.defineField(ProxyConfiguration.INTERCEPTOR_FIELD_NAME, Interceptor.class, Visibility.PRIVATE)
.method(not(isDeclaredBy(Object.class)))
.intercept(MethodDelegation.to(ProxyConfiguration.InterceptorDispatcher.class))
.implement(ProxyConfiguration.class)
.intercept(FieldAccessor.ofField(ProxyConfiguration.INTERCEPTOR_FIELD_NAME)
.withAssigner(Assigner.DEFAULT, Assigner.Typing.DYNAMIC)));
final ProxyConfiguration proxy = (ProxyConfiguration) proxyClass.getDeclaredConstructor().newInstance();
proxy.$$_pharos_set_interceptor(interceptor);
return (T) proxy;
public interface ProxyConfiguration {
String INTERCEPTOR_FIELD_NAME = "$$_pharos_interceptor";
void $$_pharos_set_interceptor(Interceptor interceptor);
class InterceptorDispatcher {
#RuntimeType
public static Object intercept(
#This final Object instance,
#Origin final Method method,
#AllArguments final Object[] arguments,
#StubValue final Object stubValue,
#FieldValue(INTERCEPTOR_FIELD_NAME) Interceptor interceptor,
#SuperMethod Method superMethod
) throws Throwable
{
if (interceptor == null) {
return stubValue;
}
else {
return interceptor.intercept(instance, method, arguments, superMethod);
}
}
}
}
Package-private methods are overriden but the JVM will not dispatch them dynamically if the subclass is loaded on a different class loader. If you declare the method public, the problem should be solved. Alternatively, inject the class into the target class loader.
1. Is it possible to put non-POJO class instances as the value of a cache?
For example, I have a QueryThread class which is a subclass of java.lang.Thread and I am trying to put this instance in a cache. It looks like the put operation is failing because this cache is always empty.
Consider the following class:
public class QueryThread extends Thread {
private IgniteCache<?, ?> cache;
private String queryId;
private String query;
private long timeIntervalinMillis;
private volatile boolean running = false;
public QueryThread(IgniteCache<?, ?> dataCache, String queryId, String query, long timeIntervalinMillis) {
this.queryId = queryId;
this.cache = dataCache;
this.query = query;
this.timeIntervalinMillis = timeIntervalinMillis;
}
public void exec() throws Throwable {
SqlFieldsQuery qry = new SqlFieldsQuery(query, false);
while (running) {
List<List<?>> queryResult = cache.query(qry).getAll();
for (List<?> list : queryResult) {
System.out.println("result : "+list);
}
System.out.println("..... ");
Thread.sleep(timeIntervalinMillis);
}
}
}
This class is not a POJO. How do I store an instance of this class in the cache?
I tried implementing Serializable (didn't help).
I need to be able to do this:
queryCache.put(queryId, queryThread);
Next I tried broadcasting the class using the IgniteCallable interface. But my class takes multiple arguments in the constructor. I feel PeerClassLoading is easy if the class takes a no-arg constructor:
IgniteCompute compute = ignite.compute(ignite.cluster().forServers());
compute.broadcast(new IgniteCallable<MyServiceImpl>() {
#Override
public MyServiceImpl call() throws Exception {
MyServiceImpl myService = new MyServiceImpl();
return myService;
}
});
2. How do I do PeerClassLoading in the case of a class with multi-arg constructor?
It's restricted to put Thread instances to the cache, Thread instance cannot be serialized due to call to Native Methods. Thats why you always get empty value.
PeerClassLoading is a special distributed ClassLoader in Ignite for inter-node byte-code exchange. So, it's only about sharing classes between nodes. It doesn't make sense how many arguments in constructor class have.
But, on the other hand, object, that you created, will be serialised and sent to other nodes and for deserialisation it will need a default(non-arg) constructor.
I am trying to mock a method call which goes something like this:
rapContext.getSysInfo().get(key)
The getSysInfo() method returns a ConcurrentHashMap.
Here is what I have done:
Class ABCTest {
#Cascading RapContext context;
#Test
doTest() {
new Expectations() {
{
rapContext.getSysInfo().get(anyString);
result = new UserPrefCtxObject();
}
}
}
With this I get a NullPointerException on rapContext.getSysInfo(). Call to getSysInfo() returns null. If I call any other method which does not return a collection, for instance rapContext.getDomain() everything working fine.
I am not sure what I am missing.
Thanks
The code example is not complete however you are likely running into some issue associated with accidentally mocking Map. If a Map (or any part of the Collection framework) is mocked then a lot of things will break. I could not reproduce your problem as any attempt to mock RapContext using #Cascading resulted in a stack over flow.
You could partially mock RapContext instead and then either return a real or mocked Map. When I run into similar issues I generally get around them using either #Injectable to only mock an instance of a class or using partial mocks.
Here is an approach that will let you mock getSysInfo:
public class RapContextTest {
#Injectable ConcurrentHashMap<String, Object> mockedMap;
#Test
public void testContext() {
RapContext context = new RapContext();
new MockUp<RapContext>(){
#Mock
public ConcurrentHashMap getSysInfo(){
return mockedMap;
}
};
new NonStrictExpectations() {
{
mockedMap.get(anyString);
result = "Success";
}
};
Object value = context.getSysInfo().get("test");
System.out.println(value);
}
}
I'm building on a previously answered question in which ICar implementations are bound using Ninject Conventions Extensions and a custom IBindingGenerator, and the ICarFactory interface is bound using the Ninject Factory Extensions' ToFactory() method and a custom instance provider.
I'm trying to refactor so that I can bind and make use of a IVehicleFactory<T>, where T is constrained to ICar, rather than the previous ICarFactory. This way, I can specify the vehicle I want in the generic type parameter, instead of passing in the name of the vehicle type in the factory's CreateCar() method.
Is it possible to bind open generic interfaces using the ToFactory() technique?
I have a feeling that I'm barking up the wrong tree, but when I was specifying an ICar type by its name, it seemed like the natural evolution to specify the ICar type itself as a generic type parameter...
Here's the test that currently fails:
[Fact]
public void A_Generic_Vehicle_Factory_Creates_A_Car_Whose_Type_Equals_Factory_Method_Generic_Type_Argument()
{
using (StandardKernel kernel = new StandardKernel())
{
// arrange
kernel.Bind(typeof(IVehicleFactory<>))
.ToFactory(() => new UseFirstGenericTypeArgumentInstanceProvider());
kernel.Bind(
scanner => scanner
.FromThisAssembly()
.SelectAllClasses()
.InheritedFrom<ICar>()
.BindWith(new BaseTypeBindingGenerator<ICar>()));
IVehicleFactory<Mercedes> factory
= kernel.Get<IVehicleFactory<Mercedes>>();
// act
var car = factory.CreateVehicle();
// assert
Assert.IsType<Mercedes>(car);
}
}
And the InvalidCastException thrown:
System.InvalidCastException was unhandled by user code
Message=Unable to cast object of type 'Castle.Proxies.ObjectProxy' to type 'IVehicleFactory`1[Mercedes]'.
Source=System.Core
StackTrace:
at System.Linq.Enumerable.<CastIterator>d__b1`1.MoveNext()
at System.Linq.Enumerable.Single[TSource](IEnumerable`1 source)
at Ninject.ResolutionExtensions.Get[T](IResolutionRoot root, IParameter[] parameters) in c:\Projects\Ninject\ninject\src\Ninject\Syntax\ResolutionExtensions.cs:line 37
at NinjectFactoryTests.A_Generic_Vehicle_Factory_Creates_A_Car_Whose_Type_Name_Equals_Factory_Method_String_Argument() in C:\Programming\Ninject.Extensions.Conventions.Tests\NinjectFactoryTests.cs:line 37
InnerException:
And the factory interface:
public interface IVehicleFactory<T> where T : ICar
{
T CreateVehicle();
}
And the custom instance provider, whose breakpoints I can't even get the debugger to stop on, so I really don't know what's going on in there:
public class UseFirstGenericTypeArgumentInstanceProvider : StandardInstanceProvider
{
protected override string GetName(MethodInfo methodInfo, object[] arguments)
{
var genericTypeArguments = methodInfo.GetGenericArguments();
var genericMethodDefinition = methodInfo.GetGenericMethodDefinition();
var g = genericMethodDefinition.MakeGenericMethod(genericTypeArguments.First());
return g.MemberType.GetType().Name;
}
protected override ConstructorArgument[] GetConstructorArguments(MethodInfo methodInfo, object[] arguments)
{
return base.GetConstructorArguments(methodInfo, arguments).Skip(1).ToArray();
}
}
EDIT 1 - Change IVehicleFactory signature and custom instance provider
Here's I've changed the IVehicleFactory signature to use a generic Create<T>() method, and explicitly bound Mercedes to itself.
public interface IVehicleFactory
{
T CreateVehicle<T>() where T : ICar;
}
And the new custom instance provider which returns the name of the first generic type parameter:
public class UseFirstGenericTypeArgumentInstanceProvider : StandardInstanceProvider
{
protected override string GetName(MethodInfo methodInfo, object[] arguments)
{
var genericTypeArguments = methodInfo.GetGenericArguments();
return genericTypeArguments[0].Name;
}
}
Here's the new test, still not passing:
[Fact]
public void A_Generic_Vehicle_Factory_Creates_A_Car_Whose_Type_Name_Equals_Factory_Method_String_Argument()
{
using (StandardKernel kernel = new StandardKernel())
{
// arrange
kernel.Bind<IVehicleFactory>()
.ToFactory(() => new UseFirstGenericTypeArgumentInstanceProvider())
.InSingletonScope();
kernel.Bind<Mercedes>().ToSelf();
IVehicleFactory factory = kernel.Get<IVehicleFactory>();
// act
var car = factory.CreateVehicle<Mercedes>();
// assert
Assert.IsType<Mercedes>(car);
}
}
}
A Ninject.ActivationException is thrown:
Ninject.ActivationException: Error activating Mercedes
No matching bindings are available, and the type is not self-bindable.
Activation path:
1) Request for Mercedes
I don't know why it can't find the Mercedes class, since I explicitly self-bound it. Can you spot what I'm doing wrong?
Use generic methods:
public interface IVehicleFactory
{
CreateVehicle<T>();
}