Interception on constructor causes ClassNotFoundException - java

I'm trying to intercept constructors annotated with #Inject. That worked fine in the context of a small unit test. However in the context of a DI container like Spring it fails with a ClassNotFoundException.
I managed to narrow down on the root cause. Calling getDeclaredConstructors on the instrumented class will trigger this exception. Interestingly enough, if we first create an instance of that class, the problem disappears.
For example:
public class InterceptConstructorTest {
#Test
public void testConstructorInterception() throws ClassNotFoundException {
ByteBuddyAgent.install();
new AgentBuilder.Default().type(nameStartsWith("test")).transform(new AgentBuilder.Transformer() {
#Override
public Builder<?> transform(Builder<?> builder, TypeDescription td) {
return builder.constructor(isAnnotatedWith(Inject.class))
.intercept(SuperMethodCall.INSTANCE.andThen(MethodDelegation.to(ConstructorInterceptor.class)));
}
}).installOnByteBuddyAgent();
// If this line is uncommented, ClassNotFoundException won't be thrown
// MyClass myClass = new MyClass("a param");
// Manually load MyClass
Class<?> myClassDefinition = getClass().getClassLoader().loadClass("test.MyClass");
// Throws NoClassDefFoundError
for(Constructor<?> constructor : myClassDefinition.getDeclaredConstructors()) {
System.out.println(constructor);
}
}
}
The stack stack trace can be found: http://pastebin.com/1zhx3fVX
class MyClass {
#Inject
public MyClass(String aParam) {
System.out.println("constructor called");
}
}
class ConstructorInterceptor {
public static void intercept() {
System.out.println("Intercepted");
}
}

The problem in this case is the constructor injection. In order to rebase a constructor, Byte Buddy needs to create an additional type and creates a class like the following:
class MyClass {
private synthetic MyClass(String aParam, $SomeType ignored) {
System.out.println("constructor called");
}
#Inject
public MyClass(String aParam) {
this(aParam, null);
// Instrumentation logic.
}
}
The additional type is unfortunately necessary to create a unique signature for the rebased constructors. With methods, Byte Buddy can rather change the name but for constructors that is not possible as they must be named <init> in the class file to be recognized as constructors.
Byte Buddy tries to only load auxiliary classes after a type was instrumented. Depending on the virtual machine, loading a class that references another class causes the loading of the referenced type. If this type is the instrumented class, the instrumentation aborts the ongoing instrumentation for the circularity.
Therefore, Byte Buddy makes sure that any auxiliary type is only loaded at the first possible point after it can be sure that the instrumented type is loaded. And it does this by adding a self-initialization into the instrumented class's class initializer. In a way, Byte Buddy adds a block:
static {
ByteBuddy.loadAuxiliaryTypes(MyClass.class);
}
If this block is not executed before reflecting on the class, the auxiliary type is not loaded and the exception you encounter is thrown. If you called:
Class.forName("test.MyClass", true, getClass().getClassLoader());
instead of loadClass, the problem would not occur where the second parameter indicates to execute the class initializer eagerly. Also, the initializer is executed if you create an instance.
Of course, this is not satisfactory, I am now adding some logic to decide for an auxiliary type if it can be loaded during the instrumentation to avoid such errors.

Related

UnfinishedStubbingException when performing stubbing from a class other than the test class

