Introducción a Spring Integration.
0. Índice de contenidos.
- 1. Introducción.
- 2. Entorno.
- 3. Canales.
- 4. Flujo de mensajes.
- 5. Transformadores.
- 6. Endpoints.
- 7. ¿Vemos un ejemplo?
- 8. Referencias.
- 9. Conclusiones.
1. Introducción
Cada vez los sistemas empresariales son más potentes y complejos. Muchas compañías manejan gran cantidad de aplicaciones que se adaptan a sus modelos empresariales. Con el paso de los años, cada vez que una compañía necesitaba una solución informática para la gestión de alguno de sus procesos, se le hacía una aplicación y punto. Otra necesidad, otra aplicación. Y otra y otra…
Sin embargo, esto a veces conlleva un problema. Puede surgir la necesidad de que diferentes aplicaciones necesiten interactuar entre sí. Nace la necesidad de una plataforma empresarial donde los procesos se gestionen de una forma centralizada y no a través de 200 aplicaciones diferentes.
Spring Integration (módulo de Spring Framework) es una plataforma de integración que ayuda a crear sistemas desacoplados con componentes reutilizables. Nos ayuda a crear sistemas modulares con baja cohesión acercándonos a una arquitectura orientada a servicios.
2. Entorno.
El tutorial está escrito usando el siguiente entorno:
- Hardware: Portátil MacBook Pro 15′ (2.2 Ghz Intel Core I7, 8GB DDR3).
- Sistema Operativo: Mac OS Snow Leopard 10.6.7
- Spring Integration 2.0.5.
- Spring 3.0.5.
3. Canales.
Spring Integration está basado en mensajes y canales. Esta es una definición tan mala como: que el fútbol son 11 tíos contra 11 corriendo detrás de un balón. Sin embargo es así. Spring Integration usa mensajes, canales y un amplio set de componentes (representan distintos patrones de integración) que interactuan con ellos. Los tipos de canales son:
- Point-to-Point channel: canal por el que se garantiza que el emisor del mensaje lo envía a un único receptor.
- Publish-Subscribe channel: canal por el que un mensaje enviado por el emisor puede llegar a varios receptores.
- Data-Typed channel: canal por el que solo se pueden enviar mensajes de un tipo concreto.
- Messaging Bridge: conecta dos canales o adaptadores.
- Channel Adapter: canal usado para integrarse con diferentes sistemas de mensajería.
- Invalid Message channel: canal por el que se envían los mensajes que no pasan las validaciones.
- Dead Letter channel: cuando un mensaje no ha podido ser enviado por el canal correspondiente dicho mensaje es enviado por el Dead Letter channel.
- Guaranteed Delivery: evita que los mensajes, normalmente almacenados en memoria, se pierdan si el sistema se viene abajo. Son almacenados en una cola de mensajes JMS.
4. Flujo de mensajes.
Normalmente los mensajes no siguen un flujo lineal sino que será variable en función de la propia composición del mensaje. Spring Integration se basa en diferenes patrones de flujo de mensajes para decidir el camino que «dibujará» el mensaje a través de la plataforma.
- Agregador (aggregator): componente que, con diferentes mensajes, construye un único mensaje.
- Enrutador (router): determina si un mensaje va a un canal u otro (otros).
- Filtro (filter): decide si un mensaje puede llegar o no a su canal destino.
- Resequencer: decide el orden en el que un grupo de mensajes serán consumidos o procesados.
- Splitter: descompone un mensaje en múltiples mensajes. Igual que el agregador, pero al revés.
Splitter y Router |
5. Transformadores.
Los transformadores son parte importante en cualquier sistema de integración. Permiten adaptar un mensaje enviado por un emisor a la entrada esperada por un receptor. Aunque podemos definir nuestros propios transformadores concretos, Spring Integration nos proporciona los siguientes tipos:
- object-to-string transformer: El mensaje saliente de un canal es transformado en un string (toString del mensaje) que será recibido por el siguiente canal.
- payload-serializing y payload-deserializing transformer: Estos dos transformadores son simétricos, el primero serializa un objeto (el mensaje) en un array de bytes y el segundo lo deserializa (array de bytes a objeto).
- object-to-map y map-to-object transformer: El primero transforma el mensaje (objeto) en un mapa de pares clave valor y el segundo realiza la operación inversa.
- json-to-object y object-to-json transformer: Pues un poco más de lo mismo, el primero transforma un objeto en una representación JSON y el segundo realiza la operación inversa.
6. Endpoints: adaptadores, gateways y service adapter.
Podríamos decir, sin miedo a equivocarnos, que el endpoint es la pieza clave en cualquier plataforma de integración. Los endpoints permiten conectar el sistema de integración con diferentes servicios o aplicaciones. Los endpoints soportan diferentes tipos de protocolos como pueden ser: servicios web, ficheros, mail, JMS o JDBC. En este apartado debemos destacar los siguientes componentes:
- Gateways: Exponen, en forma de fachada (interface), cómo la plataforma se comunicará con una aplicación o servicio. Abstrae al usuario de la lógica de negocio que lleva debajo.
- Service Activator: es un tipo de enpoint que maneja unos datos entrantes y devuelve una respuesta en caso de que sea necesaria.
Gateway y Service Activator |
7. ¿Vemos un ejemplo?
Pues bien, llegados a este punto vamos a ver un sencillo ejemplo de funcionamiento. Para ello los chicos de Spring nos han dejado en un repositorio un proyecto con múltiples ejemplos: https://github.com/SpringSource/spring-integration-samples
Lo descargamos, lo montamos en nuestro entorno y vamos a poner el foco de atención en un ejemplo que se llama «ws-inbound-gateway».
El ejemplo es sencillo: enviamos peticiones SOAP (XML) y la plataforma de integración nos responde lo que le hemos enviado envuelto en otro XML de respuesta. Gráficamente sería esto:
Y ahora alguien puede decir: «Pues vaya tontería, yo eso lo hago con un Web-Service…». Efectivamente, pero ese WS además de devolver la respuesta, ¿es capaz de interactuar con otros sistemas, monitorizar el tráfico u orquestar el flujo que llevará el mensaje de entrada en función de su contenido?
Difícilmente…
Bueno, al lío. Vamos a echarle un ojo al fichero de configuración de Spring:
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:int="http://www.springframework.org/schema/integration" xmlns:int-ws="http://www.springframework.org/schema/integration/ws" xsi:schemaLocation="http://www.springframework.org/schema/integration/ws http://www.springframework.org/schema/integration/ws/spring-integration-ws-2.0.xsd http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-2.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <int:channel id="input"/> <int-ws:inbound-gateway id="ws-inbound-gateway" request-channel="input"/> <int:service-activator input-channel="input"> <bean class="org.springframework.integration.samples.ws.SimpleEchoResponder"/> </int:service-activator> </beans>
Lo que se está haciendo es definir un canal «input» por el que pasarán los mensajes que hayan entrado por nuestro gateway de entrada «ws-inbound-gateway» y cuya salida irá a un Service Activator representado por la clase SimpleEchoResponser.
public class SimpleEchoResponder { public Source issueResponseFor(DOMSource request) { return new DomSourceFactory().createSource( "" + request.getNode().getTextContent() + ""); } }
Esta clase recibe el mensaje que ha pasado por el canal en forma de DOMSource (representación del XML que nos ha enviado el cliente en un objeto), mete su contenido en un tag «echoResponse» y lo devuelve.
Para probar el ejemplo, podemos hacerlo con herramientas como JMeter o SOAP UI o mediante la clase de test que nos viene con el ejemplo.
@ContextConfiguration("/META-INF/spring/integration/inbound-gateway-config.xml") @RunWith(SpringJUnit4ClassRunner.class) public class InboundGatewayTests { @Autowired private SimpleWebServiceInboundGateway gateway; /** * Emulate the Spring WS MessageDispatcherServlet by calling the gateway * with a DOMSource object representing the payload of the original SOAP * 'echoRequest' message. Expect an 'echoResponse' DOMSource object * to be returned in synchronous fashion, which the MessageDispatcherServlet * would in turn wrap in a SOAP envelope and return to the client. */ @Test public void testSendAndReceive() throws Exception { String xml = "hello"; DomPoxMessageFactory messageFactory = new DomPoxMessageFactory(); DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); documentBuilderFactory.setNamespaceAware(true); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); Document document = documentBuilder.parse(new InputSource(new StringReader(xml))); Transformer transformer = TransformerFactory.newInstance().newTransformer(); DomPoxMessage request = new DomPoxMessage(document, transformer, "text/xml"); MessageContext messageContext = new DefaultMessageContext(request, messageFactory); gateway.invoke(messageContext); Object reply = messageContext.getResponse().getPayloadSource(); assertThat(reply, is(DOMSource.class)); DOMSource replySource = (DOMSource) reply; Element element = (Element) replySource.getNode().getFirstChild(); assertThat(element.getTagName(), equalTo("echoResponse")); } }
¿Os ha parecido un ejemplo demasiado sencillo? Pues aquí os dejo un ejemplo mucho más completo.
8. Referencias.
- Spring Integration: Reference Manual.
- Spring Integration: Ejemplo completo.
9. Conclusiones.
En este tutorial hemos pretendido presentar la implementación de plataforma de integración que nos propone Spring. Además hemos visto cómo hace uso de los principales patrones de integración.
Recordad que esta no es la única solución para integrar diferentes servicios o sistemas, los potentes ESB´s (ServiceMix, MuleESB, Synapse, etc…) suelen ser una solución muy recurrida en cuanto a diseño de arquitecturas horizontales nos referimos. ¿La ventaja de Spring Integration? Pues probablemente que a la gente que está acostumbrada a usar Spring le costará menos hacerse con las riendas de este producto antes que con un ESB.
Espero que este tutorial os haya sido de ayuda. Un saludo.
Miguel Arlandy
Twitter: @m_arlandy
Hola Miguel,
Gracias por el fantástico tutorial.
He estado probando este ejemplo básico de Spring-Integration y no he conseguido nada más que correr el test que se ejecuta fuera del servidor. Para el test con servidor no he conseguido hacerlo funcionar ya que he estado intentando hacerlo correr en un simple Tomcat añadiendo las depencencias de ciertos estándares J2EE que se iban necesitando pero creo que esto no es lo más correcto, debería de usar un verdadero servidor de aplicación, ¿cuál me recomiendas para estos ejemplos?
Gracias.
Hola Angel,
pues verás, es muy sencillo. En Tomcat corre perfectamente (yo lo probé en Tomcat 6) pero debería correr en cualquier otro servidor.
Sigue los siguientes pasos:
– El proyecto es un proyecto maven así que debes ir al directorio donde tienes el pom.xml de ws-inbound-gateway y ejecutar «mvn package -Dmaven.test.skip»
– Coges el war que te habrá generado (directorio target), lo renombras a ws-inbound-gateway.war y lo copias en el directorio webapp del Tomcat.
– Arrancas Tomcat.
– Te vas a la clase de test que se llama «InContainerTests» (está en el mismo paquete donde está InboundGatewayTests) y ejecutas el test.
Con esto no deberías tener problemas.
Hola Miguel, ante todo feclicitaciones por tus artículos publicados en adictos al trabajo en relación con Spring Integration. En breve tendré que abordar un proyecto en el que creo que esta tecnología podría encajar bastante bien. Me he montado el proyecto del ejemplo completo y lo tengo funcionando correctamente. Hasta aqui todo bien. Pero me surge una duda a raíz de una prueba que he realizado. He parado uno de los servicios a los que se consulta. El resultado es que no me devuelve nada, ni mensaje de error ni un error 500…
Hay alguna forma de controlar esto? Los canales de consulta ws:outbound-gateway y http:outbound-gateway si no consiguen conectar porque el servicio al que se conectan está caído ¿como se controlaria este error? He leido algo sobre canales de error pero no veo cómo encajaría en este ejemplo.
Muchas gracias y perdona las molestias.
Un Saludo.