Annotation handling impl
alesj Jul 27, 2007 8:48 AMI'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 ... ;-)