In short, you will need to write an abstract class that implements your annotation type T, and extends javax.enterprise.util.AnnotationLiteral<T> (see
ContainsQualifier
class below). The client code then can instantiate this abstract class by using anonymous inner class that supplies appropriate values to T (see AnnotationLiteralTest#findBy
below). A somewhat contrived use case: there are bags and baskets that contain items like gold or fruit. So bag and basket are annotated with what they contain. In a warehouse, the staff needs to find all packgages containing gold, all packages containing fruit, etc, based on one or more qualifiers.
This is implemented in the following maven project (project source in github):
pom.xml:
The annotation type qualifier: Contains
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.blogspot.javahowto</groupId>
<artifactId>annotation-literal</artifactId>
<version>1.0</version>
<build>
<defaultGoal>install</defaultGoal>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
<dependencies>
<dependency>
<groupId>javax.enterprise</groupId>
<artifactId>cdi-api</artifactId>
<version>1.0-SP4</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Bag type containing gold:
package com.blogspot.javahowto;
import javax.inject.Qualifier;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Qualifier
@Documented
@Retention(value= RetentionPolicy.RUNTIME)
public @interface Contains {
String value() default "";
}
Basket type containing fruit:
package com.blogspot.javahowto;
@Contains("Gold")
public class Bag {
}
Abstract class that implements the qualifier Contains:
package com.blogspot.javahowto;
@Contains("Fruit")
public class Basket {
}
The qualifier-based search is implemented in CollectionManager:
package com.blogspot.javahowto;
import javax.enterprise.util.AnnotationLiteral;
public abstract class ContainsQualifier extends AnnotationLiteral<Contains>
implements Contains {}
The JUnit test class:
package com.blogspot.javahowto;
import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.LinkedList;
public class CollectionManager {
private Collection collection;
public CollectionManager(Collection collection) {
this.collection = collection;
}
/**
* Finds all objects matching all criteria represented by annotations
* @param annotationsToMatch one or multiple search qualifiers
* @return a collection of objects that match all the search qualifiers
*/
public Collection findBy(Annotation... annotationsToMatch) {
Collection result = new LinkedList();
for(Object obj : collection) {
boolean matched = false;
Annotation[] classAnnotations = obj.getClass().getAnnotations();
for(Annotation an : annotationsToMatch) {
if(contains(classAnnotations, an)) {
matched = true;
} else {
matched = false;
break;
}
}
if(matched)
result.add(obj);
}
return result;
}
/**
* Checks if an array of Annotations contains an individual Annotation
* @param annotations an array of annotations
* @param ann an individual annotation
* @return true if ann is equal to at least one of the element in
* annotations array; false otherwise
*/
private boolean contains(Annotation[] annotations, Annotation ann) {
for(Annotation a : annotations) {
if(a.equals(ann)) {
return true;
}
}
return false;
}
}
To build the project and run the JUnit tests:
package com.blogspot.javahowto.test;
import com.blogspot.javahowto.*;
import org.junit.BeforeClass;
import org.junit.Test;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
public class AnnotationLiteralTest {
private static List mixed = new LinkedList();
private static CollectionManager collectionManager;
@BeforeClass
public static void setUp() throws Exception {
for (int i = 0; i < 2; i++) {
mixed.add(new Bag());
mixed.add(new Basket());
}
collectionManager = new CollectionManager(mixed);
}
@Test
public void testContainsGold() throws Exception {
System.out.printf("contains Gold: %s%n%n", findBy("Gold"));
}
@Test
public void testContainsFruit() throws Exception {
System.out.printf("contains Fruit: %s%n%n", findBy("Fruit"));
}
@Test
public void testContainsFruitGold() throws Exception {
System.out.printf("contains Gold and Fruit: %s%n%n", findBy("Gold", "Fruit"));
}
/**
* Finds all objects matching all criteria
* @param criteria one or multiple search qualifiers
* @return a collection of objects that match all the search qualifiers
*/
private Collection findBy(final String... criteria) {
Contains[] qualifiers = new Contains[criteria.length];
for (int i = 0; i < criteria.length; i++) {
final String s = criteria[i];
Contains containsQualifier = new ContainsQualifier() {
@Override
public String value() {
return s;
}
};
qualifiers[i] = containsQualifier;
}
return collectionManager.findBy(qualifiers);
}
}
Some points for discussion:
$ mvn install
Running com.blogspot.javahowto.test.AnnotationLiteralTest
contains Gold: [com.blogspot.javahowto.Bag@7b112783, com.blogspot.javahowto.Bag@23394894]
contains Fruit: [com.blogspot.javahowto.Basket@69198891, com.blogspot.javahowto.Basket@b551d7f]
contains Gold and Fruit: []
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.047 sec
The subclass of ContainsQualifier (the anonymous inner class) represents a strategy in strategy pattern. Traditionally the strategy is an implementation of a common interface. Now this seems another area that annotation can replace the traditional interface.
In this project the search criteria are based on string values, and wrap them as annotation may be overkill. But in more complex cases, annotation offers a very flexible and generic interface for specifying conditions and qualifiers.