Dynamically fire CDI event with qualifier with members

Ben Kirby picture Ben Kirby · Aug 3, 2011 · Viewed 7.6k times · Source

I'm trying to use CDI events in my backend services, on JBoss AS6 - ideally with maximum code reuse.

I can see from the docs I can cut down on the qualifier annotation classes I have to create by using a qualifier with members e.g.

@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface Type {
    TypeEnum value();
}

I can observe this with

public void onTypeAEvent(@Observes @Type(TypeEnum.TYPEA) String eventMsg) {...}

So far, so good. However, to further cut down on the number of classes needed, I want to have one EventFirer class, where the qualifier of the event thrown is dynamic. Not a problem with qualifiers without members:

public class DynamicEventFirer {

    @Inject @Any private Event<String> event;

    public void fireEvent(AnnotationLiteral<?> eventQualifier){
        event.select(eventQualifier).fire("FIRED"); 
    }
}

then called like

dynamicEventFirer.fireEvent(new AnnotationLiteral<Type>() {});

But what about when the qualifier should have members? Looking at the code for AnnotationLiteral, it's certainly setup for members, and the class element comment has the example:

new PayByQualifier() { public PaymentMethod value() { return CHEQUE; } }

This makes sense to me - you're overriding the value() method of the annotation interface. However, when I tried this myself:

dynamicEventFirer.fireEvent(new AnnotationLiteral<Type>() {
    public TypeEnum value() {
        return TypeEnum.TYPEA;
    }
});

I receive the exception

java.lang.RuntimeException: class uk.co.jam.concept.events.MemberQualifierEventManager$1 does not implement the annotation type with members uk.co.jam.concept.events.Type
    at javax.enterprise.util.AnnotationLiteral.getMembers(AnnotationLiteral.java:69)
    at javax.enterprise.util.AnnotationLiteral.hashCode(AnnotationLiteral.java:281)
    at java.util.HashMap.getEntry(HashMap.java:344)
    at java.util.HashMap.containsKey(HashMap.java:335)
    at java.util.HashSet.contains(HashSet.java:184)
    at org.jboss.weld.util.Beans.mergeInQualifiers(Beans.java:939)
    at org.jboss.weld.bean.builtin.FacadeInjectionPoint.<init>(FacadeInjectionPoint.java:29)
    at org.jboss.weld.event.EventImpl.selectEvent(EventImpl.java:96)
    at org.jboss.weld.event.EventImpl.select(EventImpl.java:80)
    at uk.co.jam.concept.events.DynamicEventFirer.fireEvent(DynamicEventFirer.java:20)

Can anyone see what I'm doing wrong? MemberQualifierEventManager is an ApplicationScoped bean which calls on DynamicEventFirer to fire the event.

Thanks, Ben

Answer

John Ament picture John Ament · Aug 4, 2011

There's a slightly cleaner way to do it based on your post:

public class TypeQualifier extends AnnotationLiteral<Type> implements Type{

private TypeEnum type;

public TypeQualifier(TypeEnum t) {
      this.type = t;
}

public TypeEnum value() {
    return type;
}

}

then just fire like this:

dynamicEventFirer.fireEvent(new TypeQualifier(TypeEnum.TYPEA));