Manejo de JMS en JBOSS

0
37762

Manejo
de JMS en JBOSS. 1

Introducción
a JMS. 1

Arquitectura
de JMS. 2

Objetos
administrados. 2

Mensajes. 2

Clientes
JMS. 3

Configuración
de colas en JBOSS. 3

Un
ejemplo de JMS. 4

Session
Bean para envío de mensajes. 4

Message
Driver Bean consumidor de una cola. 7

Una
clase de servicio para acceder al EJB de sesión. 8

Un
cliente del EJB de sesión y las salidas del MDB en la consola del servidor 9

Conclusiones. 11

Bibliografía. 12

 

Manejo de JMS en JBOSS

Introducción a JMS[1]

JMS es la solución de Sun para los
sistemas de mensajes, empecemos por saber que es un sistema de mensajes
empresarial:

 

En las comunicaciones cliente servidor,
los datos que se intercambian entre las dos partes necesitan de una
comunicación síncrona, es decir, que las dos partes estén presentes en el
momento de la comunicación. Los sistemas de mensajes aportan una serie de
mejoras a la comunicación entre aplicaciones que no tienen por que residir en
la misma máquina.

 

JMS se sitúa como middleware (capa
media) de la comunicación de dos aplicaciones. En los entornos cliente
servidor, cuando la aplicación A quiere comunicarse con la Aplicación B, necesita saber donde esta B (su IP por ejemplo) y que B esté escuchando en ese
momento. Cuando se usa JMS (o cualquier otro sistema de mensajes), la
aplicación A envía un mensaje, el sistema de mensajes lo recibe y se lo envía a
B cuando se conecte al servicio. De esta manera se consigue una comunicación
asíncrona entre A y B, es decir no hace falta que B este presente en el momento
del envío del mensaje, y no por ello va a dejar de recibirlo.

 

 

La anterior es una de las ventajas de
JMS, pero no la única. La comunicación anterior tiene dos extremos, el
productor (A) y el consumidor (B). JMS soporta otro tipo de comunicaciones que
Sun denomina Publisher/Subscriber, traducido seria Publicador
(Publicante)/Suscriptor. En este tipo de comunicación, la aplicación A publica
su mensaje en el servicio JMS y lo reciben todas las aplicaciones que estén
suscritas al servicio JMS al que se envió el mensaje, esta forma de
comunicación es exactamente igual que la que se produce el los chats del IRC.

 

Otra de las ventajas de usar JMS (o
cualquier otro sistema de mensajes) es que las aplicaciones se pueden cambiar
simplemente asegurándose que la nueva aplicación entiende los mensajes que se
intercambian.

 

Arquitectura de JMS

 

Una aplicación JMS consta de los
siguientes elementos:

 

Clientes JMS

Aplicaciones que envían o reciben mensajes a través
de JMS

Mensajes

Los mensajes que se intercambian

Objetos administrados

Los objetos JMS a los que se dirigen las
comunicaciones

 

Objetos administrados

 

Los objetos administrados son el punto
al que se comunican los clientes JMS para enviar o recibir mensajes, se
denominan objetos administrados por que los crea el administrador (en la
implementación de referencia mediante
j2eeadmin). Implementan
las interfaces JMS y se sitúan en el espacio de nombres de JNDI (Java Naming
and Directory Interface) para que los clientes puedan solicitarlos.

 

Hay dos tipos de objetos administrados
en JMS:

  • ConnectionFactory: Se usa para crear una
    conexión al proveedor del sistema de mensajes.
  • Destination: Son los destinos de
    los mensajes que se envían y el recipiente de los mensajes que se reciben.

 

Mensajes

 

Es el corazón del sistema de mensajes.
Están formados por tres elementos:

 

  • Header: Es la cabecera del
    mensaje, contiene una serie de campos que le sirven a los clientes y
    proveedores a identificar a los mensajes. Aquí se presenta la lista de
    campos completa :

 

Campo

Tipo de Dato

Descripción

JMSMessageID

String

Un numero que identifica de manera única al
mensaje. Solo se puede consultar una vez que esta enviado el mensaje.

JMSDestination

Destination

El destino a donde se envía el mensaje.

JMSDeliveryMode

int

Puede ser de tipo PERSISTENT, entonces se envía una
única vez, o de tipo NON_PERSISTEN, de manera que se envía como mucho una
vez, lo cual incluye también que no sea enviado nunca. PERSISTENT y
NON_PERSISTENT están definidas como constantes.

JMSTimestamp

long

Pues eso, la hora a la que se envío el mensaje.

JMSExpiration

long

La hora hasta la cual el mensaje es valido, si es 0
quiere decir que no caduca nunca.

JMSPriority

int

Prioridad del mensaje de 0 a 9, siendo 0 la mas baja.

JMSCorrelationID

