How to use inheritance and lists of CDI events?
Asked Answered
D

2

8

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.

Deathday answered 28/1, 2012 at 2:28 Comment(1)
Which CDI Implementation are you using?Clarsach
D
3

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

Deathday answered 13/2, 2012 at 15:35 Comment(1)
very nice +1 for sharing with usCatalpa
A
1

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");
}
Ailsun answered 30/1, 2012 at 20:33 Comment(3)
Please see the code example i just added, hope to make the issue a little more clear.Deathday
Edited the answer, maybe it helps.Ailsun
It helps, but there i a solution using only the types... thanks for posting, learned the trick :)Deathday

© 2022 - 2024 — McMap. All rights reserved.