I want to fire an event in CDI whose type I can only determine on runtime. For instance, let's say there's some interface A
with implementing classes AA
and AB
. I have two observers:
public void observeAA(@Observes AA aa) {
}
public void observeAA(@Observes AB ab) {
}
Then some event producer:
@Inject @Any
private Event<A> event;
public A getPayload();
public void fire() {
this.event.fire(getPayload());
}
This doesn't work because A
is neither a subtype of AA
or AB
(it's the other way around). I've noticed there's a select
method that takes a subtype:
public <U extends T> Event<U> select(Class<U> subtype, Annotation... qualifiers);
However, it requires a correctly parameterized Class
object, which (correct if I'm wrong), I can't build at runtime.
Is there any solution or will I have to use qualifiers (possibly an annotation with a Class<?>
method)?
I ended up using a qualifier with a Class<?>
member.
@Qualifier
@Target({TYPE, METHOD, PARAMETER, FIELD})
@Retention(RUNTIME)
public @interface EventType {
Class<?> value();
}
public class Dispatcher {
@Inject @Any
private Event<A> event;
public void fireEvent(A a) {
this.event.select(
getTypeAnnotation(
a.getClass())).fire(a);
}
public static EventType getTypeAnnotation(
final Class<?> type) {
return (EventType) Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
new Class<?>[]{EventType.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
if (method.equals(
EventType.class.getMethod("value"))) {
return type;
} else if (method.equals(Annotation.class.getMethod(
"annotationType"))) {
return EventType.class;
} else if (method.getName().equals("hashCode")) {
return 127 * "value".hashCode() ^ type.hashCode();
} else if (method.getName().equals("equals")) {
return (args[0] instanceof EventType &&
((EventType)args[0]).value()
.equals(type));
}
return null;
}
});
}
}
public class X {
public void observeA(
@Observes @EventType(AA.class) A a) {
...
EDIT
This is a simpler way of instantiating the annotation:
public abstract static class ConfigTypeAnnotation
extends AnnotationLiteral<ConfigType>
implements ConfigType { }
public static ConfigType getConfigTypeAnnotation(final Class<?> type) {
return new ConfigTypeAnnotation() {
@Override
public Class<?> value() {
return type;
}
};
}