String

Este campo se usa para relacionar una respuesta a
un mensaje, se copia aquí el ID de mensaje del mensaje al que se esta
respondiendo.

JMSReplyTo

Destination

Especifica el lugar a donde se deben enviar las
respuestas al mensaje actual.

JMSType

String

Este campo lo puede usar el programa de mensajería
para almacenar el tipo del mensaje.

JMSRedelivered

boolean

Indica que el mensaje ha sido enviado con
anterioridad pero el destino no lo ha procesado, por lo que se reenvía.

 

  • Properties: Son propiedades
    personalizadas para un mensaje en particular. Aquí se indica la lista
    completa:

 

Propiedad

Tipo de Dato

Descripción

JMSXUserID

String

El usuario que envía el mensaje.

JMSXApplID

String

La aplicación que envía el mensaje.

JMSXDeliveryCount

int

El numero de veces que se ha intentado enviar el
mensaje

JMSXGroupID

String

Identificador del grupo al que pertenece el
mensaje.

JMSXGroupSeq

int

Numero de secuencia en el grupo de mensajes.

JMSXRcvTimestamp

long

La hora a la que JMS le entrego el mensaje al/los
destinatario/s.

JMSXState

int

Para uso del proveedor de mensajería.

JMSX_<nombre_del_proveedor>

Reservado para propiedades particulares del
proveedor.

 

  • Body: Es el mensaje en si,
    hay distintos tipos:

StreamMessage: Contiene un
stream de datos que se escriben y leen de manera secuencial.

MapMessage: Contiene
pares nombre-valor.

TextMessage: Contiene un
String.

ObjectMessage: Contiene un
objeto que implemente la interfaz
Serializable.

BytesMessage: Contiene un
stream de bytes.

 

Clientes JMS

 

Son clientes de JMS tanto el que
suministra mensajes como el que los recibe. Todos tienen una serie de pasos en
común antes de lograr enviar o recibir un mensaje:

  • Conseguir un objeto ConnectionFactory a través de JNDI.
  • Conseguir un destino, mediante
    el objeto
    Destination a través de JNDI.
  • Usar ConnectionFactory para conseguir un
    objeto
    Connection
  • Usar Destination para crear un objeto Session.

 

Configuración de colas en JBOSS

 

Para declarar una cola en JBOSS es necesario
modificar el archivo jbossmq-destinations-service.xml ubicado en
JBOSS_HOME/Server/default/deploy/jms

 

 

 

Y adicionar una sección como la siguiente

 

 

<mbean
code=»org.jboss.mq.server.jmx.Queue»

                 name=»jboss.mq.destination:service=Queue,name=myQueue»>

        <depends
optional-attribute-name=»DestinationManager»>jboss.mq:service=DestinationManager</depends>

        <depends
optional-attribute-name=»SecurityManager»>jboss.mq:service=SecurityManager</depends>

        <attribute
name=»MessageCounterHistoryDayLimit»>-1</attribute>

        <attribute
name=»SecurityConf»>

          <security>

            <role
name=»guest» read=»true» write=»true»/>

            <role
name=»publisher» read=»true» write=»true»
create=»false»/>

            <role
name=»noacc» read=»false» write=»false»
create=»false»/>

          </security>

        </attribute>

</mbean>

 

Nota: Si se llega a modificar
este archivo en caliente, es decir sin bajar el servidor, los servicios de
JBOSS detectan automáticamente la nueva configuración y realizan el cambio.

Un ejemplo de JMS

 

La siguiente sección ilustrará algo del código
necesario para crear un EJB de sesión con posibilidades de enviar mensajes a
una cola en un servidor JBOSS

 

Session Bean para envío de mensajes

 

Declaramos una interfaz remota y local del EJB con
los siguientes métodos

 

/**

       * Método para inicilizar las propiedades de la cola de mensajes

       *

       * @param properties

       */

      public void
initQueueDefinitions(Properties properties);

 

/**

* Send text to a queue and returns the ID assigned by the JMS provider.

       *

       * @ejb.interfacemethod

       * @param message

       *           
The message to publish.

       * @param queue

       *           
The queue to send to.

       * @return the JMSMessageID asssigned to this message.

       */

public
String send(Properties properties, String message, Queue queue, Date date,
Object object)
throws JMSException;

 

      /**

       * Setear la cola de mensajes

       *

       * @param properties

       * @return

       */

      public Queue
returnQueue(Properties
properties)

 

Veamos ahora un poco de la implementación

 

/**

       * Método para inicilizar las propiedades de la cola de mensajes

       *

       * @param fisaUserData

       * @param properties

       */

      public void
