Optimizing aop lifecycle
kabirkhan Jan 5, 2010 7:47 AMMy original plan was to just hard-code the annotation plugins on startup, but I then realised that we probably still want to be able to deploy them. So, when this is parsed:
<lifecycle-configure name="LifecycleCallback" class="org.jboss.test.microcontainer.support.LifecycleCallbackWithBeanDependency" classes="@org.jboss.test.microcontainer.support.Lifecycle"> </lifecycle-configure>
A bean called LifecycleCallback implemented by LCWDB is installed (as before), and an instance of LifecycleAnnotationPlugin handling @Lifecycle is added by the LifecycleBinding bean.
Previously instead of LAPlugin the LifecycleBinding bean installed a lifecycle pointcut to the AspectManager, and pointcut matching was done as part of AOPDependencyBuilder using the info from the AspectManager. AOPDepBuilder would create a LifecycleAspectDependencyBuilderListItem which would set up the correct dependencies and set up the lifecycle callback items in the matching context's dependency info. Lifecycle calbacks would then be invoked by AbstractController.handleLifecycleCallbacks().
Now instead of the AOPDepBuilder doing fancy and heavy pointcut matching to determine lifecycle callbacks, the LifecycleAnnotationPlugin is invoked as part of DescibeAction.applyAnnotations() -> BeanAnnotationAdapter.applyAnnotations().
If we deploy a bean that has the @Lifecycle annotation BAA picks that up and it is handled by LifecycleAnnotationAdapter which uses LifecycleAspectDependencyBuilderListItem to set up the dependencies and dependency info as before. I just used LADBLI since the code was already there, but am planning to refactor that somehow
Anyway, the question I really had was: am I ok to expose the add/removeAnnotationPlugin() methods in the BeanAnnotationAdapter interface?
Code from LifecycleBinding:
public void start() throws Exception { ..... if (!classes.startsWith("@")) throw new IllegalArgumentException("Could not parse '" + classes + " into an annotation. (It must start with '@')"); String annotationName = classes.substring(1); Class<Annotation> clazz = null; try { clazz = (Class<Annotation>)SecurityActions.getContextClassLoader().loadClass(annotationName); } catch (Exception e) { throw new IllegalArgumentException("An error occurred loading '" + classes + "'", e); } LifecycleAspectDependencyBuilderListItem item = new LifecycleAspectDependencyBuilderListItem(callbackBean, state, installMethod, uninstallMethod); plugin = new LifecycleAnnotationPlugin<Annotation>(clazz, item); getBeanAnnotationAdapter().addAnnotationPlugin(plugin); } public void stop() throws Exception { if (plugin != null) getBeanAnnotationAdapter().removeAnnotationPlugin(plugin); } @SuppressWarnings("unchecked") private CommonAnnotationAdapter<AnnotationPlugin<?, ?>, MetaDataVisitor> getBeanAnnotationAdapter() { BeanAnnotationAdapter adapter = BeanAnnotationAdapterFactory.getInstance().getBeanAnnotationAdapter(); if (adapter instanceof CommonAnnotationAdapter == false) throw new IllegalArgumentException("Adapter is not an instance of CommonAnnotationAdapter"); return (CommonAnnotationAdapter<AnnotationPlugin<?,?>, MetaDataVisitor>)adapter; }
public class LifecycleAnnotationPlugin<C extends Annotation> extends ClassAnnotationPlugin<C> implements CleanablePlugin { Logger log = Logger.getLogger(LifecycleAnnotationPlugin.class); private final DependencyBuilderListItem item; private final Set<KernelControllerContext> handledContexts = new ConcurrentSet<KernelControllerContext>(); public LifecycleAnnotationPlugin(Class<C> annotation, DependencyBuilderListItem item) { super(annotation); if (item == null) throw new IllegalArgumentException("Null dependency builder list item"); this.item = item; } @Override protected List<? extends MetaDataVisitorNode> internalApplyAnnotation(ClassInfo info, MetaData retrieval, C annotation, KernelControllerContext context) throws Throwable { item.addDependency(context); handledContexts.add(context); return null; } @Override protected void internalCleanAnnotation(ClassInfo info, MetaData retrieval, C annotation, KernelControllerContext context) throws Throwable { item.removeDependency(context); handledContexts.remove(context); } public void clean() { for (KernelControllerContext context : handledContexts) { try { internalCleanAnnotation(null, null, null, context); } catch(Throwable t) { log.warn("An error occurred cleaning " + context, t); } } } }
The clean() method is called by CommonBeanAnnotationAdapter.removeAnnotationPlugin() to make sure that we remove the dependency from the contexts affected by the annotation.