How to use inheritance and lists of CDI events?

1.9k views Asked by At

Is it possible to use type variance in CDI events? here is the case:

  • Suppose i have a root event type MyEvent and subclass DummyEvent
  • My goal is to process a list of events received from a remote source List<? extends MyEvent>, containing DummyEvent instances

How can i do this?

If i loop through the collection calling fire() on each event, it will invoke @Observes MyEvent evt but not @Observes DummyEvent evt methods.

** update **

Created a sample code to clarify the issue:

https://github.com/jfaerman/jfaerman/blob/master/test-cdi/src/main/java/jfaerman/App.java

I would like the event to be fired twice, one time individually and one time from the list.

2

There are 2 answers

1
Julio Faerman On BEST ANSWER

It works injecting the BeanManager instad of Event, as tested by this servlet:

https://github.com/jfaerman/cdi-tests/blob/master/src/main/java/jfaerman/TestEventsServlet.java

Answered by Jozef Hartinger in this thread in the Weld forum:

https://community.jboss.org/message/716185

3
Roland Tiefenbrunner On

Mhh I dont get it ... how does your actual code fore firing the event look like? ASFAIK you inject the javax.enterprise.event.Event interface and pass an instance to its fire method, which by that declares the called observer. And if inheritance is involved, like in your case, both Observer would be called, if you fire a DummyEvent. If you wanted to further specify the events you would use Qualifiers.

@Inject @Any Event<DummyEvent> dummyEvent;
...
dummyEvent.fire(list.get(i));

/* edit */

The "problem" is the following line of code:

weld.event().select(MyEvent.class).fire(evt);

As soon as you specifiy the event's type (MyEvent.class), the actual event instance's type (evt) does not matter anymore. One possibility is to extend your class hirachy with Qualifiers. E.g:

@ChildEvent.Child
public class ChildEvent extends BaseEvent{

    @Qualifier
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
    public @interface Child{
    }

    public void eventAction() {
        System.out.println("child");
    }    
}

After that further specify the Observer:

public void observerChild(@Observes @ChildEvent.Child BaseEvent child){
        System.out.println("child with annotation event");
}

Finally, when you have just access to base classes, like in your example where you itarate through a list, you can specify the exact type/qualifier before firing the event like that:

for (BaseEvent e : list){
    childEvent.select(e.getClass().getAnnotations()[0]).fire(e);
}

As mentioned above, if you have a general Observer (shown below), it will be called for each event.

public void observerBase(@Observes  BaseEvent base){
    System.out.println("base event");
}