SeamFramework.orgCommunity Documentation

Chapter 7. Annotation and AnnotatedType Utilities

7.1. Annotated Type Builder
7.2. Annotation Instance Provider
7.3. Annotation Inspector
7.4. Synthetic Qualifiers
7.5. Reflection Utilities

Seam Solder provides a number of utilility classes that make working with annotations and AnnotatedTypes easier. This chapter walks you through each utility, and gives you some ideas about how to use it. For more detail, take a look at the JavaDoc on each class.

Seam Solder provides an AnnotatedType implementation that should be suitable for the needs of most portable extensions. The AnnotatedType is created from AnnotatedTypeBuilder, typically in an extension's observer method, as follows:

AnnotatedTypeBuilder builder = new AnnotatedTypeBuilder()

        .readFromType(type, true) /* readFromType can read from an AnnotatedType or a class */
        .addToClass(ModelLiteral.INSTANCE); /* add the @Model annotation */

Here we create a new builder, and initialize it using an existing AnnotatedType. We can then add or remove annotations from the class, and its members. When we have finished modifying the type, we call create() to spit out a new, immutable, AnnotatedType.

AnnotatedType redefinedType = builder.create();

One place this is immensely useful is for replacing the AnnotatedType in an extension that observes the ProcessAnnotatedType event:

public <X> void processAnnotatedType(@Observes ProcessAnnotatedType<X> evt) {

    AnnotatedTypeBuilder builder = new AnnotatedTypeBuilder()
            .readFromType(evt.getAnnotatedType(), true)
            .addToClass(ModelLiteral.INSTANCE);
    evt.setAnnotatedType(builder.create());
}

This type is now effectively annotated with @Model, even if the annotation is not present on the class definition in the Java source file.

AnnotatedTypeBuilder also allows you to specify a "redefinition", which can be applied to the type, a type of member, or all members. The redefiner will receive a callback for any annotations present which match the annotation type for which the redefinition is applied.

For example, to remove the qualifier @Unique from the type and any of its members, use this:

AnnotatedTypeBuilder builder = new AnnotatedTypeBuilder()

        .readFromType(type, true)
        .redefine(Unique.class, new AnnotationRedefiner<Unique>() {
            public void redefine(RedefinitionContext<A> ctx) {
                ctx.getAnnotationBuilder().remove(Unique.class);
            }
        });
AnnotatedType redefinedType = builder.create();

No doubt, this is a key blade in Solder's army knife arsenal of tools. You can quite effectively change the picture of the type metadata CDI discovers when it scans and processes the classpath of a bean archive.

Sometimes you may need an annotation instance for an annotation whose type is not known at development time. Seam Solder provides a AnnotationInstanceProvider class that can create an AnnotationLiteral instance for any annotation at runtime. Annotation attributes are passed in via a Map<String,Object>. For example given the follow annotation:

@Retention(RetentionPolicy.RUNTIME)

public @interface MultipleMembers {
    int intMember();
    long longMember();
    short shortMember();
    float floatMember();
    double doubleMember();
    byte byteMember();
    char charMember();
    boolean booleanMember();
    int[] intArrayMember();
}

We can create an annotation instance as follows:

/* Create a new provider */

AnnotationInstanceProvider provider = new AnnotationInstanceProvider();
/* Set the value for each of attributes */
Map<String, Object> values = new HashMap<String, Object>();
values.put("intMember", 1);
values.put("longMember", 1);
values.put("shortMember", 1);
values.put("floatMember", 0);
values.put("doubleMember", 0);
values.put("byteMember", ((byte) 1));
values.put("charMember", 'c');
values.put("booleanMember", true);
values.put("intArrayMember", new int[] { 0, 1 });
/* Generate the instance */
MultipleMembers an = provider.get(MultipleMembers.class, values);

The Annotation Inspector allows you to easily discover annotations which are meta-annotated. For example:

/* Discover all annotations on type which are meta-annotated @Constraint */

Set<Annotation> constraints = AnnotationInspector.getAnnotations(type, Constraint.class, beanManager);
/* Load the annotation instance for @FacesValidator the annotation may declared on the type, */
/* or, if the type has any stereotypes, on the stereotypes */
FacesValidator validator = AnnotationInspector.getAnnotation(
        type, FacesValidator.class, true, beanManager);

The utility methods work correctly on Stereotypes as well. Let's say you're working with a bean that was decorated @Model, running the following example will still show you the underlying @Named

// assuming you have a class..

@Model
public class User {
    ...
}
// Assume type represents the User class
assert AnnotationInspector.isAnnotationPresent(type, Named.class, beanManager);
// Retrieves the underlying @Named instance on the stereotype
Named name = AnnotationInspector.getAnnotation(type, Named.class, true, beanManager);

The search algorithm will first check to see if the annotation is present directly on the annotated element first, then searches within the stereotype annotations on the element. If you only want to search for Annotations on Stereotypes, then you can use either of the methods AnnotationInspector.getAnnotationFromStereotype.

There is an overloaded form of isAnnotationPresent and getAnnotation to control whether it will search on Stereotypes or not. For both of these methods, a search is performed first directly on the element before searching in stereotypes.

When developing an extension to CDI, it can be useful to detect certain injection points, or bean definitions and based on annotations or other metadata, add qualifiers to further disambiguate the injection point or bean definition for the CDI bean resolver. Solder's synthetic qualifers can be used to easily generate and track such qualifers.

In this example, we will create a synthetic qualifier provider, and use it to create a qualifier. The provider will track the qualifier, and if a qualifier is requested again for the same original annotation, the same instance will be returned.

/* Create a provider, giving it a unique namespace */

Synthetic.Provider provider = new Synthetic.Provider("com.acme");
/* Get the a synthetic qualifier for the original annotation instance */
Synthetic synthetic = provider.get(originalAnnotation);
/* Later calls with the same original annotation instance will return the same instance */
/* Alternatively, we can "get and forget" */
Synthetic synthetic2 = provider.get();

Seam Solder comes with a number miscellaneous reflection utilities; these extend JDK reflection, and some also work on CDI's Annotated metadata. See the javadoc on Reflections for more.

Solder also includes a simple utility, PrimitiveTypes for converting between primitive and their respective wrapper types, which may be useful when performing data type conversion. Sadly, this is functionality which is missing from the JDK.

InjectableMethod allows an AnnotatedMethod to be injected with parameter values obtained by following the CDI type safe resolution rules, as well as allowing the default parameter values to be overridden.