I'm using PowerMock to mock a java.net.Inet4Address (amongst other things) to return a particular IP address (getHostAddress()), as well as whether or not it's loopback (isLoopbackAddress()). I find that if I perform the actual stubbing (PowerMock.doReturn(...).when(mock).methodToStub()) anywhere other than from within the test class or an immediately inner class I get an UnfinishedStubbingException.
The problem is most obvious if I try to perform two stubs. The first one passes without an exception, but the second one throws the exception because it thinks the first one was not finished. If I only perform the one stub then I see different errors depending on what I do after that, so it's definitely the first stub that's causing the problem.
Below is some code that demonstrates the problem.
TestClass.java
/* package, imports... */
#RunWith(PowerMockRunner.class)
#PrepareForTest({Inet4Address.class})
public class TestClass {
#Test
public void test() {
Inet4Address mocked = PowerMockito.mock(Inet4Address.class);
// Option 1: Do it from within this class - WORKS
doStubbing(mocked);
// Option 2: Do it from an inner class - WORKS
Inner.doStubbing(mocked);
// Option 3: Do it from an inner class of the inner class - FAILS
Inner.Deeper.doStubbing(mocked);
// Option 4: Do it from an entirely different class - FAILS
OtherClass.doStubbing(mocked);
}
private void doStubbing(Inet4Address mocked) {
PowerMockito.doReturn(true).when(mocked).isLoopbackAddress();
PowerMockito.doReturn("127.0.0.1").when(mocked).getHostAddress();
}
public static class Inner {
static void doStubbing(Inet4Address mocked) {
PowerMockito.doReturn(true).when(mocked).isLoopbackAddress();
PowerMockito.doReturn("127.0.0.1").when(mocked).getHostAddress();
}
public static class Deeper {
static void doStubbing(Inet4Address mocked) {
PowerMockito.doReturn(true).when(mocked).isLoopbackAddress();
PowerMockito.doReturn("127.0.0.1").when(mocked).getHostAddress();
}
}
}
}
OtherClass.java
/* package, imports... */
public class OtherClass {
public static void doStubbing(Inet4Address mocked) {
PowerMockito.doReturn(true).when(mocked).isLoopbackAddress();
PowerMockito.doReturn("127.0.0.1").when(mocked).getHostAddress();
}
}
I've put the creation of the mock at the start, common to all scenarios. It makes no difference if the mock is created from within the same class where the stubbing is being done. I've also made the methods static for ease of reading; the behaviour is the same if the classes are instantiated first.
I know there are workarounds so I can get my test working (perform the mocking right there in the test class, mock the InetAddress interface instead of the IPv4 implementation, etc) but I'd like to know why PowerMock is behaving in this way. I could almost understand it if it only worked from within the test class, but why does it work in an inner class as well?

ByteBuddy - Read class annotations in a java agent

