Download pdf - Apache Camel

Transcript
Page 1: Apache Camel

Abimael Desales López

Java Developers Mexico

www.facebook.com/JavaDevelopersMexico

Page 2: Apache Camel

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.

Page 3: Apache Camel

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.

Page 4: Apache Camel

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.

Page 5: Apache Camel

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.

Page 6: Apache Camel

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

Page 7: Apache Camel

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

Page 8: Apache Camel

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.

Page 9: Apache Camel

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”)

Page 10: Apache Camel

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.

Page 11: Apache Camel

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.

Page 12: Apache Camel

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.

Page 13: Apache Camel

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.

Page 14: Apache Camel

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.

Page 15: Apache Camel

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>

Page 16: Apache Camel

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.

Page 17: Apache Camel

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.

Page 18: Apache Camel

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.

Page 19: Apache Camel

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.

Page 20: Apache Camel

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.

Page 21: Apache Camel

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

Page 22: Apache Camel

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)

Page 23: Apache Camel

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.

Page 24: Apache Camel

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.

Page 25: Apache Camel

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.

Page 26: Apache Camel

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.

Page 27: Apache Camel

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.

Page 28: Apache Camel

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.

Page 29: Apache Camel

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

Productor Consumidor

Figura 1.10 Cómo los endpoints trabajan con productores, consumidores, y un exchange

Page 30: Apache Camel

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.

Page 31: Apache Camel

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.

Page 32: Apache Camel

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.

Page 33: Apache Camel

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.

Page 34: Apache Camel

Figura 2.5 Los RoutesBuilders son usados para crear routes en Camel. Cada RouteBuilder puede crear múltiples routes en Camel.

Page 35: Apache Camel

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());

Page 36: Apache Camel

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:

Page 37: Apache Camel

from(“ftp://rider.com/orders?username=rider&password=secret”)

El método from retorna un objeto RouteDefinition, en el cual puedes invocar un número de diferentes métodos que implementan EIPs y otros conceptos de mensajería.

Page 38: Apache Camel

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.

Page 39: Apache Camel

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

Page 40: Apache Camel

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.

Page 41: Apache Camel

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

Page 42: Apache Camel

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.

Page 43: Apache Camel

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.

Page 44: Apache Camel

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.

Page 45: Apache Camel

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”);

}

}

Page 46: Apache Camel

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());

}

}

Page 47: Apache Camel

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

Page 48: Apache Camel

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>

Page 49: Apache Camel

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

Page 50: Apache Camel

<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>

<bean id="ftpToJmsRoute" class="camelinaction.FtpToJMSRoute"/>

<camelContext xmlns="http://camel.apache.org/schema/spring">

<routeBuilder ref="ftpToJmsRoute"/>

</camelContext>

</beans>

Page 51: Apache Camel

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");

}

}

Page 52: Apache Camel

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.

Page 53: Apache Camel

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>

Page 54: Apache Camel

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.

Page 55: Apache Camel

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"));

}

}

Page 56: Apache Camel

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>

Page 57: Apache Camel

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.

Page 58: Apache Camel

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.

Page 59: Apache Camel

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");

Page 60: Apache Camel

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.

Page 61: Apache Camel

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”);

Page 62: Apache Camel

Enrutamientos y EIPs context.addRoutes(new RouteBuilder() {

public void configure() {

from("file:src/data?noop=true").to("jms:incomingOrders");

from("jms:incomingOrders")

.choice()

.when(header("CamelFileName")

.endsWith(".xml"))

.to("jms:xmlOrders")

.when(header("CamelFileName")

.endsWith(".csv"))

.to("jms:csvOrders");

from("jms:xmlOrders").process(new Processor() {

public void process(Exchange exchange) throws Exception {

System.out.println("Received XML order: "

+ exchange.getIn().getHeader("CamelFileName"));

}

});

from("jms:csvOrders").process(new Processor() {

public void process(Exchange exchange) throws Exception {

System.out.println("Received CSV order: "

+ exchange.getIn().getHeader("CamelFileName"));

}

});

}

});

Page 63: Apache Camel

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.

Page 64: Apache Camel

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.

Page 65: Apache Camel

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>

Page 66: Apache Camel

Enrutamientos y EIPs

Usando Multicasting

Figura 2.13. Un multicast envía un mensaje a un número de receptores específicos.

Page 67: Apache Camel

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.

Page 68: Apache Camel

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.

Page 69: Apache Camel

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"));

Page 70: Apache Camel

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"));

Page 71: Apache Camel

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>

Page 72: Apache Camel

Enrutamientos y EIPs

Usando el método wireTap

Page 73: Apache Camel

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");

Page 74: Apache Camel

GRACIAS

Dudas y comentarios al email:

[email protected]

Y en la página

www.facebook.com/JavaDevelopersMexico

No olviden regalarnos un like.