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):
123456789101112131415161718@LocalBean@Statelesspublic class MyEJB {@Inject@InForegroundprivate 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:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354@Statelesspublic class BackgroundEventSender implements BackgroundEventSenderItf {@Resource(mappedName = "myConnectionFactory") // the JNDI name for this connection factoryprivate ConnectionFactory connectionFactory;@Resource(mappedName = "myQueue") // JNDI name for this queueprivate Queue backgroundEventQueue;private Connection connection;private Session session;private MessageProducer producer;@PostConstructpublic 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);}}@PreDestroypublic 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*/@Overridepublic 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:
123456789101112131415161718192021222324252627282930313233343536@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@InBackgroundprivate 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.
123456789101112131415@Statelesspublic class EventConsumer implements EventConsumerItf {/*** consumes the event sent by the JMS queue** @param event*/@Overridepublic 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
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.