Deepak Chaudhary
Deepak Chaudhary

Reputation: 162

bytebuddy - add class level annotation

I am trying to use byte-buddy to create java agent, to allow modify some classes at run time -

FirstSeleniumTest class already exist - I want to add an annotation as following:

@org.testng.annotations.Listeners(value = org.deployd.test.TestNgListener.class)
public class FirstSeleniumTest {...

This is my premain method in the agent:

new AgentBuilder.Default()
            .disableClassFormatChanges()
            .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
            .with(AgentBuilder.TypeStrategy.Default.REDEFINE)
            .type(ElementMatchers.nameContains("org.deployd"))
            .transform((builder, typeDescription, agr3, arg4) -> builder
                    .annotateType(AnnotationDescription.Builder.ofType(Listeners.class)
                            .define("value", TestNgListener.class)
                            .build()))
            .with(AgentBuilder.Listener.StreamWriting.toSystemOut())
            .installOn(instrumentation);

I get following error during execution:

[Byte Buddy] DISCOVERY org.deployd.test.FirstSeleniumTest 
[sun.misc.Launcher$AppClassLoader@18b4aac2, null, loaded=false]
[Byte Buddy] ERROR org.deployd.test.FirstSeleniumTest 
[sun.misc.Launcher$AppClassLoader@18b4aac2, null, loaded=false]
java.lang.IllegalArgumentException: class org.deployd.agent.TestNgListener cannot be 
assigned to value
at net.bytebuddy.description.annotation.AnnotationDescription$Builder.define(AnnotationDescription.java:860)
at net.bytebuddy.description.annotation.AnnotationDescription$Builder.define(AnnotationDescription.java:947)
at net.bytebuddy.description.annotation.AnnotationDescription$Builder.define(AnnotationDescription.java:935)
at org.deployd.agent.TestAgent.lambda$premain$0(TestAgent.java:41)

If I manually add the annotation:

@org.testng.annotations.Listeners(value = org.deployd.test.TestNgListener.class)
public class FirstSeleniumTest {...

then no error - meaning the value 'value' is correct for the given annotation.

anypointers as to what I might be missing trying to create class level annotations with byte-buddy for a class which already exits. Thank you.

Upvotes: 0

Views: 385

Answers (2)

Rafael Winterhalter
Rafael Winterhalter

Reputation: 44032

If you want to define an array property, you have to declare it as such:

AnnotationDescription.Builder.ofType(Listeners.class)
  .define("value", new Class<?>[] { TestNgListener.class })
  .build();

will work. Byte Buddy does not treat such arrays as varargs as the Java programming language does.

Upvotes: 1

Deepak Chaudhary
Deepak Chaudhary

Reputation: 162

on further analysis, I found the annotation value excepts an array vs single value of the type. I was able to resolve this via javassist as following:

AnnotationsAttribute annotationsAttribute = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
            Annotation annotation = new Annotation(Listeners.class.getName(), constPool);
            MemberValue[] memberValues = new MemberValue[]{new ClassMemberValue(TestNgListener.class.getName(), constPool)};
            ArrayMemberValue arrayMemberValue = new ArrayMemberValue(constPool);
            arrayMemberValue.setValue(memberValues);
            annotation.addMemberValue("value", arrayMemberValue);
            annotationsAttribute.addAnnotation(annotation);
            ctClass.getClassFile().addAttribute(annotationsAttribute);

Upvotes: 0

Related Questions