I am trying to access annotations of a class before applying some transformation in a java agent implemented with ByteBuddy.
To access the annotations, I am trying to load the Class object, but it seems that this creates a duplicate class definition.
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.matcher.ElementMatchers;
import java.lang.instrument.Instrumentation;
public class SimpleTestAgent {
public static void premain(String arg, Instrumentation inst) {
new AgentBuilder.Default()
.type(ElementMatchers.isAnnotatedWith(SomeAnnotationType.class))
.transform((builder, type, classLoader, javaModule) -> {
try {
Class loadedClass = Class.forName(type.getName(), true, classLoader);
} catch (ClassNotFoundException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
return builder;
})
.installOn(inst);
}
}
A simple class that just creates an instance of the class TestClass that is annotated with the expected annotation, throws the following exception:
Exception in thread "main" java.lang.LinkageError: loader (instance of
sun/misc/Launcher$AppClassLoader): attempted duplicate class
definition for name: "TestClass"
public class AgentTest {
public static void main (String[] args) {
TestClass pet = new TestClass();
}
}
I am trying to implement the agent like in the example described in: Easily-Create-Java-Agents-with-ByteBuddy
Is there a way to load the Class object without causing this issue or a way to access the annotations using the arguments passed to the transformation() method?
The transformation should be able to implement new interfaces and not only override methods this is why I think I cannot use "ForAdvice".
UPDATE
The following loop finds the TestClass only after the Class.forName() is executed. This means that the class has not been loaded and so probably there is no hope to use Class.forName to get the annotations.
for (Class<?> t : inst.getAllLoadedClasses()) {
System.out.println("Class name: " + t.getName());
}
It is possible to get the annotations and the full information about a class using the net.bytebuddy.description.type.TypeDescription instance passed to the transform() method.
The problem is that, for example, I need the Method objects that can be called using reflection. It would be easier if I could somehow access the Class object for the class that is being transformed.
Byte Buddy exposes all information on annotations of a class via the TypeDescription API already, you should not load classes as a class that is loaded during a transformation loads this class before the transformation is applied and aborts class loading with the error you observe. Instead, implement your own matcher:
.type(type -> check(type.getDeclaredAnnotations().ofType(SomeAnnotation.class).load())
Byte Buddy will represent the annotation for you without loading the carrier class itself but rather represent it using its own class file processor.
You should make sure the annotation class is loaded before installing your agent builder to avoid a circularity for this exact annotation.

Avoiding Injection when loading instrumented classes

Let's say I want to create a custom class at runtime that another class can make use of.
package redefineconcept;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import java.lang.reflect.InvocationTargetException;
public class LoadingTest {
public static final String HelloWorldTag = "$HelloWorld";
public static void main(String[] args){
new LoadingTest().run();
}
private void run(){
InstanceUser u = new InstanceUser();
u.start();
Class <?> createdClass = createAndLoadFor(InstanceUser.class);
System.out.println(String.format("created new class %s", createdClass.getName()));
InstanceUser.canAccess = true;
try {
u.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private Class<?> createAndLoadFor(Class<?> clazz){
ByteBuddy byteBuddy = new ByteBuddy();
String newClassName = clazz.getName() + LoadingTest.HelloWorldTag;
DynamicType.Builder builder = byteBuddy
.subclass(Object.class)
.name(newClassName)
;
DynamicType.Unloaded<?> newType = builder.make();
return newType
.load(clazz.getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
.getLoaded();
}
}
class InstanceUser extends Thread{
public static volatile boolean canAccess = false;
Object instance;
#Override
public void run() {
while(!canAccess){}
String cn = this.getClass().getName() + LoadingTest.HelloWorldTag;
Class clazz;
try{
clazz = Class.forName(cn);
}catch(ClassNotFoundException e){
e.printStackTrace();
throw new RuntimeException();
}
try{
instance = clazz.getConstructor().newInstance();
}catch(NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e){
e.printStackTrace();
throw new RuntimeException();
}
}
}
This works.
However, the ByteBuddy tutorial, suggests
You might consider the chance of encountering circular dependencies to be of minor relevance since you are creating one dynamic type at a time. However, the dynamic creation of a type might trigger the creation of so-called auxiliary types.
These types are created by Byte Buddy automatically to provide access to the dynamic type you are creating.
because of this, we recommend you to load dynamically created classes by creating a specific ClassLoader instead of injecting them into an existing one, whenever possible.
I don't know terribly much about classloaders -- or ByteBuddy, for that matter -- but the tutorial seems to suggest that classloaders are hierachically ordered.
If so, it should be possbible to chain the new class loader to clazz.getClassLoader(), right?
Well, I've had no such luck with neither ClassLoadingStrategy.Default.WRAPPER nor ClassLoadingStrategy.Default.CHILD_FIRST.
Both result in
created new class redefineconcept.InstanceUser$HelloWorld
java.lang.ClassNotFoundException: redefineconcept.InstanceUser$HelloWorld
which led me to believe that
Normally, Java class loaders query their parent ClassLoader before attempting to directly load a type of a given name.
means they only query the parent ClassLoaders but not the children.
Is that so?
And is it at all possible to avoid using ClassLoadingStrategy.Default.INJECTION, here?
Class loaders are (normally) hierarchical. If you are using the INJECTION strategy, Byte Buddy manually defines type by type by explicitly defining the classes. Depending on the JVM and class loader, this might trigger a class loading.
Consider a situation where A references B and B references A. If Byte Buddy injects A before B, the injection of A might cause a loading of B which is not yet injected at that point. At this point, the class loader that is the target of the injection will prematurly and unsuccessfully try to look up B and fail with a NoClassDefFoundError.
When using the WRAPPER strategy, Byte Buddy creates a new class loader that is aware of both types and can look up B when A is loaded as no injection is required.
The problem you encounter is caused by your use of Class.forName(name). This method is caller sensitive meaning that the calling class's class loader is used. From your thread, this will most likely be the system class loader which is the same class loader you injected before.
That said, typically a JVM is loading types lazily and injection should not render a big problem for 99% of all use cases.

Code injection via custom annotation

Here's my use case:
I need to do some generic operation before and after each method of a given class, which is based on the parameter(s) of the method. For example:
void process(Processable object) {
LOGGER.log(object.getDesc());
object.process();
}
class BaseClass {
String method1(Object o){ //o may or may not be Processable(add process logic only in former case)
if(o intstanceof Prcessable){
LOGGER.log(object.getDesc());
object.process();
}
//method logic
}
}
My BaseClass has a lot of methods and I know for a fact that the same functionality will be added to several similar classes as well in future.
Is something like the following possible?
#MarkForProcessing
String method1(#Process Object o){
//method logic
}
PS: Can AspectJ/guice be used? Also want to know how to implement this from scratch for understanding.
Edit: Forgot to mention, what I have tried.(Not complete or working)
public #interface MarkForProcessing {
String getMetadata();
}
final public class Handler {
public boolean process(Object instance) throws Exception {
Class<?> clazz = instance.getClass();
for(Method m : clazz.getDeclaredMethods()) {
if(m.isAnnotationPresent(LocalSource.class)) {
LocalSource annotation = m.getAnnotation(MarkForProcessing.class);
Class<?> returnType = m.getReturnType();
Class<?>[] inputParamTypes = m.getParameterTypes();
Class<?> inputType = null;
// We are interested in just 1st param
if(inputParamTypes.length != 0) {
inputType = inputParamTypes[0];
}
// But all i have access to here is just the types, I need access to the method param.
}
return false;
}
return false;
}
Yes, it can be done. Yes, you can use AspectJ. No, Guice would only be tangentially related to this problem.
The traditional aspect approach creates a proxy which is basically a subclass of the class you've given it (e.g. a subclass of BaseClass) but that subclass is created at runtime. The subclass delegates to the wrapped class for all methods. However, when creating this new subclass you can specify some extra behavior to add before or after (or both) the call to the wrapped class. In other words, if you have:
public class Foo() {
public void doFoo() {...}
}
Then the dynamic proxy would be a subclass of Foo created at runtime that looks something like:
public class Foo$Proxy {
public void doFoo() {
//Custom pre-invocation code
super.doFoo();
//Custom post-invocation code
}
}
Actually creating a dynamic proxy is a magical process known as bytecode manipulation. If you want to to do that yourself you can use tools such as cglib or asm. Or you can use JDK dynamic proxies. The main downside to JDK proxies are that they can only wrap interfaces.
AOP tools like AspectJ provide an abstraction on top of the raw bytecode manipulation for doing the above (you can do a lot with bytecode manipulation, adding behavior before and after methods is all aspects allow). Typically they define 'Aspect's which are classes that have special methods called 'advice' along with a 'pointcut' which defines when to apply that advice. In other words you may have:
#Aspect
public class FooAspect {
#Around("#annotation(MarkForProcessing)")
public void doProcessing(final ProceedingJoinPoint joinPoint) throws Throwable
{
//Do some before processing
joinPoint.proceed(); //Invokes the underlying method
//Do some after processing
}
}
The aspect is FooAspect, the advice is doProcessing, and the pointcut is "#annotation(MarkForProcessing)" which matches all methods that are annotated with #MarkForProcessing. It's worth pointing out that the ProceedingJoinPoint will have a reference to the actual parameter values (unlike the java.lang.reflect.Method)
The last step is actually applying your aspect to an instance of your class. Typically this is either done with a container (e.g. Guice or Spring). Most containers have some way of knowing about a collection of aspects and when to apply them to classes constructed by that container. You can also do this programmatically. For example, with AspectJ you would do:
AspectJProxyFactory factory = new AspectJProxyFactory(baseClassInstance);
factory.addAspect(FooAspect.class);
BaseClass proxy = factory.getProxy();
Last, but not least, there are AOP implementations which use compile-time "weaving" which is a second compilation step run on the class files that applies the aspects. In other words, you don't have to do the above or use a container, the aspect will be injected into the class file itself.

package vs. protected protection with Java reflection

Why can I use reflection to instantiate a inner protected class, but not an inner class with package-level protection? I wouldn't think either would be accessible outside the package.
Consider the following example:
package dummy;
public class ClassContainer {
protected static class InnerProtected {
public InnerProtected() {}
}
static class InnerDefault {
public InnerDefault() {}
}
private class InnerPrivate {
public InnerPrivate() {}
}
}
package driver;
public class DriverClass {
public static void main(String[] args) throws Exception {
Class.forName("dummy.ClassContainer$InnerProtected").newInstance();
Class.forName("dummy.ClassContainer$InnerDefault").newInstance();
Class.forName("dummy.ClassContainer$InnerPrivate").newInstance();
}
}
Notice that the two classes are in different packages.
The first line in main (which instantiates InnerProtected) works.
The second line (which instantiates InnerDefault) throws this exception:
Exception in thread "main" java.lang.IllegalAccessException: Class driver.DriverClass can not access a member of class dummy.ClassContainer$InnerDefault with modifiers "public"
Since the driver is an a different package than the class definitions, shouldn't both attempts at instantiating the classes fail?
(For what it's worth: Attempting to instantiate InnerPrivate fails as I would expect:
Exception in thread "main" java.lang.InstantiationException: dummy.ClassContainer$InnerPrivate
Really, javap reports that InnerProtected is compiled as public, whereas other member classes are package-private.
I believe it's caused by the need to make it visible to subclasses of ClassContainer from different packages. Perhaps VM cannot handle access control rules in this case, so that they are handled at compiler level.
Note, however, that if you omit constuctor declarations for these classes their generated constructors would have their expected visililities, that is protected, default and private respectively.

Categories

Resources