Using AnnotationEnvironment in Seam
alesj Aug 19, 2008 8:11 AMI'll describe a bit how AnnotationEnvoroment works, where it comes from, ...
but then it should be up to Seam guys to fully incorporate this into Seam's
component scanning / lookup.
AnnotationEnv API:
public interface AnnotationEnvironment { /** * Does this annotation environment contain a class * which is annotated with annotation parameter. * This only applies to annotations for ElementType.TYPE level. * * This method should be used if we have no intention * to do real lookup of annotated classes, but we're * only interested in existance of the annotation. * e.g. deployment unit contains @Stateful EJBs * * @param annotation the annotation we're querying for * @return true if there exists a class with annotation param * @see #hasClassAnnotatedWith(Class annotation) */ boolean hasClassAnnotatedWith(Class<? extends Annotation> annotation); /** * Does this annotation environment contain a class * which is annotated with annotation parameter. * This only applies to annotations for ElementType.TYPE level. * * This method should be used if we have no intention * to do real lookup of annotated classes, but we're * only interested in existance of the annotation. * e.g. deployment unit contains @Stateful EJBs * * @param annotationName the annotation name we're querying for * @return true if there exists a class with annotation param * @see #hasClassAnnotatedWith(Class annotation) */ boolean hasClassAnnotatedWith(String annotationName); /** * Get all classes annotated with annotation param. * * @param <A> the annotation type * @param annotation the annotation we're querying for * @return set of matching classes */ <A extends Annotation> Set<Element<A, Class<?>>> classIsAnnotatedWith(Class<A> annotation); /** * Get all classes annotated with annotation param. * * @param annotationName the annotation name we're querying for * @return set of matching classes */ Set<Element<Annotation, Class<?>>> classIsAnnotatedWith(String annotationName); /** * Get all classes who have some constructor annotated with annotation param. * * @param <A> the annotation type * @param annotation the annotation we're querying for * @return set of matching classes */ <A extends Annotation> Set<Element<A, Constructor<?>>> classHasConstructorAnnotatedWith(Class<A> annotation); /** * Get all classes who have some constructor annotated with annotation param. * * @param annotationName the annotation name we're querying for * @return set of matching classes */ Set<Element<Annotation, Constructor<?>>> classHasConstructorAnnotatedWith(String annotationName); /** * Get all classes who have some field annotated with annotation param. * * @param <A> the annotation type * @param annotation the annotation we're querying for * @return set of matching classes */ <A extends Annotation> Set<Element<A, Field>> classHasFieldAnnotatedWith(Class<A> annotation); /** * Get all classes who have some field annotated with annotation param. * * @param annotationName the annotation name we're querying for * @return set of matching classes */ Set<Element<Annotation, Field>> classHasFieldAnnotatedWith(String annotationName); /** * Get all classes who have some method annotated with annotation param. * * @param <A> the annotation type * @param annotation the annotation we're querying for * @return set of matching classes */ <A extends Annotation> Set<Element<A, Method>> classHasMethodAnnotatedWith(Class<A> annotation); /** * Get all classes who have some method annotated with annotation param. * * @param annotationName the annotation name we're querying for * @return set of matching classes */ Set<Element<Annotation, Method>> classHasMethodAnnotatedWith(String annotationName); /** * Get all classes who have some method's/constructor's parameter annotated with annotation param. * * @param <A> the annotation type * @param annotation the annotation we're querying for * @return set of matching classes */ <A extends Annotation> Set<Element<A, AnnotatedElement>> classHasParameterAnnotatedWith(Class<A> annotation); /** * Get all classes who have some method's/constructor's parameter annotated with annotation param. * * @param annotationName the annotation name we're querying for * @return set of matching classes */ Set<Element<Annotation, AnnotatedElement>> classHasParameterAnnotatedWith(String annotationName); } public interface Element<A extends Annotation, M extends AnnotatedElement> { /** * Get the owner class name. * * Until we hit getOwner method the class should not be loaded. * * @return the owner classname */ String getOwnerClassName(); /** * Get the annotation owner class. * * @return the annotation owner class */ Class<?> getOwner(); /** * Get the annotation instance. * * @return the annotation instance */ A getAnnotation(); /** * Get the annotated element that holds the annotation. * * @return the annotated element instance */ M getAnnotatedElement(); }
The API is pretty simple :-),
as can be seen it has to 'flavors',
one where you put direct annotation class as parameter,
the other one where you put annotation class name.
The Element has a few useful hooks,
and its impl detail is that it's completely lazy -
it doesn't load the class or annotation until asked.
There is a flag on the annotation scanning deployer,
that allows for AnnotationEnvironment to already keep
the annotation instance gathered from Javassist lookup -
meaning you don't even need to load the class to
inspect annotation value.
e.g. you only need classname + annotation member values
When (annotation scanning) deployers are properly configured,
this is already the case in current JBoss5 trunk,
every deployment unit gets AnnotationEnvironment as an attachment.
DeploymentUnit unit = getAttribute(context, DeploymentUnit.class.getName(), DeploymentUnit.class); AnnotationEnvironment env = unit.getAttachment(AnnotationEnvironment.class);
In the Seam case we would have to do a bit of
hierarchy traversing to get all the AnnotationEnvironments,
from all deployment units, since we only have web deployment unit,
in the ServletContext attributes.
public class JBossSeamListener extends SeamListener { public void contextInitialized(ServletContextEvent event) { // TODO - enable some MC scanning notion in Seam super.contextInitialized(event); ServletContext context = event.getServletContext(); Kernel kernel = getAttribute(context, Kernel.class.getName(), Kernel.class); DeploymentUnit unit = getAttribute(context, DeploymentUnit.class.getName(), DeploymentUnit.class);
Looking at the ComponentDeploymentHandler::handle
public void handle(String name, ClassLoader classLoader) { if (name.endsWith(".class")) { String classname = filenameToClassname(name); String filename = componentFilename(name); try { ClassFile classFile = getClassFile(name, classLoader); boolean installable = ( hasAnnotation(classFile, Name.class) || classLoader.getResources(filename).hasMoreElements() ) && !"false".equals( getAnnotationValue(classFile, Install.class, "value") ); if (installable) { log.trace("found component class: " + name); classes.add( (Class<Object>) classLoader.loadClass(classname) ); }
Looks like we should also do a bit more than just looking for @Name,
but for the @Install::value==false it should be just a simple Set intersection.
Where I don't see what exactly does "classLoader.getResources(filename).hasMoreElements() " do/help.
Pete & co., if you need any other info,
post it here. :-)