martes, junio 29, 2004

Broker

La necesidad tiene cara de hereje, y para reiventar la rueda siempre hay suficientes tentaciones, por lo que hacer un Broker de mensajes desde 0 no merece el infierno ¿verdad? ¿O si? De todos modos lo tuve que hacer en un cliente para poder integrar en una aplicación diferentes sistemas de mensajería.

Los sistemas a interactuar eran diversos: MQ, JMS, WebMethods, XML-RPC y ficheros. La idea era que se pueda recibir mensajes de forma transparente desde la aplicación, es decir, que el destinatario no se entere ni del método de transporte ni del origen del mensaje. También era necesario poder enviar los mensajes por mas de un sistema a la vez.
Estos diferentes métodos deberían ser plugeables mediante la configuración de un fichero (mas adelante sería una consola).

Una ventaja adicional al producto resultante, sería que los developers podrían simular el envío de mensajes durante el desarrollo y las pruebas del software mediante ficheros, uno de los métodos de mensajería propuestos.

Sistema propuesto:


La clase principal es Broker, a la cual se la puede configurar con diferentes implementaciones de BrokerConnctorIF.

En el diagrama se pueden ver tres de estos conectores: FilesConnector, WebMethodsConnector y XmlRpcConnector. Cada una de estas implementaciones sabe como conectarse con su fuente de mensajes, un host en el caso de WebMethods por ejemplo, o una estructura de archivos en el caso del FilesConnector. Cada vez que reciben un mensaje, lo encapsulan en un BrokerMessage y lo entregan al Broker.

El Broker utiliza los BrokerMessageConverters (implementadores de BrokerMessageConvertersIF) que tenga configurados para convertir mensajes al "formato" necesario para determinada cola. Esto último ha servido para hacer unmarshalls de XMLs recibidos de distintas fuentes y tener asi objetos ya hidratados y listos para que el subscriptor del mensaje lo entienda.

Por último, los clientes del Broker, implementan la interfaz BrokerSubscriptorIF, la que les permite obtener los mensajes en forma asíncrona.

El siguiente paso es configurar las clases utilizando Spring y su IoC.

Primero los BrokerMessageConverters:

<bean id="postAddOrder" class="application.converters.PostAddOrderConverter" init-method="init"/>
<bean id="postConsolidateConverter" class="application.converters.PostConsolidateConverter" init-method="init">
  
<property name="xmlUtils"><ref bean="xmlUtils"/></property>
</bean>
<bean id="xmlUtils" class="adapter.utils.xml.CastorImpl" init-method="init">
  
<property name="mappingFileNames">
    
<list>
      
<value>castorMappings/Order.xml</value>
      
<value>castorMappings/LoadTendered.xml</value>
    
</list>
  
</property>
</bean>

Como ven uno de los conversores utiliza Castor. La idea es que el conversor sea capaz de convertir un InputStream en una clase determinada.

Los conectores:
<bean id="webMethodsBrokerConnector" class="utils.broker.connectors.WebMethodsConnector" init-method="init">
  
<property name="host"><value>${host}</value></property>
  
<property name="brokerName"><value>Name</value></property>
  
<property name="clientGroup"><value>Group</value></property>
  
<property name="clientName"><value>Client</value></property>
  
<property name="persistent"><value>true</value></property>
  
<property name="autoReconnect"><value>true</value></property>
  
<property name="sharedQueue"><value>true</value></property>
  
<property name="sharedOrderNone"><value>true</value></property>
</bean>

<bean id="filesBrokerConnector" class="utils.broker.connectors.FilesConnector" init-method="init">
  
<property name="home"><value>/data/brokerFiles</value></property>
  
<property name="xmlUtils"><ref bean="xmlUtils"/></property>
</bean>

<bean id="xmlRpcBrokerConnector" class="utils.broker.connectors.XmlRpcConnector" init-method="init">
  
<property name="port"><value>81</value></property>
</bean>

Cada conector tiene la configuración necesaria para conectarse con su fuente de mensajes e implementan la interface BrokerConnectorIF.

Solo nos queda ahora configurar "el" Broker, al cual se le especifica que conversores y conectores utilizará:

<bean id="messageBroker" class="utils.broker.Broker" init-method="init" destroy-method="close" lazy-init="false">
  
<property name="home"><value>/data/brokerFiles</value></property>
  
<property name="messageTTL"><value>1800000</value></property>
  
<property name="backupMessages"><value>true</value></property>
  
<property name="xmlUtils"><ref bean="xmlUtils"/></property>
  
<property name="converters">
    
<map>
      
<entry key="xx::Q1::postAddOrder">
        
<ref bean="postAddOrderConverter"/>
      
</entry>
      
<entry key="xx::Q2::postConsolidate">
        
<ref bean="postConsolidateConverter"/>
      
</entry>
    
</map>
  
</property>
  
<property name="connectors">
    
<list>
      
<ref bean="filesBrokerConnector"/>
      
<ref bean="xmlRpcBrokerConnector"/>
    
</list>
  
</property>
  
<property name="ignoreList">
    
<list>
      
<value>Event::error</value>
    
</list>
  
</property>
</bean>

Como se ve, una arquitectura de "plug-ins" como esta, junto con la invaluable ayuda de Spring, hace que implementar una clase a la cual se le agrega o quita funcionalidad dependiendo de las necesidades es muy simple.

3 comentarios:

Anónimo dijo...

Maldición, voy a tener que poner mi blog yo también...
Parece que si no tenés blog no sos nadie....!!

Ale.

Anónimo dijo...

Hola que tal, me podrias pasar el codigo completo de la aplicacion para poder asi entender mejor tu ejemplo.
te paso mi correo torna38@gmail.com

rc dijo...

Saludos..... Dejeme decirle que su blog esta espectacular, tiene muy buenos artículos y son muy entendibles, ah! por cierto le felicito por el blog y continue de esa manera contribuyendo con información útil para los visitantes, voy a continuar navenagdo por su blog y le visitare en otras ocasiones. :):):):):):) La entrada ha resultado muy interesante y entrenida, me he quedado un buen rato leyendola, me despido de usted y hasta la proxima oportunidad.