2. Endpoints
Un endpoint es una abstracción que modela el extremo
de un canal de mensaje a través del cual un sistema
puede enviar o recibir mensajes.
En primer lugar vamos a ver cómo se pueden usar las
URIs para configurar Camel para comunicar sobre FTP y
JMS, los cuales son dos de los protocolos de transporte
más usados.
3. Trabajando con archivos sobre FTP
Una de las cosas que hace a Camel fácil de comprender
es la URI del endpoint. Especificando una URI, puedes
identificar el componente que quieres usar y cómo ese
componente está configurado. Puedes decidir entonces
ya sea enviar mensajes al componente configurado por
esta URI, o consumir mensajes de él.
Para comprender de una manera más clara, se ilustrarán
los conceptos a través de un ejemplo: Auto Partes Rider,
un negocio de partes de motocicletas ficticio, que provee
partes a fabricantes de motocicletas.
4. Al paso de los años, han cambiado la forma en que reciben
órdenes varias veces. Inicialmente colocaban órdenes
cargando archivos de valores separados por comas (CSV) a un
servidor FTP. El formato de mensaje fue cambiado a la postre
a XML. Actualmente proporcionan un sitio web a través del
cual las órdenes son emitidas como mensajes XML sobre
HTTP.
Auto Partes Rider solicita a los nuevos clientes usar la interfaz
web para colocar las órdenes, pero debido a los SLAs con los
clientes existentes, deben mantener todos los formatos e
interfaces de mensajes viejos en funcionamiento.
5. Todos estos mensajes son convertidos a un formato POJO
antes de ser procesado. En la siguiente figura se muestra una
lista de alto nivel del sistema de procesamiento de órdenes.
Fig. 1. Un cliente tiene dos formas de emitir órdenes al sistema de manejo de órdenes Auto Partes
Rider: ya sea cargando el archivo de órdenes plano a un servidor FTP o emitiendo una orden a
través de la tienda web de Auto Partes Rider. Eventualmente todas las órdenes son enviadas vía
JMS para procesarlas en Auto Partes Rider.
6. Como una primera asignación, necesitarás implementar el
módulo FTP en el sistema frontend de órdenes de Rider.
Implementar el módulo FTP involucrará los siguientes pasos:
1. Votear en el FTP server y descargar nuevas órdenes
2. Convertir los archivos de órdenes a mensajes JMS
3. Enviar el mensaje a la queue JMS incomingOrdenes
7. Para descargar nuevas órdenes del servidor FTP,
necesitas hacer lo siguiente:
1. Conectar al servidor FTP rider.com en el puerto FTP
default de 21
2. Proporcionar un username de “rider” y password de
“secret”
3. Cambiar el directorio a “ordenes”
4. Descargar cualesquiera archivos de órdenes nuevas
8. Camel buscará primero el esquema ftp en el registro de
componentes, el cual resolverá al FTPComponent. El
FTPComponent entonces funciona como una factory,
creando el FTPEndpoint basado en la ruta del contexto y las
opciones.
La ruta del contexto de rider.com/ordenes le dice al
FTPComponent que debe loguearse en el servidor FTP en
rider.com en el puerto FTP default y cambiar el directorio a
“ordenes”. Finalmente, las únicas opciones especificadas son
username y password, las cuales son usadas para
loguearse en el servidor FTP.
9. El FTPComponent no es parte del módulo camel-core, así
que tienes que agregar una dependencia adicional a tu
proyecto. Usando Maven sólo tienes que agregar la siguiente
dependencia a tu POM:
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-ftp</artifactId>
<version>2.5.0</version>
</dependency>
La URI de este endpoint se estará usando para descargar
órdenes del servidor FTP. Para hacerlo así, necesitas usarlo en
un nodo from del DSL de Camel:
from(“ftp://rider.com/ordenes?username=rider&password=secret”)
10. Lo anterior es todo lo que necesitas hacer para consumir
archivos desde un servidor FTP.
ftp://rider.com/ordenes?username=rider&password=secret
Esquema Ruta del contexto Opciones
Figura 2. La URI de un endpoint Camel consiste de tres partes: un esquema,
una ruta de contexto, y una lista de opciones.
11. Enviando a una Queue JMS
Camel proporciona soporte extensivo para conectar
proveedores habilitados para JMS. Por ahora sólo cubriremos
lo suficiente para que puedas completar tu primer tarea para
Auto Partes Rider. Recordemos que necesitas descargar
órdenes de un servidor FTP y enviarlas a una queue JMS.
JMS
Java Message Service es una API Java que te permite crear,
recibir, y leer mensajes. Además obliga que la mensajería sea
asíncrona y tenga elementos específicos de confiabilidad,
como entrega garantizada y una-y-solamente-una-vez. JMS
es la solución de facto en la comunidad Java.
12. En JMS, los consumidores y productores de mensajes se hablan uno
a otro a través de un intermediario –un destino JMS. Como se
muestra en la figura 3, un destino puede ser una queue o un topic.
Las Queues son estrictamente punto-a-punto, donde cada mensaje
tiene sólo un consumidor. Los Topics operan en un esquema
publish/subscribe; un sólo mensaje puede ser entregado a muchos
consumidores si ellos se han suscrito al topic.
JMS también proporciona una ConnectionFactory la cual los
clientes (como Camel) pueden usar para crear una conexión con un
proveedor JMS. A los proveedores JMS generalmente se les refiere
como brokers debido a que ellos gestionan la comunicación entre
el productor del mensaje y el consumidor del mensaje.
13. Cómo usar camel para usar un Proveedor JMS
Para conectar camel a un proveedor JMS específico, necesitas
configurar el componente JMS de Camel con una
ConnectionFactory apropiada.
Apache ActiveMQ es uno de los proveedores JMS open source más
populares; y es el broker JMS que el equipo de Camel usa para
probar el componente JMS. Por lo cual estaremos usándolo para
demostrar los conceptos JMS en el artículo.
Figura 3. Hay dos tipos de destinos
JMS: queues y topics. La queue es un
canal punto a punto, donde cada
mensaje tiene un sólo receptor. Un
topic entrega una copia del mensaje a
todos los clientes quienes se hayan
suscrito para recibirlo.
14. Así que en el caso de Apache ActiveMQ, puedes crear una
ActiveMQConnectionFactory que apunta a la ubicación del
broker ActiveMQ que está corriendo.
ConnectionFactory cf = new ActiveMQConnectionFactory(“vm://localhost”);
La URI vm:/localhost significa que debes conectarte a un
broker embebido llamado “localhost” corriendo dentro de la
JVM actual. El conector de transporte “vm” en ActiveMQ crea
un broker bajo demanda si uno no está corriendo aún. de
forma que es muy práctico para aplicaciones JMS de pruebas;
para escenarios de producción, se recomienda que conectes
un broker que ya esté en ejecución. Además, en escenarios de
producción se recomienda que se use un pool de conexiones
cuando se conecta a un broker JMS.
15. A continuación, cuando creas tu CamelContext puedes agregar el
componente JMS como sigue:
CamelContext context = new DefaultCamelContext();
context.addComponent(“jms”,
JmsComponent.jmsComponentAutoAcknowldge(connectionFactory));
El componente JMS y la connection factory específica de ActiveMQ
no son parte del módulo core de Camel. Con fines de usar este,
necesitarás agregar algunas dependencias a tu proyecto basado en
Maven. Para el componente JMS plano, todo lo que tienes que
hacer es esto:
<dependency>
<groupId>org.apache.camel</groupId>
<artefactId>camel-jms</artefactId>
<version>5.3.2</version>
</dependency>
16. La connection factory viene directamente de ActiveMQ, de forma
que tendrás que agregar la siguiente dependencia:
<dependency>
<groupId>org.apache.activemq</groupId>
<artefactId>activemq-core</artefactId>
<version>5.3.2</version>
</dependency>
Ahora que has configurado el componente JMS para conectar a un
broker JMS real, es hora de ver cómo las URIs pueden ser usadas
para especificar el destino.
17. Usando URIs para especificar el Destino
Una vez configurado el componente JMS, puedes enviar y recibir
mensajes JMS a tu gusto. Debido a que estás usando URIs es muy
fácil de configurar.
Digamos que quieres enviar un mensaje JMS a la queue llamada
incomingOrders. La URI en este caso sería:
jms:queue:incomingOrders
Esto es muy autoexplicatorio. El prefijo “jms” indica que estás
usando el componente JMS que configuraste antes. Especificando
“queue”, el componente JMS sabe que enviará a una queue llamada
incomingOrders. Aún pudiste haber omitido el calificador queue,
debido a que el comportamiento default es enviar a una queue más
que a un topic.
18. Usando el DSL Java de Camel puedes enviar un mensaje a la queue
incomingOrders usando la palabra clave to, como en este caso:
…to(“jms:queue:incomingOrders”)
Lo cual puede ser leído como enviando a la queue JMS llamada
incomingOrders.
19. Creando routes en Java
Antes de adentrarnos en los enrutamientos es necesario conocer
unos conceptos básicos de los mismos.
CamelContext
La figura 1.7 muestra los servicios más notables que mantiene
unidos el CamelContext. Estos servicios son descritos en la tabla 1.
Figura 1.7 El CamelContext provee acceso a
muchos servicios útiles, los más notables
siendo componentes, convertidores de tipo,
un registro, endpoints, routes, formatos de
datos, y lemguajes.
20. Servicio Descripción
Componentes Contiene los componentes usados. Camel es capaz de cargar
componentes en el aire, ya sea autodescubriendo en el claspath o
cuando un nuevo bundle es activado en un contenedor OSGi.
Endpoints Contiene los endpoints que han sido creados
Routes Contiene los routes que han sido agregados.
Convertidores de tipo Contiene los convertidores de tipo cargados. Camel tiene un
mecanismo que te permite convertir manual o automáticamente de
un tipo a otro.
Formatos de datos Contiene los formatos de datos cargados
Registro Contiene un registro que te permite localizar beans. Por default,
este será un registro JNDI. Si estás usando Camel desde Spring, será
el ApplicationContext. Puede ser un registro OSGi si usas Camel en
un contenedor OSGi.
Lenguajes Contiene los lenguajes cargados. Camel te permite usar diferentes
lenguajes para crear exprsiones.
21. Motor de Enrutamiento
El motor de enrutamiento de Camel es lo que realmente mueve los
mensajes tras bambalinas. Este motor no es expuesto al
desarrollador, pero debe estar consciente de que está ahí y hace el
trabajo pesado, asegurando que los mensajes sean enrutados
adecuadamente.
Routes
Las routes son obviamente una abstracción de Camel. La forma
más simple de definir una route es como una cadena de
procesadores. Hay muchas razones para usar routes en aplicaciones
de mensajería
22. Desacoplando los clientes de los servidores, y productores de
consumidores, routes puede:
Decidir dinámicamente a qué servidor un cliente invocará
Proporcionar una forma sensible de agregar procesamiento extra
Permitir a los clientes y servidores ser desarrollados de forma
independiente
Permitir a los clientes y servidores ser apagados (usando mocks)
por propósitos de prueba
Promover mejores prácticas de diseño conectando sistemas
distintos que hacen una cosa bien
Mejorar las características y funcionalidades de algunos sistemas
(tales como brokers de mensajes y ESBs)
23. Domain Specific Languaje (DSL)
Para vincular (cablear) procesadores y/a endpoints para formar
routes, camel define un DSL. En Camel, DSL significa una API Java
fluida que contiene métodos nombrados por téminos de EIP.
Considera este ejemplo:
from(“file:data/inbox”)
.filter().xpath(“/order[not(@test)]”)
.to(“jms:queue:order”)
Aquí, en una sola declaración Java, defines una route que consume
archivos de un endpoint de archivos . Los mensajes son luego
enrutados al filtro EIP, el cual usará un predicado XPath para
probar si el mensaje es una orden de prueba o no. Si un mensaje
pasa el test, es reenviado al endpoint JMS. Los mensajes que fallen
el filtro de prueba serán desechados.
24. Camel proporciona varios lenguajes DSL, de forma que puedas
definir la misma route usando el DSL de Spring, como en este caso:
<route>
<from uri=“file:data/inbox”>
<filter>
<xpath>/order[Not(@test)]</xpath>
<to uri=“jms:queue:order”>
</filter>
</route>
El DSL proporciona una buena abstracción para los usuarios de
Camel con las cuales construir aplicaciones. Aunque, tras
bambalinas, un route realmente está compuesto de un grafo de
processors.
25. Processor
El processor es un concepto de core de Camel que representa un
nodo capaz de usar, crear, o modificar un exchange entrante.
Durante el enrutamiento, los exchanges fluyen de un processor a
otro; como tal, puedes pensar de un route como un grafo teniendo
especializado processors como los nodos, y líneas que conectan las
salidas de un processor a la entrada de otro. Muchos de los
processors son implementaciones de EIPs, pero uno podría
fácilmente implementar su propio processor personalizado e
insertarlo en un route.
Así que, ¿cómo los exchanges entran o salen de este grafo
processor? Para descubrirlo necesitaremos ver los componentes y
endpoints.
26. Componente
Los componentes son los principales puntos de extensión en
Camel. Hasta la fecha, hay alrededor de 80 componentes en el
ecosistema de Camel que van del rango en función de protocolos de
datos, a DSLs, formatos de datos, etc. Además de que puedes crear
tus propios componentes para Camel.
Desde un punto de vista de programación, los componentes son
completamente simples: están asociados con un nombre, que es
usado en una URI, y actúan como una factory de endpoints. Por
ejemplo, un FileComponent es referenciado por un file en una
URI, y crea FileEndpoints. El endpoint es tal vez aún un
concepto más fundamental en Camel.
27. Endpoint
Un endpoint es la abstracción Camel que modela el extremo de un
canal a través del cual un sistema puede enviar o recibir mensajes.
Esto se ilustra en la figura 1.8
Figura 1.8 Un endpoint actúa como una interface neutral permitiendo a los
sistemas integrarse.
28. En Camel, puedes configurar endpoints usando URIs, como es
file:data/inbox?delay=5000, y puedes referirte a enpoints de esa
forma. En tiempo de ejecución, Camel buscará un endpoint basado
en la notación de URI.
Productor (Producer)
Un productor es la abstracción Camel que se refiere a una entidad
capaz de crear y enviar un mensaje a un endpoint. La figura 1.10
ilustra donde el productor se ajusta con otros conceptos Camel.
Cuando un mensaje necesita ser enviado a un endpoint, el
productor creará un exchange y lo llena con datos compatibles con
ese endpoint particular. Por ejemplo, un FileProducer escribirá el
body de un mensaje a un archivo. Por otro lado, un JMSProducer,
mapeará el mensaje Camel antes de enviarlo a un destino JMS.
29. Esta es una característica importante de Camel, ya que oculta la
complejidad de interactuar con transportes particulares .
crea
usa
crea
crea
crea
usa
usa
ProductorConsumidor
Figura 1.10 Cómo los endpoints trabajan
con productores, consumidores, y un
exchange
30. Consumidor
Un consumidor es el servicio que recibe mensajes producidos por
un productor, los envuelve en un exhange, y los envía a ser
procesados. Los consumidores son la fuente de los exchanges que
están siendo enrutados en Camel.
Para crear un nuevo exchange, un consumidor usará el endpoint
que envuelve el payload que está siendo consumido. Un processor
es luego usado para iniciar el enrutamiento del exchange en Camel
usando el motor de enrutamiento.
En Camel hay dos clases de consumidores: consumidores guiados
por eventos y consumidores de voteo. Las diferencias entre estos
consumidores son importantes, debido a que ellos ayudan a
resolver problemas diferentes.
31. Consumidor guiado por evento
El consumidor más familiar es probablemente el consumidor guiado por
evento, el cual se ilustra en la figura 1.11
Esta clase de consumidor está principalmente asociado con la arquitectura
cliente servidor y web services. También se le refiere como un receptor
asíncrono en el mundo EIP. Un consumidor guiado por evento escucha en
un canal de mensaje particular, usualmente un puerto TCP/IP o una
queue JMS, y espera para que un cliente le envíe mensajes. Cuando un
mensaje llega, el consumidor despierta y toma el mensaje para procesarlo.
Figura 1.1 Un consumidor guiado por evento
espera ocioso hasta que un mensaje llega,
punto en el cual despierta y consume el
mensaje.
32. Consumidor de voteo
La otra clase de consumidor es el consumidor de voteo, el cual se
ilustra en la figura 1.12.
En contraste al consumidor guiado por eventos, el consumidor de
voteo va activamente y obtiene mensajes desde una fuente
particular, como lo es un servidor FTP. El consumidor de voteo es
también conocido como receptor síncrono en la jerga EIP, debido a
que no voteará por más mensajes hasta que haya finalizado el
procesamiento del mensaje actual. Un sabor común del
consumidor de voteo es el consumidor de voteo programado, el
cual votea en intervalos programados. Todos los transportes de
File, FTP, y email usan consumidores de voteo programados.
33. Ahora que hemos cubierto todos los conceptos básicos de Camel,
es hora de proseguir con la creación de los routes.
El RouteBuilder no es el route final que el CamelContext usará
en tiempo de ejecución; es un builder para uno o más routes, los
cuales son luego agregados al CamelContext.
El método addRoutes del CamelContext acepta un RoutesBuilder,
no sólo un RouteBuilder. La interface RoutesBuilder tiene un sólo
método definido:
void addRoutesToCamelContext(CamelContext context) throws Exception;
Esto significa que podrías construir tu propia clase personalizada
para construir routes Camel. Aunque, la forma más común de
construir routes es usando la clase RouteBuilder, que
implementa RoutesBuilder. La clase RouteBuilder te da acceso
al DSL Java de camel para creación de route.
34. Figura 2.5 Los RoutesBuilders son usados para crear routes en Camel. Cada
RouteBuilder puede crear múltiples routes en Camel.
35. Usando el RouteBuilder
Para usar la clase org.camel.builder.RouteBuilder, extiendes de ella
una clase e implementas el método configure, como esto:
class MyRouteBuilder extends RouteBuilder{
public void configure() throws Exception{
…
}
}
Luego necesitas agregar la clase al CamelContext con el método
addRoutes:
CamelContext context = new DefaultCamelContext();
context.addRoutes(new MyRouteBuilder());
36. Alternativamente, puedes combinar el RouteBuilder y la
configuración del CamelContext agregando una clase RouteBuilder
anónima directamente en el CamelContext, como esta:
CamelContext context = new DefaultCamelContext();
context.addRoutes(new RouteBuilder(){
public void configure(){
…
}
});
En el método configure tú defines tus routes usando el DSL Java.
El método from acepta una URI de endpoint como argumento.
Puedes agregar la URI de un endpoint FTP para conectar al
servidor de órdenes de AutoPartes Rider de la siguiente forma:
38. El DSL Java
Los lenguajes específicos de dominio (DSLs) son lenguajes de
computadora que se dirigen a problemas específicos de dominio, más que
a un dominio de propósito general como la mayoría de los lenguajes de
programación.
El dominio de Camel es la integración empresarial, de forma que el DSL
Java es esencialmente un conjunto de interfaces fluidas que contienen
métodos nombrados después de términos del libro EIP. En el editor de
Eclipse, toma una mirada a lo que está disponible usando autocompletar
después de un método from en el RouteBuilder. Por ahora, sólo selecciona
el método to y finaliza el route con un punto y coma. Cada declaración
Java que inicia con un método from en el RouteBuilder crea un nuevo
Route. Este nuevo route ahora completa tu primer tarea de AutoPartes
Rider –consumir órdenes de un servidor FTP y enviarlas a la queue JMS
incomingOrders.
39. Código 1. Voteando por mensajes FTP y enviándolos a la queue incomingOrders
import javax.jms.ConnectionFactory;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.camel.CamelContext;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.jms.JmsComponent;
import org.apache.camel.impl.DefaultCamelContext;
public class FtpToJMSExample {
public static void main(String args[]) throws Exception {
CamelContext context = new DefaultCamelContext();
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("vm://localhost");
context.addComponent("jms", JmsComponent.jmsComponentAutoAcknowledge(connectionFactory));
context.addRoutes(new RouteBuilder() {
public void configure() {
from("ftp://rider.com/orders?username=rider&password=secret")
.to("jms:incomingOrders");
}
});
context.start();
Thread.sleep(10000);
context.stop();
}
}
Sentencia Java que
forma una route
40. El flujo de mensajes en este route simple puede ser visualizado
como una tubería básica, donde la salida del consumidor es
alimentada en el productor como entrada. Esto se describe en la
figura 2.8:
Figura 2.8 El route mostrado en el código anterior forma una tubería simple.
La salida del consumidor FTP es alimentada en la entrada del productor JMS.
La conversión del payload de archivo a mensaje JMS es hecha automáticamente.
41. Una cosa que puedes haber notado es que no hicimos alguna
conversión del tipo de archivo FTP al tipo mensaje JMS – esto fue
hecho automáticamente por la facilidad TypeConverter de Camel.
Puedes forzar las conversiones de tipo para que ocurran en
cualquier momento durante una route, pero frecuentemente no
tienes que preocuparte de todo ello.
Agregando un Processor
La interface Processor en Camel es un bloque de construcción
importante de routes complejos. Es una interface simple que tiene
un sólo método:
public void process(Exchange exchange) throws Exception
42. Esto te da acceso completo al intercambio de mensajes,
permitiéndote hacer casi todo lo que quieras con el payload o
headers.
Todos los EIPs en Camel son implementados como processors. Aún
puedes agregar un processor simple a tu route inline, como esta:
from(“ftp://rider.com/orders?username=rider&password=secret”).
process(new processor(){
public void process(Exchange exchange) throws Exception{
System.out.println(“Solo has descargado: ”
+ exchange.getIn().getHeader(“CamelFileName”));
}
}
Esta route imprimirá el nombre de archivo de la orden que fue
descargada antes de enviarla a la queue JMS.
43. Agregando este processor en medio del route, efectivamente lo has
agregado al pipeline conceptual que mencionamos antes. La salida
del consumido FTP es alimentada en el processor como entrada; el
processor no modifica el payload o headers del mensaje, de forma
que el exchange se mueve en el productor JMS como entrada.
El método principal de Camel para crear routes es a través del DSL
Java. Esto está, después de todo, construido en el módulo camel-
core.
Figura 2.9 Con un processor en
la mezcla, la salida del consumidor
FTP es ahora alimentada en el
processor, y luego la salida del
processor es alimentada en el
productor JMS.
44. Creando routes con Spring
Spring es el contenedor Java de Inversión de Control (IoC) más
popular. El framework core te permite “vincular” beans para formas
aplicaciones. Esta vinculación es hecha a través de un archivo de
configuración XML.
Inyección de bean y Spring
Crear una aplicaciones desde beans usando Spring es muy simple.
Todo lo que necesitas son algunos beans Java (clases), un archivo de
configuración XML de Spring, y un ApplicationContext. El
ApplicationContext es similar al CamelContext, en que es el
contenedor de runtime para Spring. Vamos a ver un ejemplo muy
simple.
45. Considera una aplicación que imprime un saludo seguido por tu
username. En esta aplicación no quieres que el saludo sea
hardcodeado, de forma que puedas usar una interface para romper
esta dependencia. Considera la siguiente interface:
public interface Greeter{
public String diHola();
}
Esta interface es implementada por las siguientes clases:
public class EnglishGreeter implements Greeter{
public String diHola(){
return “Hello” + System.getProperty(“user.name”);
}
}
46. public class DanishGreeter implements Greeter{
public String diHola(){
return “Davs” + System.getProperty(“user.name”);
}
}
Ahora puedes crear una aplicación greeter como sigue:
public class GreeterMeBean{
public Greeter greeter;
public void setGreeter(Greeter greeter){
this.greeter = greeter;
}
public void execute(){
System.out.println(greeter.diHola());
}
}
47. Esta aplicación sacará un saludo diferente dependiendo de cómo lo
configures. Para configurar esta aplicación usando Spring, puedes hacer
algo como esto:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="myGreeter" class="camelinaction.EnglishGreeter"/>
<bean id="greetMeBean" class="camelinaction.GreetMeBean">
<property name="greeter" ref="myGreeter"/>
</bean>
</beans>
Este archivo XML instruye a Spring a hacer lo siguiente:
1. Crear una instancia de EnglishGreeter y nombrar al bean myGreeter
2. Crear una instancia de GreetMeBean y nombrar al bean greetMeBean
3. Asignar la referencia de la propiedad greeter del GreetMeBean al
bean llamado myGreeter
48. Esta configuración de beans es llamada wiring.
Ahora te toca cargar el archivo XML de configuración de Spring en tu
aplicación para poder hacer uso de los beans inyectados.
Para hacer las cosas más fáciles a los ojos, Camel utiliza los mecanismos
de extensión de Spring para proporcionar sintaxis XML personalizada
para conceptos Camel en el archivo XML de Spring. Para cargar un
CamelContext en Spring, puedes hacer lo siguiente:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://camel.apache.org/schema/spring
http://camel.apache.org/schema/spring/camel-spring.xsd">
...
<camelContext xmlns="http://camel.apache.org/schema/spring"/>
</beans>
49. Lo anterior automáticamente iniciará un SpringCamelContext, la
cual es una subclase del DefaultCamelContext que usaste para el
DSL Java. Además nota que tuviste que incluir la definición de
esquema XML http://camel.apache.org/schema/spring/camel-
spring.xsd en el archivo XML –que es necesario para importar los
elementos XML personalizados.
Este snippet no va a hacer nada por sí solo. Necesitas decirle a
Camel qué routes va a usar, como lo hiciste cuando usabas el DSL
Java. El siguiente código usa Spring para producir los mismos
resultados que el listado 2.1
51. Puedes haber notado que nos estamos refiriendo a la clase
camelinaction.FtpToJMSRoute como un RouteBuilder. Con el fin
de reproducir el DSL Java del listado 2.1, tienes que factorizar el
RouteBuilder anónimo en su propia clase nombrada. La clase
FtpToJMSRoute se parece a la siguiente:
public class FtpToJMSRoute extends RouteBuilder {
public void configure() {
from("ftp://rider.com" +
"/orders?username=rider&password=secret")
.to("jms:incomingOrders");
}
}
52. Lo que hemos visto de la integración de Camel con Spring es
adecuado, pero no está tomando completa ventaja de la
metodología de Spring sin usar código. Para completamente
invertir el control de crear aplicaciones usando XML Spring, Camel
proporciona extensiones XML personalizadas a las que llamamos el
DSL Spring. El DSL Spring te permite hacer casi todo lo que puedes
hacer en el DSL Java.
Continuemos con el ejemplo de AutoPartes Rider mostrado en el
listado 2.2, pero esta vez especificarás las reglas de enrutamiento
definidas en el RouteBuilder puramente en XML. El siguiente XML
Spring hace esto.
53. Listado 2.3 Ejemplo DSL Spring que produce el mismo resultado que el listado 2.1
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://camel.apache.org/schema/spring
http://camel.apache.org/schema/spring/camel-spring.xsd">
<bean id="jms" class="org.apache.camel.component.jms.JmsComponent">
<property name="connectionFactory">
<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="vm://localhost" />
</bean>
</property>
</bean>
<camelContext xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="ftp://rider.com/orders?username=rider&password=secret"/>
<to uri="jms:incomingOrders"/>
</route>
</camelContext>
</beans>
54. En el listado anterior bajo el elemento camelContext remplazas el
routeBuilder con el elemento route. En el elemento route,
especificas el route usando elementos con nombres similares a
aquellos usados dentro del RouteBuilder del DSL Java. Este listado
es funcionalmente equivalente a la versión DSL de Java en el listado
2.1 y la combinación de Spring más el DSL Java.
El endpoint file cargará archivos de órdenes del directorio src/data
relativo. La propiedad noop configura el endpoint para que deje el
archivo como está después de procesarlo, esta opción es muy útil
para testing.
Este route no desplegará algo interesante aún. Necesitas agregar un
paso de procesamiento adicional para testing.
55. Agregando un Processor
Agregar pasos de procesamiento adicional es simple, como en el
DSL Java, aquí agregarás un procesador personalizado como lo
hiciste en la sección 2.3.2.
Debido a que no puedes referirte a una clase anónima en el XML
Spring, necesitas refactorizar el procesador anónimo en la siguiente
clase:
public class DownloadLogger implements Processor {
public void process(Exchange exchange) throws Exception {
System.out.println("We just downloaded: "
+ exchange.getIn().getHeader("CamelFileName"));
}
}
56. Puedes ahora usarla en el route de tu DSL Spring como sigue:
<bean id="downloadLogger" class="camelinaction.DownloadLogger"/>
<camelContext xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="file:src/data?noop=true"/>
<process ref="downloadLogger"/>
<to uri="jms:incomingOrders"/>
</route>
</camelContext>
57. Enrutamientos y EIPs
Usando un Enrutador Basado en Contenido (CBR)
Como su nombre lo implica, un Enrutador Basado en Contenido (CBR) es
un router de mensaje que enruta un mensaje a un destino basado en su
contenido. El contenido podría ser un header de mensaje, el tipo de dato
del payload, parte del mismo payload.
Para demostrar, vamos atrás a Autopartes Rider. Algunos clientes han
empezado a cargar órdenes al servidor FTP en el formato XML más nuevo
más que CSV. Lo que significa que tienes dos tipos de mensajes entrantes
a la queue incomingOrders. No hemos tocado esto antes, pero necesitas
convertir las órdenes entrantes en un formato POJO interno. Obviamente
necesitas hacer diferentes conversiones para los diferentes tipos de
órdenes entrantes.
Como una posible solución, podrías usar la extensión del nombre de
archivo si un mensaje de orden particular debe ser enviado a la queue para
órdenes CSV o a una queue para órdenes XML.
58. Enrutamientos y EIPs
Usando un Enrutador Basado en Contenido (CBR)
Figura 2.10. El CBR enruta mensajes basado en su contenido. En este caso, la
extensión filename (como un header del mensaje) es usado para determinar
a qué queue enrutar.
59. Enrutamientos y EIPs
Como lo viste antes, puedes usar el header CamelFileName
asignado por el consumidor FTP para obtener el nombre de archivo.
Para hacer el enrutamiento condicional requerido por el CBR,
Camel introduce algunas keywords en el DSL. El método choice
crea un processor CBR, y las condiciones son agregadas siguiendo
choice con una combinación de un método choice con una
combinación de un método when y un predicado.
from("jms:incomingOrders")
.choice()
.when(predicate)
.to("jms:xmlOrders")
.when(predicate)
.to("jms:csvOrders");
60. Enrutamientos y EIPs
Puedes haber notado que no llenamos el predicado requerido por
cada método when. Un predicado en Camel es una simple interface
que sólo tiene un método matches:
public interface Predicate {
boolean matches(Exchange exchange);
}
Por ejemplo, puedes pensar de un predicado como una condición
booleana en una sentencia if Java.
Probablemente no quieras buscar por ti mismo dentro del exchange
y hacer una comparación. Afortunadamente, los predicados son
construidos de expresiones, y las expresiones son usadas para
extraer resultados basados de un exchange basado en el contenido
de la expresión.
61. Enrutamientos y EIPs
Hay muchos lenguajes de expresión de los cuales elegir en Camel,
algunos de los cuales incluyen Simple, EL, JXPath, Mvel, OGNL,
PHP, BeanShell, JavaScript, Groovy, Python, Ruby, XPath y XQuery.
En el RouteBuilder, puedes comenzar usando el método header,
que retorna una expresión que evaluará al valor del header. Por
ejemplo, header(“CamelFileName”) creará una expresión que
resolverá al valor del header CamelFileName en el siguiente
exchange. En esta expresión puedes invocar muchos métodos para
crear un predicado. Así que verifica si la extensión del nombre del
archivo es .xml, puedes usar el siguiente predicado:
header(“CamelFileName”).endsWith(“.xml”);
63. Enrutamientos y EIPs
Usando Filtros de Mensajes
Figura 2.12. Un Filtro de Mensaje te permite filtrar los mensajes que no
importan basados en alguna condición. En este caso los mensajes de
prueba son filtrados.
64. Enrutamientos y EIPs
Usando filtros de mensajes
AutoPartes Rider ahora tiene un nuevo issue –su departamento de
QA ha expresado la necesidad de enviar órdenes de prueba en el
frontend web en vivo del sistema de órdenes. Tu solución actual
aceptaría estas órdenes como reales y enviarlas a los sistemas
externos para procesamiento. Tu has sugerido que QA pruebe en
un clon de desarrollo del sistema real, pero la gestión ha desechado
esa idea, alegando un presupuesto limitado. Lo que necesitas es
una solución que descarte estos mensajes de prueba mientras aún
se opera en las órdenes reales.
El EIP Message Filter, proporciona una buena forma de tratar con
esta clase de problemas. Los mensajes entrantes sólo pasan el filtro
si reúnen una cierta condición. Los mensajes que no reúnen la
condición son desechados.
65. Enrutamientos y EIPs
Usando filtros de mensajes
from("jms:xmlOrders").filter(xpath("/order[not(@test)]"))
.process(new Processor() {
public void process(Exchange exchange) throws Exception {
System.out.println("Received XML order: "
+ exchange.getIn().getHeader("CamelFileName"));
}
});
El anterior en el DSL Java, y el siguiente en el DSL de Spring
<route>
<from uri="jms:xmlOrders"/>
<filter>
<xpath>/order[not(@test)]</xpath>
<process ref="orderLogger"/>
</filter>
</route>
66. Enrutamientos y EIPs
Usando Multicasting
Figura 2.13. Un multicast envía un mensaje a un número de receptores
específicos.
67. Enrutamientos y EIPs
Usando Multicasting
Frecuentemente en las aplicaciones empresariales necesitarás
enviar una copia de un mensaje a varios destinos diferentes para
procesamiento. Cuando la lista de destinos es conocida adelante
del tiempo y es estática, puedes agregar un elemento al route que
consumirá mensajes desde un endpoint fuente y luego enviar el
mensaje a una lista de destinos.
68. Enrutamientos y EIPs
Usando lista de receptores
Figura 2.14. Una lista de receptores inspecciona los mensajes entrantes
y determina una lista de receptores basado en el contenido del
mensaje. En este caso, sólo es enviado a los destinos A, B, y D.
69. Enrutamientos y EIPs
Usando lista de receptores
Usando el EIP Recipient List. Una lista de receptores primero
inspecciona el mensaje entrante, luego genera una lista de
receptores deseados basados en el contenido del mensaje, y envía el
mensaje a esos receptores. Un receptor es especificado por una
URI. Nota que la lista de receptores es diferente que el multicast
debido a que la lista de receptores es dinámica.
Camel proporciona un método recipientList para implementar el
patrón EIP Recipient List. Por ejemplo, el siguiente route tomará la
lista de receptores desde un header llamado recipients, donde cada
recipiente es separado del siguiente por una coma:
from("jms:xmlOrders")
.recipientList(header("recipients"));
70. Enrutamientos y EIPs
En la versión DSL Java
from("jms:xmlOrders")
.setHeader("customer", xpath("/order/@customer"))
.process(new Processor() {
public void process(Exchange exchange) throws Exception {
String recipients = "jms:accounting";
String customer =
exchange.getIn().getHeader("customer", String.class);
if (customer.equals("honda")) {
recipients += ",jms:production";
}
exchange.getIn().setHeader("recipients", recipients);
}
})
.recipientList(header("recipients"));
71. Enrutamientos y EIPs
En la versión DSL Spring
<route>
<from uri="jms:xmlOrders" />
<setHeader headerName="customer">
<xpath>/order/@customer</xpath>
</setHeader>
<process ref="calculateRecipients" />
<recipientList>
<header>recipients</header>
</recipientList>
</route>
73. Enrutamientos y EIPs
Usando el método wireTap
Frecuentemente en aplicaciones empresariales es útil y necesario
inspeccionar cómo los mensajes fluyen a través del sistema. Por
ejemplo, cuando una orden falla, necesitas una forma de buscar
qué mensajes fueron recibidos fueron recibidos para determinar la
causa de la falla.
Usando el método wireTap en el DSL Java, puedes enviar una copia
del exchange a un destino secundario sin afectar el
comportamiento del resto del route.
from("jms:incomingOrders")
.wireTap("jms:orderAudit")
.choice()
.when(header("CamelFileName").endsWith(".xml"))
.to("jms:xmlOrders")
.when(header("CamelFileName").regex("^.*(csv|csl)$"))
.to("jms:csvOrders")
.otherwise()
.to("jms:badOrders");
74. GRACIAS
Dudas y comentarios al email:
it.adesales@gmail.com
Y en la página
www.facebook.com/JavaDevelopersMexico
No olviden regalarnos un like.