On Mon, Apr 28, 2008 at 6:27 AM, James Carman <[EMAIL PROTECTED]> wrote: > Yes, this would involve making up some rules about the order in which > the parent class' initializer methods are invoked. A couple that come > to mind: > > 1. There should only be one initializer method declared per class (to > avoid the which do we call first problem). > 2. Parent class' initializer methods should be invoked first. > > I don't agree that marking "lifecycle" methods with annotations is an > abuse (of annotations?) at all. Perhaps this wouldn't belong in the > "core", but I can see it as an extension or a wicketstuff project > maybe? >
I actually played around with this a bit. I've created an annotation: @Retention( RetentionPolicy.RUNTIME) @Target( ElementType.METHOD) public @interface Initialization { } Then, I created the listener: public class InitializationInstantiationListener implements IComponentInstantiationListener { //********************************************************************************************************************** // Fields //********************************************************************************************************************** private final Map<Class, Initializer> initializersMap = new HashMap<Class, Initializer>(); //********************************************************************************************************************** // IComponentInstantiationListener Implementation //********************************************************************************************************************** public void onInstantiation( Component component ) { Initializer initializer = getInitializer(component); initializer.initialize(component); } private synchronized Initializer getInitializer( Component component ) { final Class<? extends Component> componentClass = component.getClass(); Initializer initializer = initializersMap.get(componentClass); if( initializer == null ) { final List<Method> initializationMethods = getInitializationMethods(component); initializer = createInitializer(componentClass, initializationMethods); initializersMap.put(componentClass, initializer); } return initializer; } //********************************************************************************************************************** // Other Methods //********************************************************************************************************************** private synchronized List<Method> getInitializationMethods( Component component ) { List<Method> initializationMethods = new LinkedList<Method>(); Class c = component.getClass(); while( c != null ) { Method initializer = getInitializationMethod(c); if( initializer != null ) { initializationMethods.add(0, initializer); } c = c.getSuperclass(); } return initializationMethods; } private Method getInitializationMethod( Class c ) { Set<Method> initializers = new HashSet<Method>(); final Method[] declaredMethods = c.getDeclaredMethods(); for( Method declaredMethod : declaredMethods ) { if( declaredMethod.getAnnotation(Initialization.class) != null ) { if( Modifier.isPublic(declaredMethod.getModifiers()) && Void.TYPE.equals(declaredMethod.getReturnType()) && declaredMethod.getParameterTypes().length == 0 ) { initializers.add(declaredMethod); } else { throw new WicketRuntimeException("Invalid initializer method declared on class " + c.getName() + ". Initializers must be public, have a void return type, and take no parameters."); } } } switch( initializers.size() ) { case 1: return initializers.iterator().next(); case 0: return null; default: throw new WicketRuntimeException("Class " + c.getName() + " declares multiple (" + initializers.size() + ") initializer methods."); } } protected Initializer createInitializer( Class componentClass, List<Method> initializationMethods ) { return initializationMethods.isEmpty() ? new NullInitializer() : new ReflectionInitializer(initializationMethods); } //********************************************************************************************************************** // Inner Classes //********************************************************************************************************************** protected static interface Initializer { public void initialize( Component component ); } protected static class NullInitializer implements Initializer { public void initialize( Component component ) { // Do nothing! } } protected static class ReflectionInitializer implements Initializer { private final List<Method> initializationMethods; public ReflectionInitializer( List<Method> initializationMethods ) { this.initializationMethods = initializationMethods; } public void initialize( Component component ) { for( Method initializationMethod : initializationMethods ) { try { initializationMethod.invoke(component); } catch( IllegalAccessException e ) { throw new WicketRuntimeException("Failed to invoke initializer method " + initializationMethod.getName() + " on component of type " + component.getClass().getName() + ".", e); } catch( InvocationTargetException e ) { throw new WicketRuntimeException("Failed to invoke initializer method " + initializationMethod.getName() + " on component of type " + component.getClass().getName() + ".", e); } } } } } If one really wanted to, they could override the createInitializer() method to provide the capability of dynamically generating a class at runtime which merely invokes the initialization methods on the component (using Javassist perhaps). This class basically enforces the two simple rules I mentioned before. --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]