initQueueDefinitions(

                  Properties
properties) {

            try {

                  logger.info(«Dentro
de initQueue»
);

                  InitialContext
ctx =
new InitialContext(properties);

                  queue =
(Queue) ctx.lookup(
«queue/myQueue»);

                  queueConnectionFactory =
(QueueConnectionFactory) ctx

                             .lookup(«UIL2ConnectionFactory»);

            } catch
(NamingException e) {

                  String
msg =
«An error was encountered trying to lookup an object from
JNDI»
;

                  logger.error(msg);

            }

 

      }

 

Este método initQueueDefinitions
servirá para inicializar los objetos factory y queue que emplearemos para el
envío y recepción de mensajes.

 

/**

       * Setear la cola de mensajes

       *

       * @param fisaUserData

       * @param properties

       * @return

       */

      public Queue
returnQueue(

                  Properties
properties)
throws JMSException {

            logger.info(«Estoy
en returnQueueSender «
);

            initQueueDefinitions( 
properties);

 

            return
getQueue();

            // return
queue;

      }

 

El método returnQueue nos permite tomar una cola del
servidor para emplearla en el envío de mensajes.

 

/**

       * Send a Message to a queue with a scheduled delivery and returns the ID

       * assigned by the JMS provider.

       *

       * @ejb.interfacemethod

       * @param messageCreator

       *           
MessageCreator to create the message with.

       * @param queue

       *         
  
The queue to send to.

       * @param deliveryDate

       *           
the date at which the message should be delivered. If null, no

       *           
delivery date is set

       * @return the JMSMessageID asssigned to this message.

       */

public
String send(Properties properties, MessageCreator messageCreator, Queue queue,
Date deliveryDate, Object object)
throws
JMSException {

if (logger.isDebugEnabled())
{

logger.debug(«Sending
message [«
+ messageCreator + «] to
queue [«
+ queue.getQueueName() + «]»);

            }

 

      QueueConnection connection
=
null;

      QueueSession session = null;

      QueueSender
sender =
null;

 

      try {

            initQueueDefinitions( 
properties);

           

      connection = queueConnectionFactory.createQueueConnection();
//

      session =
connection.createQueueSession(
false,

                  QueueSession.AUTO_ACKNOWLEDGE);

      sender =
session.createSender(queue);

      Message
msg = messageCreator.getMessage(session);

 

      if
(deliveryDate !=
null) {

      logger.debug(«Message
will be delivered on [«
+ deliveryDate     + «]»);

                        msg.setLongProperty(«JMS_JBOSS_SCHEDULED_DELIVERY»,

                                   deliveryDate.getTime());

                  }

 

                  msg.setObjectProperty(«MyObject»,
object);

 

                  sender.send(msg);

                  return
msg.getJMSMessageID();

            } finally {

                  closeQueueSender(sender);

                  closeSession(session);

                  closeConnection(connection);

            }

      }

 

Como se observa en el método send se ha
seguido los pasos indicados en la sección clientes JMS, es decir:

  • Conseguir un objeto ConnectionFactory a través de JNDI.
  • Conseguir un destino, mediante
    el objeto
    Destination a través de JNDI.
  • Usar ConnectionFactory para conseguir un
    objeto
    Connection
  • Usar Destination para crear un objeto Session.

 

Algo que también es interesante en este
ejemplo es que al método send le he dado un parámetro tipo Object y en el
mensaje se settea este objeto, una utilidad podría ser el envío de adjuntos en
un mensaje de texto o el envío de cualquier clase de objeto para que luego sea
procesado por la lógica de negocio.

 

Message Driver Bean consumidor de una
cola

 

Bien en la sección anterior definimos un EJB común de
session y lo hemos configurado para poder acceder a una cola y enviar mensajes
a la misma, ahora definiremos un MDB que será un cliente consumidor de la misma
cola.

 

Para la implementación usaremos las anotaciones de
EJB 3.0

 

import
javax.ejb.ActivationConfigProperty;

import
javax.ejb.EJBException;

import
javax.ejb.MessageDriven;

 

 

import
javax.jms.Message;

import
javax.jms.MessageListener;

import
javax.jms.TextMessage;

import
javax.persistence.EntityManager;

import
javax.persistence.PersistenceContext;

 

/**

 *

 * Message Driver Bean implementado como cliente de la cola de mensajes

 *

 * @author Kirs Herrera

 * @version ${Revision} 1.0$

 */

@MessageDriven(mappedName
=
«jms/JmsSampleListener»,
activationConfig = {

            @ActivationConfigProperty(propertyName
=
«acknowledgeMode», propertyValue
=
«Auto-acknowledge»),

 

            @ActivationConfigProperty(propertyName
=
«destinationType», propertyValue
=
«javax.jms.Queue»),

 

            @ActivationConfigProperty(propertyName
=
«destination», propertyValue = «queue/myQueue»)

 

})

public class JmsSampleListener
implements MessageListener

