In this post I will show you how to decouple event producer from the event consumers (aka async events).
- you must configure your application server (here the glassfish settings):


- call the EJB method sendEvent() to send the event (from a servlet or from a web page):
@LocalBean @Stateless public class MyEJB { @Inject @InForeground private Event<BaseEvent> event; /** * fires the @InForeground event * * @throws InterruptedException */ public void sendEvent() throws InterruptedException { event.fire(new MyEvent("Hello world event", new Date())); Thread.sleep(5000); } } - the EJB fires the event anotaded with @InForeground qualifier.
- An observer method handle the event and send it to the JMS Queue with the help of a message producer:
@Stateless public class BackgroundEventSender implements BackgroundEventSenderItf { @Resource(mappedName = "myConnectionFactory") // the JNDI name for this connection factory private ConnectionFactory connectionFactory; @Resource(mappedName = "myQueue") // JNDI name for this queue private Queue backgroundEventQueue; private Connection connection; private Session session; private MessageProducer producer; @PostConstruct public void init() { try { connection = connectionFactory.createConnection(); session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE); producer = session.createProducer(backgroundEventQueue); } catch (JMSException ex) { throw new RuntimeException(ex.getMessage(), ex); } } @PreDestroy public void destroy() { try { if (connection != null) { connection.close(); } } catch (Exception ex) { // todo: handle the exception } } /** * sends the foreground event to the queue with the help of the producer object * * @param event */ @Override public void event(@Observes @InForeground BaseEvent event) { try { ObjectMessage msg = session.createObjectMessage(); msg.setObject(event); producer.send(msg); } catch (JMSException ex) { throw new RuntimeException(ex.getMessage(), ex); } } } - Now, in another (background) thread, the onMessage method handle the message and fires a new event (which wraps the received event) now annotated with @InBackground qualifier:
@MessageDriven(mappedName = "myQueue", activationConfig = { @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge"), @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue") }) public class BackgroundEventDispatcher implements MessageListener { public BackgroundEventDispatcher() { } @Inject @InBackground private Event<BaseEvent> event; /** * dispatches the message received from the queue and fires a new event (now with the @InBackground qualifier) * * @param message */ public void onMessage(Message message) { if (!(message instanceof ObjectMessage)) { throw new RuntimeException("Invalid message type received"); } ObjectMessage msg = (ObjectMessage) message; try { Serializable eventObject = msg.getObject(); if (!(eventObject instanceof BaseEvent)) { throw new RuntimeException("Unknown event type received"); } BaseEvent evt = (BaseEvent) eventObject; this.event.fire(evt); } catch (JMSException ex) { throw new RuntimeException(ex.getMessage(), ex); } } } - And finally, if you have a method that consumes events annotated with @InBackground qualifier, then you receive the event and process it accordingly to your business needs.
@Stateless public class EventConsumer implements EventConsumerItf { /** * consumes the event sent by the JMS queue * * @param event */ @Override public void afterMyEvent(@Observes @InBackground MyEvent event) { System.out.println("Event message: " + event.getData()); System.out.println("Event time: " + new SimpleDateFormat("yyyy.MM.dd @ HH:mm:ss").format(event.getEventTime())); } }
The event object it’s just a POJO like this one:
public class MyEvent extends BaseEvent {
private String data;
private Date eventTime;
public String getData() {
return data;
}
public MyEvent(String data, Date eventTime) {
this.data = data;
this.eventTime = eventTime;
}
public void setData(String data) {
this.data = data;
}
public Date getEventTime() {
return eventTime;
}
public void setEventTime(Date eventTime) {
this.eventTime = eventTime;
}
}
You can get the full code from github. Thank you!
Feel free to comment.