Improving the process of applying AnnotationPlugins on MC be
jaikiran Apr 3, 2009 7:42 AMWhile working on improving the EJB3 deployment time https://jira.jboss.org/jira/browse/EJBTHREE-1800 i noticed that MC aggresively scans for annotations on MC beans (we deploy EJB3 containers and EJB3 proxy factories as MC beans) and their methods and fields. MC does this to process these annotations (if present) through various AnnotationPlugins. However, the way its currently done:
1) Get all plugins
2) Create a method/field/constructor signature from MethodInfo/FieldInfo/ConstructorInfo
3) Create ComponentMetaData for these signatures (internally leads to reflection on the classes/methods).
4) Pass this info to the plugin which *then* checks to see whether it can process the annotation. If it can't then step #2 and #3 go waste
These steps are repeated when the plugin is being applied (deployment) and once during cleanup (undeployment).
Profiling the application shows that step#3 turns into a hotspot when the number of beans increases and the number of methods and fields on the beans increases. Also the logs show a lot of these statements :
2009-04-03 16:50:55,256 TRACE [org.jboss.kernel.plugins.annotations.ExternalInstallAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.ExternalInstalls 2009-04-03 16:50:55,256 TRACE [org.jboss.kernel.plugins.annotations.SupplysAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.Supplys 2009-04-03 16:50:55,256 TRACE [org.jboss.kernel.plugins.annotations.AliasesAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.Aliases 2009-04-03 16:50:55,256 TRACE [org.jboss.kernel.plugins.annotations.DemandsAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.Demands 2009-04-03 16:50:55,256 TRACE [org.jboss.kernel.plugins.annotations.ClassFactoryAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.Factory 2009-04-03 16:50:55,256 TRACE [org.jboss.kernel.plugins.annotations.BeanAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.Bean 2009-04-03 16:50:55,256 TRACE [org.jboss.kernel.plugins.annotations.ExternalUninstallAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.ExternalUninstalls 2009-04-03 16:50:55,256 TRACE [org.jboss.kernel.plugins.annotations.DependsAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.Depends 2009-04-03 16:50:55,256 TRACE [org.jboss.kernel.plugins.annotations.ConstructorParameterAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.Constructor 2009-04-03 16:50:55,256 TRACE [org.jboss.kernel.plugins.annotations.StringValueAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.StringValue 2009-04-03 16:50:55,256 TRACE [org.jboss.kernel.plugins.annotations.NullValueAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.NullValue 2009-04-03 16:50:55,256 TRACE [org.jboss.kernel.plugins.annotations.ArrayValueAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.ArrayValue 2009-04-03 16:50:55,256 TRACE [org.jboss.kernel.plugins.annotations.ListValueAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.ListValue 2009-04-03 16:50:55,256 TRACE [org.jboss.kernel.plugins.annotations.PropertyInstallCallbackAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.Install 2009-04-03 16:50:55,256 TRACE [org.jboss.kernel.plugins.annotations.ValueFactoryAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.ValueFactory 2009-04-03 16:50:55,256 TRACE [org.jboss.kernel.plugins.annotations.MapValueAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.MapValue 2009-04-03 16:50:55,256 TRACE [org.jboss.kernel.plugins.annotations.InjectAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.Inject 2009-04-03 16:50:55,256 TRACE [org.jboss.kernel.plugins.annotations.SetValueAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.SetValue 2009-04-03 16:50:55,256 TRACE [org.jboss.kernel.plugins.annotations.JavaBeanValueAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.JavaBeanValue 2009-04-03 16:50:55,256 TRACE [org.jboss.kernel.plugins.annotations.CollectionValueAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.CollectionValue 2009-04-03 16:50:55,256 TRACE [org.jboss.kernel.plugins.annotations.ThisValueAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.ThisValue 2009-04-03 16:50:55,256 TRACE [org.jboss.kernel.plugins.annotations.PropertyUninstallCallbackAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.Uninstall 2009-04-03 16:50:55,256 TRACE [org.jboss.kernel.plugins.annotations.StringValueAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.StringValue 2009-04-03 16:50:55,256 TRACE [org.jboss.kernel.plugins.annotations.NullValueAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.NullValue 2009-04-03 16:50:55,256 TRACE [org.jboss.kernel.plugins.annotations.ArrayValueAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.ArrayValue 2009-04-03 16:50:55,256 TRACE [org.jboss.kernel.plugins.annotations.ListValueAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.ListValue 2009-04-03 16:50:55,256 TRACE [org.jboss.kernel.plugins.annotations.PropertyInstallCallbackAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.Install 2009-04-03 16:50:55,256 TRACE [org.jboss.kernel.plugins.annotations.ValueFactoryAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.ValueFactory 2009-04-03 16:50:55,256 TRACE [org.jboss.kernel.plugins.annotations.MapValueAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.MapValue 2009-04-03 16:50:55,256 TRACE [org.jboss.kernel.plugins.annotations.InjectAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.Inject 2009-04-03 16:50:55,256 TRACE [org.jboss.kernel.plugins.annotations.SetValueAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.SetValue ...// removed a lot of similar log lines, intentionally ... ... 2009-04-03 16:50:55,261 TRACE [org.jboss.kernel.plugins.annotations.MethodUninstallCallbackAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.Uninstall 2009-04-03 16:50:55,261 TRACE [org.jboss.kernel.plugins.annotations.DestroyLifecycleAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.Destroy 2009-04-03 16:50:55,261 TRACE [org.jboss.kernel.plugins.annotations.UninstallMethodParameterAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.UninstallMethod 2009-04-03 16:50:55,261 TRACE [org.jboss.kernel.plugins.annotations.FactoryMethodAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.FactoryMethod 2009-04-03 16:50:55,261 TRACE [org.jboss.kernel.plugins.annotations.StopLifecycleAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.Stop 2009-04-03 16:50:55,261 TRACE [org.jboss.kernel.plugins.annotations.StartLifecycleAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.Start 2009-04-03 16:50:55,261 TRACE [org.jboss.kernel.plugins.annotations.MethodInstallCallbackAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.Install 2009-04-03 16:50:55,261 TRACE [org.jboss.kernel.plugins.annotations.CreateLifecycleAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.Create 2009-04-03 16:50:55,261 TRACE [org.jboss.kernel.plugins.annotations.InstallMethodParameterAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.InstallMethod 2009-04-03 16:50:55,261 TRACE [org.jboss.kernel.plugins.annotations.MethodUninstallCallbackAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.Uninstall 2009-04-03 16:50:55,261 TRACE [org.jboss.kernel.plugins.annotations.DestroyLifecycleAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.Destroy 2009-04-03 16:50:55,261 TRACE [org.jboss.kernel.plugins.annotations.UninstallMethodParameterAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.UninstallMethod 2009-04-03 16:50:55,261 TRACE [org.jboss.kernel.plugins.annotations.FactoryMethodAnnotationPlugin] (main) No annotation: org.jboss.beans.metadata.api.annotations.FactoryMethod
From what i see in the code (org.jboss.kernel.plugins.annotations.CommonAnnotationAdapter.java), i think there's chance for optimization :
1) We have all the annotation plugins and hence know which annotations, they can handle
2) The MethodInfo/ConstructorInfo/FieldInfo, provides a isAnnotationPresent(...) API.
3) Use this API and compare it against *all the plugins* to filter out only relevant plugins that can process these annotations
4) Most of the times, most of the methods/fields will have no annotations and hence those methods will get skipped.
5) After we have a non-empty collection of plugins, we can then go on to create the method/field signatures and the ComponentMetadata
Here's the patch which i came up with:
Index: src/main/java/org/jboss/kernel/plugins/annotations/CommonAnnotationAdapter.java =================================================================== --- src/main/java/org/jboss/kernel/plugins/annotations/CommonAnnotationAdapter.java (revision 86731) +++ src/main/java/org/jboss/kernel/plugins/annotations/CommonAnnotationAdapter.java (working copy) @@ -25,7 +25,9 @@ import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; +import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.Set; import org.jboss.beans.info.spi.BeanInfo; @@ -209,11 +211,36 @@ { for(ConstructorInfo ci : constructors) { + // let's first find only relevant plugins, through ConstructorInfo, before + // creating the constructor signature and retrieving the ComponentMetadata + Set<T> relevantConstructorAnnotationPlugins = new HashSet<T>(constructorAnnotationPlugins); + for(T plugin : constructorAnnotationPlugins) + { + // Can this constructor be handled by the plugin? + if (!ci.isAnnotationPresent(plugin.getAnnotation())) + { + // if not then ignore this plugin from processing this constructor + if (log.isTraceEnabled()) + { + log.trace("Plugin " + plugin + " with annotation " + plugin.getAnnotation().getName() + " is not relevant for constructor " + ci.toString()); + } + // remove this plugin from list of relevant plugins + relevantConstructorAnnotationPlugins.remove(plugin); + } + } + // if there are no plugins which can handle this constructor, + // then no use creating a constructor signature and processing further. + // Just move on to the next constructor + if (relevantConstructorAnnotationPlugins.isEmpty()) + { + continue; + } Signature cis = new ConstructorSignature(ci); MetaData cmdr = retrieval.getComponentMetaData(cis); if (cmdr != null) { - for(T plugin : constructorAnnotationPlugins) + // only consider relevant plugins for this constructor + for(T plugin : relevantConstructorAnnotationPlugins) { if (isApplyPhase) applyPlugin(plugin, ci, cmdr, handle); @@ -238,11 +265,37 @@ FieldInfo field = pi.getFieldInfo(); if (field != null) { + // let's first find only relevant plugins, through FieldInfo, before + // creating the field signature and retrieving the ComponentMetadata + Set<T> relevantFiledAnnotationPlugins = new HashSet<T>(fieldAnnotationPlugins); + for(T plugin : fieldAnnotationPlugins) + { + // Can this field be handled by the plugin? + if (!field.isAnnotationPresent(plugin.getAnnotation())) + { + // if not then ignore this plugin from processing this field + if (log.isTraceEnabled()) + { + log.trace("Plugin " + plugin + " with annotation " + plugin.getAnnotation().getName() + " is not relevant for field " + field.toString()); + } + // remove this plugin from list of relevant plugins + relevantFiledAnnotationPlugins.remove(plugin); + } + } + // if there are no plugins which can handle this field, + // then no use creating a field signature and processing further. + // Just move on to the next field + if (relevantFiledAnnotationPlugins.isEmpty()) + { + continue; + } + Signature sis = new FieldSignature(field); MetaData cmdr = retrieval.getComponentMetaData(sis); if (cmdr != null) { - for(T plugin : fieldAnnotationPlugins) + // only consider the relevant plugins for this field + for(T plugin : relevantFiledAnnotationPlugins) { if (isApplyPhase) applyPlugin(plugin, field, cmdr, handle); @@ -269,11 +322,37 @@ { if (visitedMethods.contains(mi) == false) { + // let's first find only relevant plugins, through MethodInfo, before + // creating the method signature and retrieving the ComponentMetadata + Set<T> relevantMethodAnnotationPlugins = new HashSet<T>(methodAnnotationPlugins); + for(T plugin : methodAnnotationPlugins) + { + // Can this method be handled by the plugin? + if (!mi.isAnnotationPresent(plugin.getAnnotation())) + { + // if not then ignore this plugin from processing this method + if (log.isTraceEnabled()) + { + log.trace("Plugin " + plugin + " with annotation " + plugin.getAnnotation().getName() + " is not relevant for method " + mi.toString()); + } + // remove this plugin from list of relevant plugins + relevantMethodAnnotationPlugins.remove(plugin); + } + } + // if there are no plugins which can handle this method, + // then no use creating a method signature and processing further. + // Just move on to the next method + if (relevantMethodAnnotationPlugins.isEmpty()) + { + continue; + } + Signature mis = new MethodSignature(mi); MetaData cmdr = retrieval.getComponentMetaData(mis); if (cmdr != null) { - for(T plugin : methodAnnotationPlugins) + // only consider the relevant plugins for this method + for(T plugin : relevantMethodAnnotationPlugins) { if (isApplyPhase) applyPlugin(plugin, mi, cmdr, handle); @@ -297,11 +376,36 @@ { if (smi.isStatic() && smi.isPublic()) { + // let's first find only relevant plugins, through MethodInfo, before + // creating the method signature and retrieving the ComponentMetadata + Set<T> relevantStaticMethodAnnotationPlugins = new HashSet<T>(methodAnnotationPlugins); + for(T plugin : methodAnnotationPlugins) + { + // Can this static method be handled by the plugin? + if (!smi.isAnnotationPresent(plugin.getAnnotation())) + { + // if not then ignore this plugin from processing this method + if (log.isTraceEnabled()) + { + log.trace("Plugin " + plugin + " with annotation " + plugin.getAnnotation().getName() + " is not relevant for static method " + smi.toString()); + } + // remove this plugin from list of relevant plugins + relevantStaticMethodAnnotationPlugins.remove(plugin); + } + } + // if there are no plugins which can handle this static method, + // then no use creating a method signature and processing further. + // Just move on to the next static method + if (relevantStaticMethodAnnotationPlugins.isEmpty()) + { + continue; + } Signature mis = new MethodSignature(smi); MetaData cmdr = retrieval.getComponentMetaData(mis); if (cmdr != null) { - for(T plugin : methodAnnotationPlugins) + // use only relevant plugins + for(T plugin : relevantStaticMethodAnnotationPlugins) { if (isApplyPhase) applyPlugin(plugin, smi, cmdr, handle); @@ -348,11 +452,36 @@ return; visitedMethods.add(method); + // let's first find only relevant plugins, through MethodInfo, before + // creating the method signature and retrieving the ComponentMetadata + Set<T> relevantPropertyAnnotationPlugins = new HashSet<T>(propertyAnnotationPlugins); + for(T plugin : propertyAnnotationPlugins) + { + // Can this getter/setter be handled by the plugin? + if (!method.isAnnotationPresent(plugin.getAnnotation())) + { + // if not then ignore this plugin from processing this getter/setter + if (log.isTraceEnabled()) + { + log.trace("Plugin " + plugin + " with annotation " + plugin.getAnnotation().getName() + " is not relevant for getter/setter " + method.toString()); + } + // remove this plugin from list of relevant plugins + relevantPropertyAnnotationPlugins.remove(plugin); + } + } + // if there are no plugins which can handle this getter/setter method, + // then no use creating a method signature and processing further. + if (relevantPropertyAnnotationPlugins.isEmpty()) + { + return; + } + Signature sis = new MethodSignature(method); MetaData cmdr = retrieval.getComponentMetaData(sis); if (cmdr != null) { - for(T plugin : propertyAnnotationPlugins) + // consider only relevant plugins + for(T plugin : relevantPropertyAnnotationPlugins) { if (isApplyPhase) applyPlugin(plugin, pi, cmdr, handle);
I applied this against my test setup and the profiler now shows that the hotspot that was identified earlier, no longer exists. I have attached a couple of screenshots to EJBTHREE-1800 to show the "before" and "after" from the profiler.
Any thoughts about this?