package org.radargun.config; import java.lang.reflect.Method; import java.util.Arrays; import java.util.Iterator; import java.util.LinkedList; import org.radargun.logging.Log; import org.radargun.logging.LogFactory; /** * @author Radim Vansa <rvansa@redhat.com> */ public final class InitHelper { private static final Log log = LogFactory.getLog(InitHelper.class); private InitHelper() {} public static void init(Object target) { processAnnotatedMethods(target, Init.class, false); } public static void destroy(Object target) { processAnnotatedMethods(target, Destroy.class, true); } /** * Looks for annotated methods of given type in the class hierarchy of given object and invokes them in specified * order. The behavior is as follows: * * <ol> * <li>1. If overriden methods are detected, only the most specific one is invoked. This is generally aplicable.</li> * <li>2. If superclass contains annotated method, which is overriden in a sublclass and overriding method is not annotated, * invoke subclass method in place of superclass method (respecting superclass method priority).</li> * <li>2. If superclass contains annotated method, which is overriden in a sublclass and overriding method is annotated, * invoke subclass method only (respecting subclass method priority). Superclass method is not invoked</li> * </ol> * * e.g. * class A { * @Init * void foo() { * System.out.print("A"); * } * } * * class B extends A { * @Init * void fooB() { * System.out.print("B"); * } * } * * class C extends A { * @Override * void foo() { * System.out.print("C"); * } * } * *class D extends B { * @Init * @Override * void foo() { * System.out.print("D"); * } * } * * <ul> * <li>Invoking processAnnotatedMethods(new A(), Init.class, false) prints "A"</li> * <li>Invoking processAnnotatedMethods(new B(), Init.class, false) prints "AB"</li> * <li>Invoking processAnnotatedMethods(new C(), Init.class, false) prints "CB"</li> * <li>Invoking processAnnotatedMethods(new D(), Init.class, false) prints "BD"</li> * </ul> * * @param target Object to invoke annotated methods on * @param annotationClass Target annotation class to look for * @param specializedClassFirst If set to true, annotated methods will be invoked in bottom-up approach */ private static void processAnnotatedMethods(Object target, Class annotationClass, boolean specializedClassFirst) { if (target == null) throw new NullPointerException(); LinkedList<Method> annotatedMethods = new LinkedList<>(); Class<?> clazz = target.getClass(); while (clazz != null) { for (Method m : clazz.getDeclaredMethods()) { if (m.getAnnotation(annotationClass) != null) { boolean overridden = false; for (Method m2 : annotatedMethods) { if (m2.getName().equals(m.getName()) && Arrays.equals(m2.getGenericParameterTypes(), m.getGenericParameterTypes())) { log.warnf("Method %s overrides %s but both are declared with @%s annotation: calling only once", m2, m, annotationClass.getSimpleName()); overridden = true; } } m.setAccessible(true); if (!overridden) { annotatedMethods.add(m); } } } clazz = clazz.getSuperclass(); } Iterator<Method> iterator = specializedClassFirst ? annotatedMethods.listIterator() : annotatedMethods.descendingIterator(); while (iterator.hasNext()) { try { iterator.next().invoke(target); } catch (Exception e) { throw new RuntimeException(e); } } } }