Spring Integration: Ejemplo completo.
0. Índice de contenidos.
- 1. Introducción.
- 2. Entorno.
- 3. El problema.
- 4. Los Servicios Web.
- 5. Paso 1: Recibiendo las peticiones del cliente.
- 6. Paso 2: Trazando la petición y obteniendo el identificador del usuario por cada entidad bancaria.
- 7. Paso 3: descomponiendo el mensaje.
- 8. Paso 4: Enrutando los mensajes en función de su contenido.
- 9. Paso 5: obteniendo las cuentas del cliente para la entidad 088.
- 10. Paso 6: obteniendo las cuentas del cliente para la entidad 767.
- 11. Paso 7: obteniendo las cuentas del cliente para la entidad 955.
- 12. Paso 8: agregar todos los resultados y devolver la respuesta.
- 13. Probando el ejemplo.
- 14. Referencias.
- 15. Conclusiones.
1. Introducción
Como vimos en el anterior tutorial Spring Integration es la implementación de una plataforma de integración propuesta por Spring Framework.
En este tutorial vamos a ver un ejemplo de uso más complejo en el que consumiremos diferentes servicios, transformaremos datos y gestionaremos el flujo de los mensajes en función de su contenido.
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.
- Apache JMeter 2.4.
3. El problema.
El escenario que proponemos es el siguiente. Trabajamos con tres entidades bancarias, cada una de las cuales tiene un servicio que devuelve las cuentas y saldo de un cliente de dicha entidad. Para que el servicio devuelva las cuentas y saldos debe recibir el identificador del cliente para esa entidad. Además, disponemos de un servicio que, recibiendo un NIF de un usuario devolverá las entidades bancarias y el identificador del usuario de cada una de las entidades con las que tiene cuentas abiertas el usuario.
Nuestra plataforma de integración (Spring Integration) deberá hacer lo siguiente. Recibirá peticiones via Web-Service recibiendo el NIF de un usuario. Con ese NIF consumirá el servicio de entidades por cliente. Con lo que responda ese servicio, irá a cada uno de los servicios de las distintas entidades con las que trabaja el cliente a por las cuentas y saldos del cliente en esa entidad. Devolverá una respuesta con los datos de todas las cuentas y saldos que tiene el cliente en todas las entidades. Además dejaremos un registro en un fichero (log) por cada petición que reciba la plataforma.
Las características de los servicios son las siguientes:
- Servicio de consulta de entidades/identificadores de cliente por entidad: Este es el servicio que devuelve las entidades e indentificadores de cliente por entidad. Recibe un NIF. Es un Servicio WEB.
- Servicio de consulta de cuentas de usuario en la entidad 1 (código de entidad 088): Servicio Web que recibe peticiones con el identificador del cliente para dicha entidad y devuelve sus cuentas con su saldo.
- Servicio de consulta de cuentas de usuario en la entidad 2 (código de entidad 767): Servicio REST que recibe peticiones con el identificador del cliente para dicha entidad y devuelve sus cuentas con su saldo.
- Servicio de consulta de cuentas de usuario en la entidad 3 (código de entidad 955): Clase Java a la que se invoca pasando el identificador del cliente para dicha entidad y devuelve una lista (Collection) con las cuentas y saldo.
Gráficamente hay que hacer esto:
4. Los Servicios Web.
Como hemos comentado, dos de los servicios son Web-Services.
El servicio que recibe un NIF y devuelve los identificadores del cliente para cada una de las entidades bancarias con las que tiene cuentas debe recibir un elemento «usuario» que contendrá un nodo hijo «NIF». Devolverá una «respuesta» con una lista de «clienteEntidad» tal y como se aprecia en el esquema del siguiente .wsdl:
El servicio que recibe el identificador del usuario de la entidad 088 y devuelve la lista de cuentas con saldos debe recibir un elemento «cliente» que contendrá un nodo hijo «ID». Devolverá un elemento «cuentas» con una lista de «cuentaSaldo» tal y como se aprecia en el esquema del siguiente .wsdl:
5. Paso 1: Recibiendo las peticiones del cliente.
Una vez presentado el escenario, lo primero que haremos será configurar nuestra plataforma de integración para que sea capaz de recibir peticiones del cliente. El cliente nos enviará el NIF del usuario del que queremos obtener sus cuentas y saldos. Añadimos lo siguiente en el fichero de configuración de Spring.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ws="http://www.springframework.org/schema/integration/ws" xmlns:int="http://www.springframework.org/schema/integration" xmlns:context="http://www.springframework.org/schema/context" xmlns:http="http://www.springframework.org/schema/integration/http" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-2.0.xsd 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 http://www.springframework.org/schema/integration/http/spring-integration-http-2.0.xsd"> <context:component-scan base-package="com.autentia.spring.integration" /> <context:property-placeholder location="classpath:springIntegration.properties" /> <int:channel id="inputChannel" /> <ws:inbound-gateway id="wsInboundGateway" request-channel="inputChannel" /> <int:service-activator input-channel="inputChannel" ref="usuarioEndpoint" output-channel="idsUsuarioChannel" /> </bean>
Lo que estamos haciendo es definir un gateway de entrada vía Web-Service «wsInboundGateway» que hará pasar las peticiones por el canal de entrada «inputChannel» el cual tiene un Service-Activator «usuarioEndpoint» que procesará dichas peticiones y las enviará al siguiente canal «idsUsuarioChannel». El Service-Activator sería el siguiente:
import javax.xml.transform.dom.DOMSource; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.integration.annotation.ServiceActivator; import org.springframework.stereotype.Component; import org.w3c.dom.Node; import org.w3c.dom.NodeList @Component public class UsuarioEndpoint { private static final Log LOG = LogFactory.getLog(UsuarioEndpoint.class); @ServiceActivator public Usuario handleRequest(DOMSource source) throws Exception { final NodeList nodeList = source.getNode().getChildNodes(); LOG.debug("Receiving request " + nodeList + " length " + nodeList.getLength()); String nif = ""; if (nodeList.getLength() > 1) { for (int i = 0; i
El Service-Activator recibe en su método handleRequest un DOMSource (XML de la petición) y lo transforma a un objeto Usuario. Dicho objeto será la entrada del siguiente canal «idsUsuarioChannel». Es muy parecido al ejemplo que vimos en el anterior tutorial. Nótese que esta tranformación podría haberse hecho usando otro componente distinto a un Service-Activator, como podría ser un Transformador.
6. Paso 2: Trazando la petición y obteniendo el identificador del usuario por cada entidad bancaria.
Bien, una vez recibidos los datos de entrada, el siguiente paso será trazar la petición en un fichero de log y conectar con el servicio que nos dará el identificador de cliente por cada una de las entidades bancarias con las que tenga cuentas abiertas el usuario. Añadimos lo siguiente en nuestro fichero de configuración de Spring:
<int:channel id="idsUsuarioChannel" /> <!-- Trazamos las peticiones en el fichero (ver log4j.xml) --> <int:logging-channel-adapter id="loggingChannel" level="INFO" expression="'Received request with ' + payload" channel="idsUsuarioChannel"/> <ws:outbound-gateway id="idsUsuarioGateway" request-channel="idsUsuarioChannel" uri="http://${ws.usuarioentidad.host}:${ws.usuarioentidad.port}/${ws.usuarioentidad.context}" marshaller="usuarioClienteEntidadMarshaller" reply-channel="clienteEntidadSplitterChannel" /> <bean id="usuarioClienteEntidadMarshaller" class="com.autentia.spring.integration.prueba_spring_integration.UsuarioToClienteEntidadMarshaller" />
Recordemos que en el paso anterior vimos que el Service-Activator enviaba los datos al canal idsUsuarioChannel. Pues bien, a este canal le añadimos un logging-channel-adapter «loggingChannel» que añadirá a un fichero de logs cada mensaje (payload) de entrada que pase por este canal.
Por otro lado, tenemos un gateway que conectará con el Servicio Web de consulta de identificadores por entidad y envía los datos a un canal llamado «clienteEntidadSplitterChannel». La transformación de los datos la delega en UsuarioToClienteEntidadMarshaller.
import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.xml.transform.Result; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.dom.DOMSource; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.oxm.Marshaller; import org.springframework.oxm.Unmarshaller; import org.springframework.oxm.XmlMappingException; import org.springframework.xml.transform.StringSource; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; public class UsuarioToClienteEntidadMarshaller implements Marshaller, Unmarshaller { private static final Log LOG = LogFactory.getLog(UsuarioToClienteEntidadMarshaller.class); public Object unmarshal(Source source) throws IOException, XmlMappingException { final NodeList nodeList = ((DOMSource)source).getNode().getChildNodes(); LOG.debug("Receiving request " + nodeList + " length " + nodeList.getLength()); final List<UsuarioEntidad> usuarioEntidades = new ArrayList<UsuarioEntidad>(); if (nodeList.getLength() > 1) { for (int i = 0; i " + "" + usuario.getNIF() + ""; try { final Transformer transformer = new TransformerFactoryImpl().newTransformer(); transformer.transform(new StringSource(xmlString), result); } catch (Exception e) { e.printStackTrace(); } } }
El método marshal recibirá los usuarios entran por el canal y los transforma en el XML necesario para enviárselo al WS de identificador de cliente por entidades. El método unmarshal recibe la respuesta del servicio (XML) y la transforma en una lista de UsuarioEntidad (contiene id de usuario e id entidad).
7. Paso 3: descomponiendo el mensaje.
Llegados a este punto ya tenemos una lista con los identificadores de cliente para cada una de las entidades con las que trabaja. Lo que tenemos que hacer ahora con ese mensaje (lista de pares identificador/entidad) es descomponerlo en mensajes identificador/entidad para que sean procesados de manera independiente.
<int:channel id="clienteEntidadSplitterChannel" /> <int:splitter input-channel="clienteEntidadSplitterChannel" output-channel="usuarioEntidadRouterChannel" ref="clienteEntidadSplitter" />
La descomposición del mensaje en mensajes más simples será responsabilidad del componente splitter, el cual enviará la salida al canal «usuarioEntidadRouterChannel» que recibirá la lista de mensajes.
import java.util.Collection; import java.util.List; import org.springframework.integration.annotation.Splitter; import org.springframework.stereotype.Component; @Component public class ClienteEntidadSplitter { @Splitter public Collection<UsuarioEntidad> split (List<UsuarioEntidad> usuarioEntidades) { return usuarioEntidades; } }
8. Paso 4: Enrutando los mensajes en función de su contenido.
Bueno, pues ya tenemos cada identificador de usuario para cada entidad con la que trabaja en mensajes independientes. Ahora lo que debemos hacer es conectar con el servicio de cada entidad bancaria, pero antes, debemos saber qué mensajes irán por el canal que comunicará con cada entidad. Tendremos 3 canales: uno por el que pasarán los mensajes que vayan a la entidad 1 (088), otro por el que pasarán los que irán a la entidad 2 (767) y otro por el que pasarán los de la entidad 3 (955). Por tanto necesitamos enviar los mensajes, en función de su contenido a su canal correspondiente. Usaremos un enrutador.
<int:channel id="usuarioEntidadRouterChannel" /> <int:recipient-list-router input-channel="usuarioEntidadRouterChannel"> <int:recipient channel="cuentasUsuario088" selector-expression="payload.entidad.equals('088')" /> <int:recipient channel="cuentasUsuario767" selector-expression="payload.entidad.equals('767')" /> <int:recipient channel="cuentasUsuario955" selector-expression="payload.entidad.equals('955')" /> </int:recipient-list-router>
Creo que se entiende perfectamente, pero lo explico un poco. Este canal contiene un router que, en función del contenido del mensaje (recordemos que en este canal los mensajes entrantes son de tipo UsuarioEntidad), en concreto en función del valor del atributo entidad (método getEntidad) que nos dirá la entidad bancaria del cliente, enviamos el mensaje por un canal u otro.
9. Paso 5: obteniendo las cuentas del cliente para la entidad 088.
Vale, pues vamos a configurar el canal por el que pasarán los mensajes con los clientes de la entidad 088. Recordemos que debemos conectar con un WS para obtener las cuentas y saldos del cliente de dicha entidad. Este caso es muy parecido a cuando conectamos con el WS que devolvía los ids de usuarios en función de su NIF en el paso 2.
<int:channel id="cuentasUsuario088" /> <ws:outbound-gateway id="entidad088Gateway" request-channel="cuentasUsuario088" uri="http://${ws.entidad088.host}:${ws.entidad088.port}/${ws.entidad088.context}" marshaller="entidad088Marshaller" reply-channel="aggregatorResponseChannel" /> <bean id="entidad088Marshaller" class="com.autentia.spring.integration.prueba_spring_integration.Entidad088Marshaller" />
El marshaller es muy parecido al del caso anterior. En este caso, con la respuesta del servicio devolverá una lista de CuentaSaldo (clase que nos hemos creado para guardar esos datos).
10. Paso 6: obteniendo las cuentas del cliente para la entidad 767.
Recordemos que para obtener las cuentas de un usuario de la entidad bancaria 767 debemos consumir un Servicio REST mediante una petición GET.
<!-- Conecta con el servicio REST que devuelve las cuentas de los clientes de la entidad 767 --> <int:channel id="cuentasUsuario767" /> <http:outbound-gateway request-channel="cuentasUsuario767" reply-channel="aggregatorResponseChannel" url="http://${rest.entidad767.host}:${rest.entidad767.port}/${rest.entidad767.context}/{usuario}" http-method="GET" expected-response-type="java.lang.String" message-converters="entidad767Converter"> <http:uri-variable name="usuario" expression="payload.usuario" /> </http:outbound-gateway>
El responsable de convertir los datos de respuesta en una lista de CuentaSaldo será entidad767Converter:
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.StringWriter; import java.io.Writer; import java.util.ArrayList; import java.util.List; import javax.xml.transform.dom.DOMSource; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpOutputMessage; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.http.converter.HttpMessageNotWritableException; import org.springframework.integration.xml.source.DomSourceFactory; import org.springframework.stereotype.Component; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @Component public class Entidad767Converter implements HttpMessageConverter<List<CuentaSaldo>> { private static final Log LOG = LogFactory.getLog(Entidad767Converter.class); private static final List<MediaType> SUPPORTED_MEDIA_TYPES = new ArrayList<MediaType>(); static { SUPPORTED_MEDIA_TYPES.add(MediaType.APPLICATION_XML); SUPPORTED_MEDIA_TYPES.add(MediaType.TEXT_XML); } public boolean canRead(Class<?> clazz, MediaType mediaType) { return clazz.equals(String.class); } public boolean canWrite(Class<?> clazz, MediaType mediaType) { return false; } public List<MediaType> getSupportedMediaTypes() { return SUPPORTED_MEDIA_TYPES; } public List<CuentaSaldo> read(Class<? extends List<CuentaSaldo>> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { final String xmlResponse = getResponse(inputMessage.getBody()); LOG.debug("Received response from entity 767 " + xmlResponse); return getCuentas(xmlResponse); } private List<CuentaSaldo> getCuentas(final String response) { final List<CuentaSaldo> cuentas = new ArrayList<CuentaSaldo>(); final DOMSource source = (DOMSource) new DomSourceFactory().createSource(response); final NodeList nodelist = source.getNode().getChildNodes(); final Node cuentaNode = nodelist.item(0); if (cuentaNode.getLocalName().equals("cuenta")) { for (int i=0; i<nodelist.getLength(); i++) { final Node cuenta = nodelist.item(i); cuentas.add(getCuentaSaldo(cuenta)); } } return cuentas; } private CuentaSaldo getCuentaSaldo(final Node node) { final CuentaSaldo cuenta = new CuentaSaldo(); String numeroCuenta = ""; String saldo = ""; if (node.getLocalName().equals("cuenta")) { final NodeList cuentaAndSaldo = node.getChildNodes(); for (int i=0; i<cuentaAndSaldo.getLength(); i++) { final Node cuentaOrSaldoNode = cuentaAndSaldo.item(i); if (cuentaOrSaldoNode.getLocalName().equals("numeroCuenta")) { numeroCuenta = cuentaOrSaldoNode.getTextContent(); } else if (cuentaOrSaldoNode.getLocalName().equals("importe")) { saldo = cuentaOrSaldoNode.getTextContent(); } } } cuenta.setCuenta(numeroCuenta); cuenta.setSaldo(Float.valueOf(saldo)); return cuenta; } private String getResponse(InputStream inputStream) throws IOException { if (inputStream != null) { final Writer writer = new StringWriter(); char[] buffer = new char[1024]; try { final Reader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); int n; while ((n = reader.read(buffer)) != -1) { writer.write(buffer, 0, n); } } finally { inputStream.close(); } return writer.toString(); } else { return ""; } } public void write(List<CuentaSaldo> t, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { } }
11. Paso 7: obteniendo las cuentas del cliente para la entidad 955.
Y nos queda hacer lo mismo con la entidad 955. Recordemos que en este caso es una clase quien nos devolverá la lista de cuentas y saldos, por tanto, con un simple Service-Activator nos valdrá. En este ejemplo la clase corre dentro de la misma aplicación que Spring Integration (CuentasCliente955.java) pero si estuviese en otro entorno bastaría una invocación RMI.
<!-- Conecta con la clase java que devuelve las cuentas de los clientes de la entidad 955 --> <int:channel id="cuentasUsuario955" /> <int:service-activator input-channel="cuentasUsuario955" ref="cuentasCliente955ServiceActivator" output-channel="aggregatorResponseChannel" /> <bean id="cuentasCliente955ServiceActivator" class="com.autentia.spring.integration.prueba_spring_integration.CuentasCliente955ServiceActivator"> <constructor-arg name="cuentasService" ref="cuentasCliente955" /> </bean>
Nuestro Service-Activator:
import java.util.ArrayList; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.integration.annotation.ServiceActivator; import org.springframework.stereotype.Component; import com.autentia.spring.integration.prueba_spring_integration.clientes_955.CuentaSaldoCliente955; import com.autentia.spring.integration.prueba_spring_integration.clientes_955.CuentasCliente955; @Component public class CuentasCliente955ServiceActivator { private static final Log LOG = LogFactory.getLog(CuentasCliente955ServiceActivator.class); private final CuentasCliente955 cuentasCliente; @Autowired public CuentasCliente955ServiceActivator(CuentasCliente955 cuentasCliente) { this.cuentasCliente = cuentasCliente; } @ServiceActivator public List<CuentaSaldo> handleCuentas(UsuarioEntidad usuario) { final List<CuentaSaldoCliente955> cuentas = cuentasCliente.getCuentasCliente(usuario.getUsuario()); LOG.debug("Received cuentas:" + cuentas + " from user: " + usuario.getUsuario()); return convertCuentaSaldo(cuentas); } private List<CuentaSaldo> convertCuentaSaldo(List<CuentaSaldoCliente955> cuentas) { final List<CuentaSaldo> convertedCuentas = new ArrayList<CuentaSaldo>(); for (CuentaSaldoCliente955 cuenta : cuentas) { convertedCuentas.add(new CuentaSaldo(cuenta.getCuenta(), cuenta.getSaldo())); } return convertedCuentas; } }
12. Paso 8: agregar todos los resultados y devolver la respuesta.
Bueno, pues ya queda poco. Lo único que nos falta es agrupar todos los resultados que nos han devuelto los servicios de las entidades bancarias y devolver los datos. Para ello haremos uso de un agregador (juntará todas las respuestas en un único mensaje) y un Service-Activator (devolverá la respuesta).
<int:channel id="aggregatorResponseChannel" /> <int:aggregator input-channel="aggregatorResponseChannel" ref="cuentasAggregator" method="addCuentas" output-channel="cuentasResponseChannel" /> <int:channel id="cuentasResponseChannel" /> <int:service-activator input-channel="cuentasResponseChannel" ref="cuentasResponseServiceActivator" />
El agregador recibe la lista de respuestas de todos los servicios anteriores y las une en un único mensaje (Cuentas):
import java.util.Collection; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.integration.annotation.Aggregator; import org.springframework.stereotype.Component; @Component public class CuentasAggregator { private static final Log LOG = LogFactory.getLog(CuentasAggregator.class); @Aggregator public Cuentas addCuentas(List<Collection<CuentaSaldo>> cuentasEntidades) { final Cuentas cuentas = new Cuentas(); for (Collection<CuentaSaldo> cuentasEntidad : cuentasEntidades) { LOG.debug("Adding " + cuentasEntidad + " to final data"); for (CuentaSaldo cuentaSaldo : cuentasEntidad) { cuentas.addCuenta(cuentaSaldo); } } return cuentas; } }
El Service-Activator recibe los resultados unificados en el bean Cuentas y prepara la respuesta:
import javax.xml.transform.Source; import org.springframework.integration.annotation.ServiceActivator; import org.springframework.stereotype.Component; import org.springframework.xml.transform.StringSource; @Component public class CuentasResponseServiceActivator { @ServiceActivator public Source handleCuentas(final Cuentas cuentas) { final StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append(""); for (CuentaSaldo cuenta : cuentas.getCuentas()) { appendCuenta(cuenta, stringBuilder); } stringBuilder.append(""); return new StringSource(stringBuilder.toString()); } private void appendCuenta(final CuentaSaldo cuenta, final StringBuilder stringBuilder) { stringBuilder.append("").append("").append(cuenta.getCuenta()).append("") .append("").append(cuenta.getSaldo()).append("").append(""); } }
13. Probando el ejemplo.
Hemos colgado todo el código fuente del ejemplo: tanto servicios como la plataforma con Spring Integration en un repositorio público https://github.com/autentia/esb-tutorial, para que se lo descargue quien quiera.
Para probarlo arrancamos los Servicios Web (localhost:8081) y el Servicio REST (localhost:8081), así como la aplicación de Spring integration (localhost:8080). Todas las aplicaciones que hemos colgado en el repositorio tienen su correspondiente .jmx para que puedan ser probados con JMeter.
Una vez tenemos todo arrancado enviamos la siguiente petición a la plataforma (aplicación con Spring Integration):
Y la respuesta que nos devuelve es la esperada, la lista de todas las cuentas y saldos de todas las entidades bancarias del cliente.
14. Referencias.
- Spring Integration: Reference Manual.
- Introducción a Spring Integration
- Código fuente del tutorial.
15. Conclusiones.
Después de la introducción a Spring Integration hemos querido hacer un ejemplo mucho más completo para demostrar todo lo que se puede hacer, o al menos una parte, con este sistema de integración. Recordemos que Spring Integration nos provee de mucha más funcionalidad de la que hemos visto en este tutorial, pero creo que con este ejemplo, cualquiera se puede hacer una idea del alcance de Spring Integration y, en general, de las plataformas de integración.
Espero que este tutorial os haya sido de ayuda. Un saludo.
Miguel Arlandy
Twitter: @m_arlandy
Hola Miguel,
Muchas Gracias por el tutorial.
He querido probar este ejemplo completo de Spring-Integration pero hay un problema con los archivos .xsd hay un error en el acceso a las URLs: «http://adictosaltrabajo.com/spring/ws/schemas» y «http://adictosaltrabajo.com/spring/ws/schemas/088». Podrías por favor proporcionarnos los archivos necesarios para poder ejecutar el ejemplo correctamente.
Gracias nuevamente.
La configuracion de Spring Integration se puede hacer con anotaciones?
I don’t understand a word of spanish, but you saved my life! 🙂 thanks for your well-documented and commented code!!!
Greetings from Switzerland