{

      @PersistenceContext

    EntityManager manager;

     

     

     

      public
JmsSampleListener()

      {

           

      }

     

      /**

       * Método que toma los mensajes de la cola

       * @param msg

       */

public void
onMessage(Message msg) {

       TextMessage
tm = (TextMessage) msg;

        try {

           
String texto= tm.getText();

            System.out.println(«En
Mensaje «
+ tm.getJMSMessageID());

            System.out.println(«Con
texto «
+ texto);

            //Recupero el objeto
adjuntado al mensaje      

            Object
obj = msg.getObjectProperty(
«MyObject»);

                 

            if(obj instanceof
String)

            {

                        System.out.println(«Tengo
la cadena «
+ obj);

            }

                  System.out.println(«Luego
de obtener objeto»
);

 

                   } catch
(Exception e) {

                 
e.printStackTrace();

                 
throw new
EJBException(e);

              }

          }

       

       public void
persist(Object object) {

              // TODO:

              //
manager.persist(object);

            }

       

       public void save(Object
object) {

                // TODO:

                manager.persist(object);

            }

 

}

 

Al actuar como cliente este MDB se pega a la
cola del servidor a escuchar todos los mensajes que llegan, el método onMessage
se disparará con cada nuevo mensaje.

 

Es importante notar que el mapeo de anotaciones al
inicio de la clase nos evita el tener que ir a definir el archivo xml con los
MDB que estamos manejando.

 

Una clase de servicio para acceder al
EJB de sesión

 

Será necesario acceder al EJB Sender por medio de
JNDI para esto necesitamos de una clase de servicio que haga un lookup del EJB
y pueda acceder a los métodos expuestos.

 

Básicamente declaramos una de las interfaces del EJB
y la instanciamos en el constructor de la clase de servicios

 

FacadeJmsSampleSender
sender = null;

 

public
JMSSampleService()

{

try {

            ServiceLocator
locator = ServiceLocator.getInstance();

                  sender =
(FacadeJmsSampleSender) locator

                                                           .lookup(«FacadeJmsSampleSender/remote»);

            } catch
(Throwable ex) {

                  ex.printStackTrace();

            }

      }

 

/**

 * Método para inicilizar las propiedades de la cola de mensajes

 *

 * @param properties

 */

public void
initQueueDefinitionsSender(Properties properties) {

            sender.initQueueDefinitions(
properties);

      }

 

Como se observa para ir a cualquiera de los
métodos lo hacemos a través de la propiedad que declaramos en la clase de
servicios.         

 

 

Un cliente del EJB de sesión y las
salidas del MDB en la consola del servidor

 

 

El siguiente código muestra de manera muy sencilla un
cliente de consola para los EJB que se han definido en este ejemplo:

 

import
java.util.Date;

import
java.util.Properties;

 

import
javax.jms.Queue;

 

import
ec.com.kirs.jms.service.JMSSampleService;

 

public class
ClientJMS {

 

      /**

       * @param args

       */

      public static void
main(String[] args)
throws Exception

      {

            // TODO
Auto-generated method stub

            JMSSampleService
service =
new JMSSampleService();

            Date
date;

            date
=
new Date();

            Properties
properties =
new Properties();

 

            properties.put(«java.naming.factory.initial»,

                        «org.jnp.interfaces.NamingContextFactory»);

            properties.put(«java.naming.factory.url.pkgs»,

                        «=org.jboss.naming:org.jnp.interfaces»);

            properties.put(«java.naming.provider.url», «localhost:1099»);

            System.out.println(«Antes
de obtener la cola del servidor «
);

            Queue queue = service.returnQueueSender(
properties);

            String mensaje = «Vamos
a ir a myQueue»
;

            String obj = «Objeto
tipo string a adjuntar la mensaje»
;

           

            String id =
service.send( properties, mensaje, queue,

                        date,
obj);

           

            System.out.println(«Id
de mensaje en la cola es «
+ id);

 

 

      }

 

}

 

Para probar el desarrollo levantamos el servidor

 

 

Notamos que la cola myQueue que definimos en el
archivo de configuración se encuentra ahora instanciada

 

 

Ejecutemos la clase cliente y observemos la salida
dentro del IDE Eclipse.

 

Observemos la salida en la consola del servidor

 

 

Conclusiones

  • JMS simplifica la comunicación de aplicaciones
  • Permite que varios clientes acceden a una cola
    de mensajes
  • Algunos usos directos de JMS serían las
    aplicaciones punto a punto (tipo MSN), sistemas de notificaciones, los
    news y los servicios de suscripción.

Bibliografía

 


[1] Esta sección, con excepción del gráfico presentado se ha tomado del
artículo del web de programación en castellano referenciado en la bibliografía.

Dejar respuesta

Please enter your comment!
Please enter your name here