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.