Una guía para los beans controlados por mensajes en EJB

1. Introducción

En pocas palabras, un Enterprise JavaBean (EJB) es un componente JEE que se ejecuta en un servidor de aplicaciones.

En este tutorial, analizaremos los beans controlados por mensajes (MDB), responsables de manejar el procesamiento de mensajes en un contexto asincrónico.

Los MDB son parte de JEE desde la especificación EJB 2.0; EJB 3.0 introdujo el uso de anotaciones , lo que facilita la creación de esos objetos. Aquí, nos centraremos en las anotaciones.

2. Algunos antecedentes

Antes de profundizar en los detalles de los beans controlados por mensajes, repasemos algunos conceptos relacionados con la mensajería.

2.1. Mensajería

La mensajería es un mecanismo de comunicación. Al usar la mensajería, los programas pueden intercambiar datos incluso si están escritos en diferentes lenguajes de programa o residen en diferentes sistemas operativos.

Ofrece una solución débilmente acoplada; ni el productor ni el consumidor de la información necesitan conocer detalles entre sí .

Por lo tanto, ni siquiera tienen que estar conectados al sistema de mensajería al mismo tiempo (comunicación asincrónica).

2.2. Comunicación sincrónica y asincrónica

Durante la comunicación síncrona, el solicitante espera hasta que reciba la respuesta. Mientras tanto, el proceso del solicitante permanece bloqueado.

En la comunicación asíncrona, por otro lado, el solicitante inicia la operación pero no lo bloquea; el solicitante puede pasar a otras tareas y recibir la respuesta más tarde.

2.3. JMS

Java Message Services ("JMS") es una API de Java que admite mensajería.

JMS proporciona modelos de mensajería de igual a igual y de publicación / suscripción.

3. Beans controlados por mensajes

Un MDB es un componente que invoca el contenedor cada vez que llega un mensaje al sistema de mensajería. Como resultado, este evento activa el código dentro de este bean.

Podemos realizar muchas tareas dentro de un método MDB onMessage () , desde mostrar los datos recibidos en un navegador o analizarlos y guardarlos en una base de datos.

Otro ejemplo es enviar datos a otra cola después de algún procesamiento. Todo se reduce a nuestras reglas comerciales.

3.1. Ciclo de vida de beans controlados por mensajes

Un MDB tiene solo dos estados:

  1. No existe en el contenedor
  2. creado y listo para recibir mensajes

Las dependencias, si están presentes, se inyectan inmediatamente después de que se crea el MDB.

Para ejecutar instrucciones antes de recibir mensajes, necesitamos anotar un método con @ javax.ejb. PostConstruct .

Tanto la inyección de dependencia como @ javax.ejb. La ejecución de PostConstruct ocurre solo una vez.

Después de eso, el MDB está listo para recibir mensajes.

3.2. Transacción

Se puede entregar un mensaje a un MDB dentro de un contexto de transacción.

Lo que significa que todas las operaciones dentro del método onMessage () son parte de una sola transacción.

Por lo tanto, si ocurre una reversión, el sistema de mensajes vuelve a entregar los datos.

4. Trabajar con beans controlados por mensajes

4.1. Creando al consumidor

Para crear un Bean controlado por mensaje, usamos la anotación @ javax.ejb.MessageDriven antes de la declaración del nombre de la clase.

Para manejar el mensaje entrante, debemos implementar el método onMessage () de la interfaz MessageListener :

@MessageDriven(activationConfig = { @ActivationConfigProperty( propertyName = "destination", propertyValue = "tutorialQueue"), @ActivationConfigProperty( propertyName = "destinationType", propertyValue = "javax.jms.Queue") }) public class ReadMessageMDB implements MessageListener { public void onMessage(Message message) { TextMessage textMessage = (TextMessage) message; try { System.out.println("Message received: " + textMessage.getText()); } catch (JMSException e) { System.out.println( "Error while trying to consume messages: " + e.getMessage()); } } }

Dado que este artículo se centra en anotaciones en lugar de descriptores .xml, usaremos @ActivationConfigProperty en lugar de .

@ActivationConfigProperty es una propiedad clave-valor que representa esa configuración. Usaremos dos propiedades dentro de activationConfig , configurando la cola y el tipo de objeto que consumirá el MDB.

Dentro del método onMessage () podemos convertir el parámetro de mensaje a TextMessage, BytesMessage, MapMessage StreamMessage u ObjectMessage .

Sin embargo, para este artículo, solo veremos el contenido del mensaje en la salida estándar.

4.2. Creando el Productor

Como se explica en la sección 2.1, los servicios para productores y consumidores son completamente independientes e incluso pueden escribirse en diferentes lenguajes de programación .

Produciremos nuestros mensajes usando Java Servlets:

@Override protected void doGet( HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { String text = req.getParameter("text") != null ? req.getParameter("text") : "Hello World"; try ( Context ic = new InitialContext(); ConnectionFactory cf = (ConnectionFactory) ic.lookup("/ConnectionFactory"); Queue queue = (Queue) ic.lookup("queue/tutorialQueue"); Connection connection = cf.createConnection(); ) { Session session = connection.createSession( false, Session.AUTO_ACKNOWLEDGE); MessageProducer publisher = session .createProducer(queue); connection.start(); TextMessage message = session.createTextMessage(text); publisher.send(message); } catch (NamingException | JMSException e) { res.getWriter() .println("Error while trying to send  message: " + e.getMessage()); } res.getWriter() .println("Message sent: " + text); }

Después de obtener las instancias de ConnectionFactory y Queue , debemos crear una Connection y Session .

Para crear una sesión, llamamos al método createSession .

The first parameter in createSession is a boolean which defines whether the session is part of a transaction or not.

The second parameter is only used when the first is false. It allows us to describe the acknowledgment method that applies to incoming messages and takes the values of Session.AUTO_ACKNOWLEDGE, Session.CLIENT_ACKNOWLEDGE and Session.DUPS_OK_ACKNOWLEDGE.

We can now start the connection, create a text message on the session object and send our message.

A consumer, bound to the same queue will receive a message and perform its asynchronous task.

Also, apart from looking up JNDI objects, all actions in our try-with-resources block make sure the connection is closed if JMSException encounters an error, such as trying to connect to a non-existing queue or specifying a wrong port number to connect.

5. Testing the Message Driven Bean

Send a message through the GET method on SendMessageServlet, as in:

//127.0.0.1:8080/producer/SendMessageServlet?text=Text to send

Also, the servlet sends “Hello World” to the queue if we don't send any parameters, as in //127.0.0.1:8080/producer/SendMessageServlet.

6. Conclusion

Message Driven Beans allow simple creation of a queue based application.

Por lo tanto, los MDB nos permiten desacoplar nuestras aplicaciones en servicios más pequeños con responsabilidades localizadas , lo que permite un sistema mucho más modular e incremental que puede recuperarse de fallas del sistema.

Como siempre, el código ha terminado en GitHub.