I'm a Java software developer, consultant and architect that focuses on enterprise applications, quality and performance. I gained interest in Java due to it's open nature and community support. Next to Java, I spend most of my time trying to stay up to date with everything that moves inside the software world like Scala and NoSQL db's. Jelle is a DZone MVB and is not an employee of DZone and has posted 12 posts at DZone. You can read more from them at their website. View Full User Profile

Java EE6 Events, a lightweight alternative to JMS

06.21.2011
| 9442 views |
  • submit to reddit

A few weeks ago I attended a bejug meeting about Java EE 6, Building next generation enterprise applications. Having read much about it, I did not expect to see much shocking hidden features. But there was one part of the demo I really found impressive. Due to its loose coupling, Enterprise possibilities and simplicity. The feature I’m going to talk about today is the event mechanism that is in java EE 6.
The general idea is to fire an event and let an eventlistener pick it up. I have created this example that is totally useless, but it simplicity helps me to focus on the important stuff. I’m going to fire a LogEvent from my backing action, that will log to the java.util.Logger.
The first thing I need is to create a pojo that contains my log message and my LogLevel.

public class LogMessage implements Serializable {
 
    private final String message;
    private final Level level;
 
    LogMessage(String message, Level level) {
        this.message = message;
        this.level = level;
    }
 
    public String getMessage() {
        return message;
    }
 
    public Level getLevel() {
        return level;
    }
}

easy peasy.
Now that I have my data wrapper, I need something to fire the event and something to pick it up. The first thing I create is my method where I fire the event.
Due to CDI I can inject an event.

@Inject Event<LogMessage> event;

So we just need to fire it.

event.fire(new LogMessage("Log it baby!", Level.INFO));

Now the event is fired, If no one is registerd to pick it up, it disappears into oblivion, thus we create a listener. The listeners needs a method that has one parameter, the generic type that is given to the previous event. LogMessage.

public class LogListener {
    private static final Logger LOGGER = Logger.getAnonymousLogger();
    public void process(@Observes LogMessage message){
        LOGGER.log(message.getLevel(), message.getMessage());
    }
}

The @Observes annotation listens to all events with a LogMessage. When the event is fired, this method will be triggered.
This is a very nice way to create a loosely coupled application, you can separate heavy operations or encapsulate less essential operations in these event listeners.
All of this all happens synchronously. When we want to replace the log statement with a slow database call to a logging table, we could make our operation heavier than it should be. What I’m looking for is to create an asynchronous call.
As long as we support EJB, we can transform our Listener to an EJB by adding the @Stateless annotation on top of it. Now it’s a statless enterprise bean. This changes nothing to our sync/async problem, but EJB 3.1 support async operations. So if we also add the @Asynchronous annotation on top of it. It will asynchronously execute our logging statement.

@Stateless
@Asynchronous
public class LogListener {
    private static final Logger LOGGER = Logger.getAnonymousLogger();
    public void process(@Observes LogMessage message){
        LOGGER.log(message.getLevel(), message.getMessage());
    }
}

If we would want to combine the database logging and the console logging, we can just create multiple methods that listen to the same event.
This is a great way to create a lightweight application with a very flexible components. The alternative solution to this problem is to use JMS, but you don’t want a heavyweight configuration for this kind of loosely coupling.
Java EE has worked hard to get rid of the stigma of being heavyweight, I think they are getting there :-)

 

From http://styledideas.be/blog/2011/05/22/java-ee6-events-a-lightweight-alternative-to-jms/

Published at DZone with permission of Jelle Victoor, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Gérald Quintana replied on Wed, 2011/06/22 - 8:02am

What about:

  • Scalability: are emitter and consumer necessarily in the same JVM? Is possible to tune consumer pool size and then monitor it?
  • Fault tolerance: are unconsumed events lost if server is restarted?
  • Transaction: are event firing and consuming transactionnal? What happens if emitter writes log message to database, sends only message id and commit transaction? Can the consumer receive the event before transaction commit (in order to read log message from database from id and print it for instance)?

Markus Kolb replied on Wed, 2011/06/22 - 3:17pm

This is the actionlistening, GUI apps do for many years.

Like Gerald I'm missing scalability.

This is no replacement for JMS. If you have used JMS for this before you have missed to do some important architecture work.

Maybe a replacement for some kind of WorkerThreads.

You use JMS to support clusters, to combine independent application parts, multiple JVMs, and much more.

The example with a logger is a bad idea. Never log async. That's nearly useless logging.

Robert Bram replied on Wed, 2011/06/22 - 6:43pm

Nice article - thanks very much. One thing I am confused about is where are you registering the listener? I understand that the parameter "@Observes LogMessage message" means this listener will listen for events that accept LogMessage - will it do the same job for subclasses of the class named? Rob :)

Kenneth Mark replied on Thu, 2011/06/23 - 1:05am

Thanks for the article. Didn't know this feature in JEE6.

Gérald Quintana replied on Thu, 2011/06/23 - 2:00am

It would be great if this API could work with a JMS backend, it would make JMS simpler for most use cases.

Radu Alex replied on Sun, 2011/06/26 - 8:27pm

If you ask me, the Event Observer is by no means an alternative to JMS. It is not as useful as it might seems on the first time and the documentation is very poor. Basically, all examples are trivial and I didn't see any solid use case until now.

The fact that you have to bundle all your observers inside the same application WAR/EAR/EJB JAR as the emitter it drastically limit its usability - you can not use them as an extension point to your application.

So not only that the emitter and consumer has to be in the same JVM, they have to be in the same deployment unit archive...

As a difference to JMS, try the other way around: make a JMS call synchronous and wait for all listeners to reply.

Jelle Victoor replied on Mon, 2011/06/27 - 3:58pm

The case here described is off course no alternative for uses cases where you need JMS, it is an alternative for when you don't really want heavy JMS, but only the event driven mechanism. When talking about big enterprise applications, it's obvious that this wouldn't be a proper solution.

Fab Mars replied on Wed, 2011/06/29 - 4:12pm

The article is useful...until the last paragraph. I think CDI Events and JMS have each a different purpose.

But to make ends meet, let's see things from another angle: sometimes in the past, you needed to set up JMS to meet some simple requirements and it sounded like a hammer to smash a fly. Example: you want to spool some long processings that might instead block your GUI. Solution: JMS. A burden for your appserver, and relatively few added value in the end.

In such cases, totally right, CDI Events are OK to workaround JMS...if there's several observers. If there's only one observer, that will work of course, but that's a lot of container wiring to just call one method.

So another solution with EE6 is to @Inject LogListener (instead of @Inject Event), call your @Asynchronous logging method on it (instead of event.fire), and if you need a return value, you may even use Future<> !!

Eventually, and as usual, there are many tools to achieve what you need, you just have to make the right choice.

Alexandre Verri replied on Sun, 2011/10/09 - 2:58pm

CDI events is a nice feature, but I discovered a strange behavior in Glassfish 3.1.1. An event generated will not be propagated to all SessionScoped bean instances. 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?

Sirikant Noori replied on Sun, 2012/01/15 - 12:44pm

I think I’d still prefer the spring version; it looks like the jee6 board has gone overboard with annotations. Just because they misused interfaces in the past does not mean they are inheritly bad.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.