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. :-)