Java EE 6 Observer Pattern / Events With CDI (JSR-299/JSR-330) and EJB 3.1

1. You will have to define an event class first. It can be any POJO you like:

public class HelloEvent {

    private String message;

    public HelloEvent(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }
}

2. An event has to be fired by an injected javax.enterprise.event.Event class. It can be injected into an EJB 3.1:

@Named("messenger")

@Stateless

public class HelloMessenger {

    @Inject Event<HelloEvent> events;

    public void hello(){

        events.fire(new HelloEvent("from bean " + System.currentTimeMillis()));

    }

3. The event can be consumed by another Session Bean / managed bean. You only have to use the @Observes annotation: 

@Stateless

public class HelloListener {

    public void listenToHello(@Observes HelloEvent helloEvent){

        System.out.println("HelloEvent: " + helloEvent);

    }

}

4. You can access the EJB 3.1 directly - without any backing beans. You only have to use the name (messenger) from the @Named annotation in the JSF 2.0 page and bind the action to the method (hello()) name:

    <h:body>

        <h:form>

            <h:commandButton value="Fire!" action="#{messenger.hello}"/>

        </h:form>

[...] 

The project (CDIEJB3Events) was implemented in few minutes with NetBeans 6.8, tested with Glassfish v3 and pushed into: http://kenai.com/projects/javaee-patterns/. The entire WAR (JSF 2.0 , EJB 3.1, CDI) is 8 kB small. The initial deployment took (897 ms): 

INFO: Initializing Mojarra 2.0.2 (FCS b10) for context '/CDIEJB3Events'

INFO: Loading application CDIEJB3Events at /CDIEJB3Events

INFO: CDIEJB3Events was successfully deployed in 897 milliseconds. 

Comments:

If I may notice, this is similar to what Spring supports (javadoc states since 1.1.1) via ApplicationEventPublisher.publishEvent ( http://bit.ly/5PSYS5 ) implemented by (Web)ApplicationContext so bean had to implement ApplicationContextAware, event had and seems still with 3.0.0.RELEASE has to extend ApplicationEvent, and @Observes seems cleaner than implementing ApplicationListener<E extends ApplicationEvent> too, so + for @Inject.

Posted by Stevo Slavic on December 17, 2009 at 04:26 PM CET #

@Stevo,

perfect - then Spring apps are easier portable to Java EE 6 and vice versa! See: http://www.adam-bien.com/roller/abien/entry/8_reasons_why_java_ee point 9.

Would be the Spring solution easier / leaner than Java EE 6?

regards && thanks!,

adam

Posted by Adam Bien on December 17, 2009 at 05:28 PM CET #

I have to create a bean.xml file on WEB-INF, in order to index.xhtml could resolve #{mesenger.hello}

¿is it an automatic way to have these file created? or ¿have it to be always manually created?

Greetings

Posted by nestorgm on February 05, 2010 at 01:05 AM CET #

Good post.
I have a question, is there a simple approach to united the Event model of JSF 2 and CDI(and JMS)? eg. Email sent, order processing, paymant processing.

Posted by hantsy on September 16, 2010 at 07:28 PM CEST #

Hi,

Unfortunately CDI( atleast 1.0) do not support Typed Parameter in Event.

Don't you think it should?

Thanks & Regards,

Puspendu Banerjee

Posted by Puspendu Banerjee on October 30, 2010 at 06:24 AM CEST #

@Puspendu,

didn't miss them so far. Do you have a concrete example?

thanks!,

adam

Posted by adam-bien.com on October 31, 2010 at 12:10 AM CEST #

@Adam Yes I have some concrete example.
Let's take an example of a Message Mediation Engine/ Social Site e.g. twitter, where Messages are coming from different sources like, email, SMS other other apps.

UseCase is that, we need to extract the content of message and log that before displaying to forum/timeline.

So, we have created an EventConsumer to act on any Message Saving Event of type of any incoming message type[SMS, Email etc. those extend AbstractMessage]

If I should upload some expected code[:D], please let me know, how to upload a file.
sample:
======================
public class EventConsumer {
public void onEvent(@Observes IEvent event) throws Exception {
Logger.getAnonymousLogger().info("Received Event: "+event.getClass().getName());
}
}

======================
public class MessageSavingEvent<T extends AbstractMessage> implements IEvent {

/**
*
*/
private static final long serialVersionUID = 4785714438989387307L;
private T message;

public MessageSavingEvent(T message) {
super();
this.message = message;
}

public void setMessage(T message) {
this.message = message;
}
public T getMessage() {
return message;
}

}
======================
@Stateless
@LocalBean
public class EventProducer{
@Inject @Any Event<IEvent> event;

@Asynchronous
public void fire(IEvent eventObject){
event.fire(eventObject);
}
}

======================

@Singleton
@Startup
public class Emulator {
@EJB
EventProducer eventProducer;
@SuppressWarnings("unused")
@Schedule(second="*/10", minute="*", hour="*", info="MyTimer")
private void scheduledTimeout(final Timer t) {
Logger.getAnonymousLogger().info("@Schedule called at: " + new java.util.Date());
eventProducer.fire(new UserCreationEvent());
eventProducer.fire(new MessageSavingEvent<SMSMessage>(new SMSMessage()));
}
}

Posted by Puspendu Banerjee on November 05, 2010 at 11:17 AM CET #

@Puspendu,

If you want to contribute some code to http://kenai.com/projects/javaee-patterns let me know :-).

Why you are not using a @Qualifier? Would be even more readable.

thanks for the detailed explanation!,

adam

Posted by adam-bien.com on November 05, 2010 at 11:38 PM CET #

@Adam Are you asking to put qualifier for logger?

Regards,
Puspendu

Posted by Puspendu Banerjee on November 08, 2010 at 09:09 PM CET #

If we want to use qualifier for message types then we need to know before-hand what types of messages I am going to handle.

But I want to write the logger for any message type at very begining and later other mesage-gateway vendors need to write their gateway, message-type and message processor implementations conforming the base framework designed and those could be plugged in easily into the system without any single line of code change in the framework.

So, my point is that I don't know before-hand, about all message types but I know that all of those will be of my base message type and I want to log those/ pre process those using a common, pre-defined, Event basedmechanism.

Regards,
Puspendu

Posted by Puspendu Banerjee on November 17, 2010 at 09:01 AM CET #

Hi Adam,

how about observing CDI-Events in a SessionScoped "Bean"?

Is this possible?

So if I tried it, I was not able to solve this Exception:

WELD-001303 No active contexts for scope type javax.enterprise.context.SessionScoped

Robert

Posted by Robert on May 17, 2011 at 07:38 PM CEST #

Using your CDI example
would work fine if I set it up as an EJB module but it will not work if I set it up as a Maven 3 EJB module.
Can anyone help?

Posted by Mohammad Jeragh on July 13, 2011 at 11:12 PM CEST #

CDI events is a nice feature, but I discovered a strange behavior in Glassfish 3.1.1.

An event generated by a ApplicationScoped bean will not be propagated to all SessionScoped bean listener methods.

For example: supose that you call ‘fire(“text”)’ on Producer bean, and we have 2 instances of Consumer bean. The ‘fired’ method on Consumer bean will be invoked only for one instance. The second Consumer instance will not be notificated.

@Named
@ApplicationScoped
public class Producer {
@Inject @Any
private Event event;

public void fire(String message) {
event.fire(message);
}
}

@Named
@SessionScoped
public class Consumer {
public void fired(@Observes String event) {
// do something…
}

}

I think that there is something wrong. Maybe with Weld? I don't know yet. What do you think?

Posted by Alexandre on October 10, 2011 at 12:54 AM CEST #

Hi Adam, are CDI observers safe to be used with SLSBs and MDBs? Because those instances live in pool and is it safe to assume that even those pooled instances will receive events?

Posted by JGuru on March 07, 2012 at 01:07 AM CET #

Hi Adam,
I have tried the example posted by Puspendu on a glasfish 3.1.2.2 with eclispe, but the injected eventProducer (@EJB EventProducer eventProducer;) is always null. Is there anything what have to setup for Event-Observes usage by hand on the server, too? Futher on, is it a wrong aproach to use CDI between ejb & web tiers? (as JGuru already have asked...)
Thx,
;-D

Posted by dejo on February 09, 2013 at 02:42 AM CET #

Salam,
great solution, but it seems that it works only if the observer and observable are in the same War i've tested.
When i've put the observable in a separate EJB module and the observer in a war module, the observer wasn't notified !

Any help please.
PS: I'm working on glassfish 4.1.1

Posted by Hani on August 22, 2016 at 05:05 PM CEST #

Post a Comment:
  • HTML Syntax: NOT allowed
...the last 150 posts
...the last 10 comments
License