3 Replies Latest reply on Jul 27, 2007 11:22 AM by adrian.brock

    Annotation handling impl

    alesj

      I've implemented initial version of handling MC beans annotations.
      So, before I go into madly testing all the features, backcompatibility, javadocs, ..., it would probably be good to get some feedback on my impl - bad, really bad, so so, good, nice, wonderful, ... :-)

      Let me first explain what I did.
      In the DescribeAction, after the metadata has been set, I create BeanAnnotationAdapter - currently only BasicBeanAnnotationAdapter.

      public interface BeanAnnotationAdapter
      {
       /**
       * Apply the annotations.
       *
       * @param context the context
       * @throws Throwable for any error
       */
       void applyAnnotations(KernelControllerContext context) throws Throwable;
      }
      

      The main goal is to fill in BeanMetaData with information from @Annotations.

      There are 5 different AnnotetedInfo types that need checking:
      - class
      - constructor
      - property
      - method
      - field

      I currently don't do field (since there is no matching 'Bean'MetaData), and I differentiate between property and method for the ease of providing matching PropertyMetaData.

      The whole idea is to check type's matching MetaDataRetrieval (MDR).
      For class this is context's top level MDR, for others it is the component of this top level MDR.
      And for constructor|method parameters it is component's component MDR - see the AnnotatedElementMetaDataLoader post issue.

      For each annotation there is matching AnnotationPlugin:
      public interface AnnotationPlugin<T extends AnnotatedInfo, C extends Annotation>
      {
       Class<C> getAnnotation();
      
       Set<ElementType> getSupportedTypes();
      
       void applyAnnotation(T info, MetaDataRetrieval retrieval, KernelControllerContext context) throws Throwable;
      }
      


      Depending on the ElementType's of @Target on the annotation, matching collections of plugins are filled: classPlugins, constructorPlugins, ...

      I then iterate through all possible AnnotatedInfos - ClassInfo, ConstructorInfos, PropertyInfos, MethodInfos, static MethodInfos, FieldInfos - and apply matching plugins. For each AnnotatedInfo I iterate over with all possible plugins.
       // methods
       Set<MethodInfo> methods = info.getMethods();
       if (methods != null && methods.isEmpty() == false)
       {
       for(MethodInfo mi : methods)
       {
       Signature mis = new MethodSignature(mi.getName(), Configurator.getParameterTypes(trace, mi.getParameterTypes()));
       MetaDataRetrieval cmdr = retrieval.getComponentMetaDataRetrieval(mis);
       if (cmdr != null)
       {
       for(AnnotationPlugin plugin : methodAnnotationPlugins)
       plugin.applyAnnotation(mi, cmdr, context);
       }
       }
       }
      


      If the AnnotationItem is found in MDR and metadata is not yet set, I apply information from the annotation.
       public final void applyAnnotation(T info, MetaDataRetrieval retrieval, KernelControllerContext context) throws Throwable
       {
       AnnotationItem<C> item = retrieval.retrieveAnnotation(getAnnotation());
       if (item == null || isMetaDataAlreadyPresent(info, item.getAnnotation(), context))
       return;
       internalApplyAnnotation(info, retrieval, item.getAnnotation(), context);
       }
      


      I'm also supporting constructor|method parameter annotation injection.
      In order for this to work, all parameters must have of the Annotation2ValueMetaDataAdapter annotations present: @Inject, @StringValue, @ValueFactory, @ThisValue, @NullValue.

      So much for simple explanation. :-)

      This is what you can currently do:
      @Aliases({"al", "test"})
      @Demands({"otherBean"})
      @Depends({"serviceBean"})
      @Supplys({"txBean"})
      public class SimpleInject
      {
       private int intVF;
       private TestBean testBean;
       private Set<MyDeployer> deployers;
      
       public int getVf()
       {
       return intVF;
       }
      
       @FactoryMethod
       public static SimpleInject getInstance(@NullValue Object someNull)
       {
       return new SimpleInject();
       }
      
       @Start
       public void startMeUp(@Inject(bean = "lifecycleBean") TestBean bean, @ValueFactory(bean="valueBean", method = "getValue", parameter = "123") int value)
       {
       intVF =- value;
       }
      
       @StringValue("123")
       public void setVf(int vf)
       {
       this.intVF = vf;
       }
      
       @Install
       public void addDeployer(MyDeployer deployer)
       {
       if (deployers == null)
       deployers = new HashSet<MyDeployer>();
       deployers.add(deployer);
       }
      
       @Uninstall
       public void removeDeployer(MyDeployer deployer)
       {
       deployers.remove(deployer);
       }
      
       @InstallMethod
       public void whenInstalled(@ThisValue SimpleInject me, @NullValue Object plainNull)
       {
       System.out.println(me == this);
       System.out.println("plainNull = " + plainNull);
       }
      
       @UninstallMethod
       public void withUninstall(@ThisValue SimpleInject me, @NullValue Object plainNull)
       {
       System.out.println(me == this);
       System.out.println("plainNull = " + plainNull);
       }
      
       public TestBean getTestBean()
       {
       return testBean;
       }
      
       @Inject(bean = "testBean")
       public void setTestBean(TestBean bean)
       {
       this.testBean = bean;
       }
      
      public class TestConstructorBean
      {
       @Constructor
       public TestConstructorBean(@Inject(bean = "testBean") TestBean test)
       {
       System.out.println("test = " + test);
       }
      }
      


      I'll commit the code, just to make it more simple to see what I did.
      There is just one entry point to all this mess - in the DescribeAction.

      I removed the old @Annotation lifecycle support - now part of this new approach.

      The BeanMetaData is not cloned yet - a big TODO. :-)

      I haven't thought of how exactly this will fit with the factory beans. One step at the time ... ;-)

        • 1. Re: Annotation handling impl

          I haven't looked at the details, but one issue is to me is how we test
          (including new features going forwards).

          The original MC tests came in two versions:
          1) Test using BeanMetaData programmatically
          2) Same Test using XML

          You haven't been following that approach in some of your tests
          which is something I've been meaning to bring up.

          But now we need a third set
          3) Same Test using annotations.

          • 2. Re: Annotation handling impl
            alesj

             

            "adrian@jboss.org" wrote:

            You haven't been following that approach in some of your tests
            which is something I've been meaning to bring up.

            Hmm, I tried to follow that approach, but apparently missed some of them.
            If you know which one's are those, let me know :-), but otherwise I can have a look.

            "adrian@jboss.org" wrote:

            But now we need a third set
            3) Same Test using annotations.

            Yup.
            But I'm not sure we can port all of them, meaning those that use the same class for instantiating two different instances.

            • 3. Re: Annotation handling impl

               

              "alesj" wrote:

              But I'm not sure we can port all of them, meaning those that use the same class for instantiating two different instances.


              You can override the annotations in the xml.