Skip to content

Instantly share code, notes, and snippets.

@nickman
Created March 16, 2018 11:41
Show Gist options
  • Save nickman/647e1fbd52112ab3b15147cd99bbcd88 to your computer and use it in GitHub Desktop.
Save nickman/647e1fbd52112ab3b15147cd99bbcd88 to your computer and use it in GitHub Desktop.
Example of adding annotations at runtime
package com.heliosapm.aop.retransformer;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.Method;
import java.security.ProtectionDomain;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.LoaderClassPath;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.ClassFile;
import javassist.bytecode.ConstPool;
import javassist.bytecode.annotation.AnnotationMemberValue;
import javassist.bytecode.annotation.ArrayMemberValue;
import javassist.bytecode.annotation.BooleanMemberValue;
import javassist.bytecode.annotation.ByteMemberValue;
import javassist.bytecode.annotation.CharMemberValue;
import javassist.bytecode.annotation.ClassMemberValue;
import javassist.bytecode.annotation.DoubleMemberValue;
import javassist.bytecode.annotation.EnumMemberValue;
import javassist.bytecode.annotation.FloatMemberValue;
import javassist.bytecode.annotation.IntegerMemberValue;
import javassist.bytecode.annotation.LongMemberValue;
import javassist.bytecode.annotation.MemberValue;
import javassist.bytecode.annotation.ShortMemberValue;
import javassist.bytecode.annotation.StringMemberValue;
import net.bytebuddy.agent.ByteBuddyAgent;
import javax.xml.ws.soap.Addressing;
import javax.xml.ws.soap.AddressingFeature.Responses;
/**
* <p>Title: AnnotationBuilder</p>
* <p>Description: Fluent style annotation builder</p>
* <p>Company: Helios Development Group LLC</p>
* @author Whitehead (nwhitehead AT heliosdev DOT org)
* <p><code>com.heliosapm.aop.retransformer.AnnotationBuilder</code></p>
*/
public class AnnotationBuilder {
/** A map of named member values keyed by the element type */
protected final Map<String, MemberValueFactory> members = new HashMap<String, MemberValueFactory>();
/** A set of the member names on the annotation type */
protected final Set<String> memberNames;
/** The member definitions keyed by the member name */
final Map<String, MemberDef<?>> defs;
/** The element types supported by the annotation */
final Set<ElementType> elementTypes;
/** The annotation class we're applying */
final Class<? extends Annotation> annotationClass;
/** Indicates if this annotation has runtime visibility */
final boolean visible;
/** The parent annotation builder stack in a nested annotation builder used when we're adding an annotation which itself needs an annotation builder */
protected final Stack<NamedAnnotationBuilder> annotationBuilderStack = new Stack<NamedAnnotationBuilder>();
private class NamedAnnotationBuilder {
/** The intended annotation's annotation builder */
final AnnotationBuilder annotationBuilder;
/** The intended annotation's name */
final String name;
/**
* Creates a new NamedAnnotationBuilder
* @param name The intended annotation's name
* @param annotationBuilder The intended annotation's annotation builder
*/
public NamedAnnotationBuilder(final String name, final AnnotationBuilder annotationBuilder) {
this.annotationBuilder = annotationBuilder;
this.name = name;
}
}
public interface MemberValueFactory {
public MemberValue forValue(final ConstPool pool);
}
public static final Set<Class> ALLOWED_TYPES = Collections.unmodifiableSet(new HashSet<Class>(Arrays.asList(
new Class[]{
byte.class, byte[].class, boolean.class, boolean[].class, short.class, short[].class,
char.class, char[].class, int.class, int[].class, float.class, float[].class, long.class,
long[].class, double.class, double[].class,
String.class, Enum.class, Annotation.class, Class.class,
String[].class, Enum[].class, Annotation[].class, Class[].class
}
)));
public static final Set<Class> ALLOWED_NON_FINAL_TYPES = Collections.unmodifiableSet(new HashSet<Class>(Arrays.asList(
new Class[]{
Enum.class, Annotation.class
}
)));
public static final Map<Class, Class> P2O;
public static final Map<Class, Class> O2P;
/** Primitive type to Jvaassist CtClass type decode */
public static final Map<Class, CtClass> P2J;
static {
final Map<Class, Class> o2p = new HashMap<Class, Class>();
final Map<Class, Class> p2o = new HashMap<Class, Class>();
final Map<Class, CtClass> p2j = new HashMap<Class, CtClass>();
// ========= full object --> primitive
o2p.put(java.lang.Byte.class, byte.class);
o2p.put(java.lang.Boolean.class, boolean.class);
o2p.put(java.lang.Short.class, short.class);
o2p.put(java.lang.Integer.class, int.class);
o2p.put(java.lang.Character.class, char.class);
o2p.put(java.lang.Float.class, float.class);
o2p.put(java.lang.Long.class, long.class);
o2p.put(java.lang.Double.class, double.class);
o2p.put(java.lang.Byte[].class, byte[].class);
o2p.put(java.lang.Boolean[].class, boolean[].class);
o2p.put(java.lang.Short[].class, short[].class);
o2p.put(java.lang.Character[].class, char[].class);
o2p.put(java.lang.Integer[].class, int[].class);
o2p.put(java.lang.Float[].class, float[].class);
o2p.put(java.lang.Long[].class, long[].class);
o2p.put(java.lang.Double[].class, double[].class);
// ========= primitive --> full object
p2o.put(byte.class, java.lang.Byte.class);
p2o.put(boolean.class, java.lang.Boolean.class);
p2o.put(short.class, java.lang.Short.class);
p2o.put(char.class, java.lang.Character.class);
p2o.put(int.class, java.lang.Integer.class);
p2o.put(float.class, java.lang.Float.class);
p2o.put(long.class, java.lang.Long.class);
p2o.put(double.class, java.lang.Double.class);
p2o.put(byte[].class, java.lang.Byte[].class);
p2o.put(boolean[].class, java.lang.Boolean[].class);
p2o.put(short[].class, java.lang.Short[].class);
p2o.put(char[].class, java.lang.Character[].class);
p2o.put(int[].class, java.lang.Integer[].class);
p2o.put(float[].class, java.lang.Float[].class);
p2o.put(long[].class, java.lang.Long[].class);
p2o.put(double[].class, java.lang.Double[].class);
// ========= primitive --> CtClass primtive
p2j.put(byte.class, CtClass.byteType);
p2j.put(boolean.class, CtClass.booleanType);
p2j.put(short.class, CtClass.shortType);
p2j.put(char.class, CtClass.charType);
p2j.put(int.class, CtClass.intType);
p2j.put(float.class, CtClass.floatType);
p2j.put(long.class, CtClass.longType);
p2j.put(double.class, CtClass.doubleType);
p2j.put(void.class, CtClass.voidType);
O2P = Collections.unmodifiableMap(new HashMap<Class, Class>(o2p));
P2O = Collections.unmodifiableMap(new HashMap<Class, Class>(p2o));
P2J = Collections.unmodifiableMap(new HashMap<Class, CtClass>(p2j));
}
/**
* Creates a new AnnotationBuilder
* @param annotationClassName The class name of the annotation to build
* @param classLoader The optional class loader
* @return an annotation builder
*/
public static AnnotationBuilder newBuilder(final String annotationClassName, final ClassLoader classLoader) {
if(annotationClassName==null || annotationClassName.trim().isEmpty()) throw new IllegalArgumentException("The passed class name was null or empty");
try {
@SuppressWarnings("unchecked")
Class<Annotation> annotationClass = (Class<Annotation>) (classLoader==null ?
Class.forName(annotationClassName, true, Thread.currentThread().getContextClassLoader()) :
Class.forName(annotationClassName, true, classLoader));
return new AnnotationBuilder(annotationClass);
} catch (Exception ex) {
throw new RuntimeException("Failed to load class [" + annotationClassName + "]", ex);
}
}
/**
* Creates a new AnnotationBuilder loading the annotation class using the calling thread's context classloader
* @param annotationClassName The class name of the annotation to build
* @return an annotation builder
*/
public static AnnotationBuilder newBuilder(final String annotationClassName) {
return newBuilder(annotationClassName, null);
}
/**
* Creates a new AnnotationBuilder for the passed annotation type
* @param annotationClass The annotation type
* @return an annotation builder
*/
public static AnnotationBuilder newBuilder(final Class<? extends Annotation> annotationClass) {
return new AnnotationBuilder(annotationClass);
}
/**
* Creates a new AnnotationBuilder
* @param annotationClass The annotation class to build
*/
private AnnotationBuilder(final Class<? extends Annotation> annotationClass) {
this.annotationClass = annotationClass;
final Retention retention = this.annotationClass.getAnnotation(Retention.class);
visible = (retention!=null && retention.value().equals(RetentionPolicy.RUNTIME));
final Method[] methods = annotationClass.getDeclaredMethods();
final Set<String> mn = new HashSet<String>(methods.length);
for(Method m: methods) {
mn.add(m.getName());
}
memberNames = Collections.unmodifiableSet(mn);
log("Member Names: " + memberNames);
defs = MemberDef.getMemberDefs(annotationClass);
final Target target = annotationClass.getAnnotation(Target.class);
Set<ElementType> ets = EnumSet.noneOf(ElementType.class);
if(target!=null) {
for(ElementType et: target.value()) {
ets.add(et);
}
}
this.elementTypes = Collections.unmodifiableSet(ets);
}
/**
* Applies the built annotation to the passed CtClass instances
* @param clazzes The CtClasses to apply the annotation to
*/
public void applyTo(final CtClass...clazzes) {
if(!annotationBuilderStack.isEmpty()) throw new IllegalStateException("The annotation builder stack is not empty");
for(CtClass clazz: clazzes) {
final ClassFile classFile = clazz.getClassFile();
final ConstPool constPool = classFile.getConstPool();
final javassist.bytecode.annotation.Annotation annot = new javassist.bytecode.annotation.Annotation(this.annotationClass.getName(), constPool);
final AnnotationsAttribute attr = new AnnotationsAttribute(constPool, visible ? AnnotationsAttribute.visibleTag : AnnotationsAttribute.invisibleTag);
for(Map.Entry<String, MemberValueFactory> entry: members.entrySet()) {
annot.addMemberValue(entry.getKey(), entry.getValue().forValue(constPool));
}
attr.addAnnotation(annot);
classFile.addAttribute(attr);
}
}
/**
* Adds a new member name and value to the builder
* @param name The member name
* @param value The member value
* @return this annotation builder
*/
public AnnotationBuilder add(final String name, final Class<?> value) {
validate(name, value);
members.put(name, new MemberValueFactory() {
@Override
public MemberValue forValue(final ConstPool pool) {
return new ClassMemberValue(value.getName(), pool);
}
});
return this;
}
/**
* Adds a new member name and value to the builder
* @param name The member name
* @param value The member value
* @return this annotation builder
*/
public AnnotationBuilder add(final String name, final byte value) {
validate(name, value);
members.put(name, new MemberValueFactory() {
@Override
public MemberValue forValue(final ConstPool pool) {
return new ByteMemberValue(value, pool);
}
});
return this;
}
/**
* Adds a new member name and value to the builder
* @param name The member name
* @param value The member value
* @return this annotation builder
*/
public AnnotationBuilder add(final String name, final byte[] value) {
validate(name, value);
members.put(name, new MemberValueFactory() {
@Override
public MemberValue forValue(final ConstPool pool) {
final ArrayMemberValue amv = new ArrayMemberValue(pool);
final MemberValue[] mvs = new MemberValue[value.length];
for(int i = 0; i < value.length; i++) {
mvs[i] = new ByteMemberValue(value[i], pool);
}
amv.setValue(mvs);
return amv;
}
});
return this;
}
/**
* Adds a new member name and value to the builder
* @param name The member name
* @param value The member value
* @return this annotation builder
*/
public AnnotationBuilder add(final String name, final boolean value) {
validate(name, value);
members.put(name, new MemberValueFactory() {
@Override
public MemberValue forValue(final ConstPool pool) {
return new BooleanMemberValue(value, pool);
}
});
return this;
}
/**
* Adds a new member name and value to the builder
* @param name The member name
* @param value The member value
* @return this annotation builder
*/
public <E extends Enum<E>> AnnotationBuilder add(final String name, final E value) {
validate(name, value);
members.put(name, new MemberValueFactory() {
@Override
public MemberValue forValue(final ConstPool pool) {
final EnumMemberValue emv = new EnumMemberValue(pool);
emv.setType(value.getDeclaringClass().getName());
emv.setValue(value.name());
return emv;
}
});
return this;
}
/**
* Adds a new member name and value to the builder
* @param name The member name
* @param value The member value
* @return this annotation builder
*/
public <E extends Enum<E>> AnnotationBuilder add(final String name, final E[] value) {
validate(name, value);
members.put(name, new MemberValueFactory() {
@Override
public MemberValue forValue(final ConstPool pool) {
final ArrayMemberValue amv = new ArrayMemberValue(pool);
final EnumMemberValue[] emvs = new EnumMemberValue[value.length];
final Class<E> enumType = value[0].getDeclaringClass();
for(int i = 0; i < value.length; i++) {
final EnumMemberValue emv = new EnumMemberValue(pool);
emv.setType(enumType.getName());
emv.setValue(value[i].name());
}
amv.setValue(emvs);
return amv;
}
});
return this;
}
/**
* Adds a new member name and value to the builder
* @param name The member name
* @param value The member value
* @return this annotation builder
*/
public AnnotationBuilder add(final String name, final boolean[] value) {
validate(name, value);
members.put(name, new MemberValueFactory() {
@Override
public MemberValue forValue(final ConstPool pool) {
final ArrayMemberValue amv = new ArrayMemberValue(pool);
final MemberValue[] mvs = new MemberValue[value.length];
for(int i = 0; i < value.length; i++) {
mvs[i] = new BooleanMemberValue(value[i], pool);
}
amv.setValue(mvs);
return amv;
}
});
return this;
}
/**
* Adds a new member name and value to the builder
* @param name The member name
* @param value The member value
* @return this annotation builder
*/
public AnnotationBuilder add(final String name, final short value) {
validate(name, value);
members.put(name, new MemberValueFactory() {
@Override
public MemberValue forValue(final ConstPool pool) {
return new ShortMemberValue(value, pool);
}
});
return this;
}
/**
* Adds a new member name and value to the builder
* @param name The member name
* @param value The member value
* @return this annotation builder
*/
public AnnotationBuilder add(final String name, final short[] value) {
validate(name, value);
members.put(name, new MemberValueFactory() {
@Override
public MemberValue forValue(final ConstPool pool) {
final ArrayMemberValue amv = new ArrayMemberValue(pool);
final MemberValue[] mvs = new MemberValue[value.length];
for(int i = 0; i < value.length; i++) {
mvs[i] = new ShortMemberValue(value[i], pool);
}
amv.setValue(mvs);
return amv;
}
});
return this;
}
/**
* Adds a new member name and value to the builder
* @param name The member name
* @param value The member value
* @return this annotation builder
*/
public AnnotationBuilder add(final String name, final int value) {
validate(name, value);
members.put(name, new MemberValueFactory() {
@Override
public MemberValue forValue(final ConstPool pool) {
return new IntegerMemberValue(value, pool);
}
});
return this;
}
/**
* Adds a new member name and value to the builder
* @param name The member name
* @param value The member value
* @return this annotation builder
*/
public AnnotationBuilder add(final String name, final int[] value) {
validate(name, value);
members.put(name, new MemberValueFactory() {
@Override
public MemberValue forValue(final ConstPool pool) {
final ArrayMemberValue amv = new ArrayMemberValue(pool);
final MemberValue[] mvs = new MemberValue[value.length];
for(int i = 0; i < value.length; i++) {
mvs[i] = new IntegerMemberValue(value[i], pool);
}
amv.setValue(mvs);
return amv;
}
});
return this;
}
/**
* Adds a new member name and value to the builder
* @param name The member name
* @param value The member value
* @return this annotation builder
*/
public AnnotationBuilder add(final String name, final float value) {
validate(name, value);
members.put(name, new MemberValueFactory() {
@Override
public MemberValue forValue(final ConstPool pool) {
return new FloatMemberValue(value, pool);
}
});
return this;
}
/**
* Adds a new member name and value to the builder
* @param name The member name
* @param value The member value
* @return this annotation builder
*/
public AnnotationBuilder add(final String name, final float[] value) {
validate(name, value);
members.put(name, new MemberValueFactory() {
@Override
public MemberValue forValue(final ConstPool pool) {
final ArrayMemberValue amv = new ArrayMemberValue(pool);
final MemberValue[] mvs = new MemberValue[value.length];
for(int i = 0; i < value.length; i++) {
mvs[i] = new FloatMemberValue(value[i], pool);
}
amv.setValue(mvs);
return amv;
}
});
return this;
}
/**
* Adds a new member name and value to the builder
* @param name The member name
* @param value The member value
* @return this annotation builder
*/
public AnnotationBuilder add(final String name, final long value) {
validate(name, value);
members.put(name, new MemberValueFactory() {
@Override
public MemberValue forValue(final ConstPool pool) {
return new LongMemberValue(value, pool);
}
});
return this;
}
/**
* Adds a new member name and value to the builder
* @param name The member name
* @param value The member value
* @return this annotation builder
*/
public AnnotationBuilder add(final String name, final long[] value) {
validate(name, value);
members.put(name, new MemberValueFactory() {
@Override
public MemberValue forValue(final ConstPool pool) {
final ArrayMemberValue amv = new ArrayMemberValue(pool);
final MemberValue[] mvs = new MemberValue[value.length];
for(int i = 0; i < value.length; i++) {
mvs[i] = new LongMemberValue(value[i], pool);
}
amv.setValue(mvs);
return amv;
}
});
return this;
}
/**
* Starts a new annotation to build an annotation to add to an annotatable target
* @param name The name of the annotation
* @param value The annotation class
* @return the new annotation builder
*/
public AnnotationBuilder addAnnotation(final String name, final Class<? extends Annotation> value) {
validate(name, value);
final AnnotationBuilder inner = new AnnotationBuilder(value);
inner.annotationBuilderStack.push(new NamedAnnotationBuilder("parent", this));
this.annotationBuilderStack.push(new NamedAnnotationBuilder(name, inner));
return inner;
}
/**
* Adds the current annotation builder to the parent and pops the parent back into the current context
* @return the parent annotation builder
*/
public AnnotationBuilder endAnnotation() {
if(this.annotationBuilderStack.isEmpty()) throw new IllegalStateException("No annotation builder in state. Did you call addAnnotation first ?");
final NamedAnnotationBuilder parent = this.annotationBuilderStack.pop();
if(!"parent".equals(parent.name)) throw new IllegalStateException("The annotation builder in state was named [" + parent.name + "] but expected [parent]");
if(parent.annotationBuilder.annotationBuilderStack.isEmpty()) throw new IllegalStateException("No annotation builder in parent state. Did you call addAnnotation first ?");
final NamedAnnotationBuilder self = parent.annotationBuilder.annotationBuilderStack.pop();
if(self.annotationBuilder != this) throw new IllegalStateException("The popped NamedAnnotationBuilder did not contain the expected annotation builder");
members.put(self.name, new MemberValueFactory(){
@Override
public MemberValue forValue(final ConstPool pool) {
final javassist.bytecode.annotation.Annotation annot = new javassist.bytecode.annotation.Annotation(self.annotationBuilder.annotationClass.getName(), pool);
for(Map.Entry<String, MemberValueFactory> entry: self.annotationBuilder.members.entrySet()) {
annot.addMemberValue(entry.getKey(), entry.getValue().forValue(pool));
}
return new AnnotationMemberValue(annot, pool);
}
});
return parent.annotationBuilder;
}
/**
* Adds a new member name and value to the builder
* @param name The member name
* @param value The member value
* @return this annotation builder
*/
public AnnotationBuilder add(final String name, final double value) {
validate(name, value);
members.put(name, new MemberValueFactory() {
@Override
public MemberValue forValue(final ConstPool pool) {
return new DoubleMemberValue(value, pool);
}
});
return this;
}
/**
* Adds a new member name and value to the builder
* @param name The member name
* @param value The member value
* @return this annotation builder
*/
public AnnotationBuilder add(final String name, final double[] value) {
validate(name, value);
members.put(name, new MemberValueFactory() {
@Override
public MemberValue forValue(final ConstPool pool) {
final ArrayMemberValue amv = new ArrayMemberValue(pool);
final MemberValue[] mvs = new MemberValue[value.length];
for(int i = 0; i < value.length; i++) {
mvs[i] = new DoubleMemberValue(value[i], pool);
}
amv.setValue(mvs);
return amv;
}
});
return this;
}
/**
* Adds a new member name and value to the builder
* @param name The member name
* @param value The member value
* @return this annotation builder
*/
public AnnotationBuilder add(final String name, final char value) {
validate(name, value);
members.put(name, new MemberValueFactory() {
@Override
public MemberValue forValue(final ConstPool pool) {
return new CharMemberValue(value, pool);
}
});
return this;
}
/**
* Adds a new member name and value to the builder
* @param name The member name
* @param value The member value
* @return this annotation builder
*/
public AnnotationBuilder add(final String name, final char[] value) {
validate(name, value);
members.put(name, new MemberValueFactory() {
@Override
public MemberValue forValue(final ConstPool pool) {
final ArrayMemberValue amv = new ArrayMemberValue(pool);
final MemberValue[] mvs = new MemberValue[value.length];
for(int i = 0; i < value.length; i++) {
mvs[i] = new CharMemberValue(value[i], pool);
}
amv.setValue(mvs);
return amv;
}
});
return this;
}
/**
* Adds a new member name and value to the builder
* @param name The member name
* @param value The member value
* @return this annotation builder
*/
public AnnotationBuilder add(final String name, final String value) {
validate(name, value);
members.put(name, new MemberValueFactory() {
@Override
public MemberValue forValue(final ConstPool pool) {
return new StringMemberValue(value, pool);
}
});
return this;
}
/**
* Adds a new member name and value to the builder
* @param name The member name
* @param value The member value
* @return this annotation builder
*/
public AnnotationBuilder add(final String name, final String[] value) {
validate(name, value);
members.put(name, new MemberValueFactory() {
@Override
public MemberValue forValue(final ConstPool pool) {
final ArrayMemberValue amv = new ArrayMemberValue(pool);
final MemberValue[] mvs = new MemberValue[value.length];
for(int i = 0; i < value.length; i++) {
mvs[i] = new StringMemberValue(value[i], pool);
}
amv.setValue(mvs);
return amv;
}
});
return this;
}
public void validate(final String name, final Object value) {
if(name==null || name.trim().isEmpty()) throw new IllegalArgumentException("Member name was null or empty");
if(!memberNames.contains(name)) throw new IllegalArgumentException("Invalid member name [" + name + "]");
if(value==null) throw new IllegalArgumentException("The value passed was null for Member name [" + name + "]");
final Class<?> type = value.getClass();
for(MemberDef md: defs.values()) {
if(md.match(name, type)) return;
}
throw new IllegalArgumentException("Invalid type [" + value.getClass() + "] for member name [" + name + "]");
}
class TestClass {
}
public static void main(String[] agrs) {
log("Quickie Test");
AnnotationBuilder ab = newBuilder("javax.xml.ws.soap.Addressing")
.add("enabled", false)
.add("required", true)
.add("responses", Responses.NON_ANONYMOUS);
try {
final ClassPool cp = new ClassPool();
cp.appendSystemPath();
cp.appendClassPath(new LoaderClassPath(TestClass.class.getClassLoader()));
final CtClass ctclazz = cp.get(TestClass.class.getName());
ab.applyTo(ctclazz);
final byte[] byteCode = ctclazz.toBytecode();
final String target = TestClass.class.getName().replace('.', '/');
log("Target:" + target);
final ClassFileTransformer cft = new ClassFileTransformer() {
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
log("ClassName:" + className);
if(target.equals(className)) {
log("Match !");
return byteCode;
}
return null;
}
};
// ==============================================================================
// There's several ways to acquire an Instrumentation instance
// This one is the easiest
// ==============================================================================
Instrumentation ins = ByteBuddyAgent.install();
try {
ins.addTransformer(cft, true);
ins.retransformClasses(TestClass.class);
} finally {
ins.removeTransformer(cft);
}
log("Done");
Addressing addr = TestClass.class.getAnnotation(Addressing.class);
log("Annotation Present:" + addr!=null);
if(addr!=null) {
log("Enabled:" + addr.enabled());
log("Required:" + addr.required());
log("Responses:" + addr.responses().name());
}
} catch (Exception ex) {
ex.printStackTrace(System.err);
}
}
public static void log(Object msg) {
System.out.println(msg);
}
protected static class MemberDef<T> {
/** The member name */
final String name;
/** The annotation member type */
final Class<T> type;
/** The annotation member type upped alt */
final Class<T> otype;
/** The annotation member default value */
final T value;
/**
* Returns an array of member definitions for the passed annotation type
* @param type The annotation type
* @return the array of member definitions
*/
@SuppressWarnings("unchecked")
public static <T extends Annotation> Map<String, MemberDef<?>> getMemberDefs(final Class<T> type) {
final Method[] methods = type.getDeclaredMethods();
final Map<String, MemberDef<?>> map = new HashMap<String, MemberDef<?>>(methods.length);
for(Method method: methods) {
map.put(method.getName(), new MemberDef(method.getName(), method.getReturnType(), method.getDefaultValue()));
}
return Collections.unmodifiableMap(map);
}
/**
* Creates a new MemberDef
* @param name The member name
* @param type The annotation member type
* @param value The annotation member default value
*/
public MemberDef(final String name, final Class<T> type, final T value) {
this.name = name;
this.type = type;
this.value = value;
this.otype = P2O.get(type);
}
/**
* Tests the passed member name and value to see if it is a match for this member def
* @param name The member name
* @param valueType The member value type
* @return true for a match, false otherwise
*/
public boolean match(final String name, final Class<?> valueType) {
if(name==null || name.trim().isEmpty()) throw new IllegalArgumentException("The member name was null or empty");
if(value==null) throw new IllegalArgumentException("The member type was null");
if(!this.name.equals(name)) return false;
return (valueType.equals(this.type) || valueType.equals(this.otype));
}
/**
* Returns the member name
* @return the name
*/
public String getName() {
return name;
}
/**
* Returns the annotation member type
* @return the annotation member type
*/
public Class<T> getType() {
return type;
}
/**
* Returns the annotation member value
* @return the annotation member value or null if no default is defined
*/
public T getValue() {
return value;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment