apache camel - parte ii

83
Abimael Desales López Java Developers México

Upload: abmel-lopessier

Post on 14-Jun-2015

1.381 views

Category:

Technology


6 download

DESCRIPTION

En esta segunda entrega se proporciona una descripción más cercana de las características de Apache Camel.

TRANSCRIPT

Page 1: Apache Camel - Parte II

Abimael Desales López

Java Developers México

Page 2: Apache Camel - Parte II

Contenido 1. Vista General de la Transformación de Datos

2. Transformando datos usando EIPs y Java

1. Usando el EIP Message Translator

2. Usando el EIP Content Enricher

3. Transformando XML

1. Transformando XML con XSLT

2. Transformando XML con marshalling de objetos

4. Transformando con formatos de datos

1. Formatos de datos provistos con Camel

2. Usando el formato de dato CSV de Camel

3. Usando el formato de dato Bindy de Camel

4. Usando el formato de dato JSON de Camel

5. Configurando formatos de datos Camel

6. Escribiendo tu propio formato de datos

Page 3: Apache Camel - Parte II

Contenido 5. Transformando con plantillas

1. Usando Apache Velocity

6. A cerca de los convertidores de tipo Camel

5. Cómo funciona el mecanismo convertidor de tipo Camel

6. Usando los convertidores de tipo Camel

7. Escribiendo tu propio convertidor de tipo

Page 4: Apache Camel - Parte II

1.Vista General de la Transformación de Datos Camel proporciona muchas técnicas para transformación de datos.

La transformación de datos es un término amplio que cubre dos tipos de transformación:

Transformación de formatos de datos. El formato de datos del cuerpo del mensaje es transformado de una forma a otra. Por ejemplo, un registro CSV es formateado como XML.

Transformación de tipo de datos. El tipo de datos del cuerpo del mensaje es transformado de un tipo a otro. Por ejemplo, un java.lang.String es transformado en un javax.jms.TextMessage.

La figura 3.1 ilustra el principio de transformar el cuerpo de un mensaje de un formato a otro. Esta combinación puede involucrar cualquier combinación de tipo y formato.

Page 5: Apache Camel - Parte II

En la mayoría de los casos la transformación de datos con la que te enfrentarás con Camel es transformación de formato, donde tienes que mediar entre dos protocolos. Camel tiene un mecanismo convertidor de tipo incorporado que puede convertir automáticamente entre dos tipos, lo cual reduce mucho la necesidad de tratar para los usuarios finales con transformaciones de tipo.

1.Vista General de la Transformación de Datos

Figura 3.1. Camel ofrece muchas características para transformación de datos de un formato a otro

Page 6: Apache Camel - Parte II

Transformación de datos con Camel En Camel la transformación de datos generalmente toma lugar en las seis formas listadas en la tabla 3.1.

Page 7: Apache Camel - Parte II

Transformación Descripción

Transformación de datos en routes

Puedes explícitamente forzar la transformación en el route usando los EIPs Message Translator o Content Enricher. Esto te da el poder de hacer mapeos de datos usando código regular Java.

Transformación de datos usando componentes

Camel proporciona un rango de componentes para transformación, como lo es el componente XSLT para transformación XML.

Transformación de datos usando formatos de datos

Los formatos de datos son transformadores Camel que vienen en pares para transformar datos atrás y hacia adelante entre formatos bien conocidos

Transformación de datos usando templates

Camel proporciona un rango de componentes para transformar usando plantillas, como Apache Velocity.

Transformación de tipos de datos usando el mecanismo convertidor de tipos de Camel

Camel tiene un mecanismo convertidor de tipos elaborado que se activa bajo demanda. Esto es conveniente cuando necesitas convertir de tipos comunes como java.lang.Integer a java.lang.String o aún de java.io.File a java.lang.String.

Transformación de mensajes en adaptadores de componentes

Muchos componentes de Camel se adaptan a varios protocolos comúnmente usados y, como tal, necesita ser capaz de transformar mensajes en la medida en que viajan a y desde estos protocolos. Frecuentemente estos componentes usan una combinación de transformaciones de datos y convertidores de tipo personalizadas.

Page 8: Apache Camel - Parte II

2. Transformando datos usando EIPs y Java Data Mapping es el proceso de mapear entre dos modelos de datos distintos, y es un factor clave en la integración de datos. Hay muchos estándares existentes para modelos de datos, gobernados por varias organizaciones o comités. Por tal, frecuentemente te encontrarás necesario mapear del modelo de datos personalizado de una compañía a un modelo de datos estándar.

Camel proporciona gran libertad en el mapeo de datos debido a que te permite usar código Java –no estás limitado a usar una herramienta de mapeo de datos particular que en la primera podría parecer elegante pero que torna a hacer las cosas imposibles.

Aquí veremos cómo mapear datos usando un Processor, el cual es una API Camel. Camel también usa beans para mapear, lo cual es una buena práctica, debido a que le permite a tu lógica de mapeo ser independiente de la API de Camel.

Page 9: Apache Camel - Parte II

Usando el EIP Message Translator El EIP Mesage Translator es ilustrado en la figura 3.2

Este patrón cubre traducir un mensaje de un formato a otro. Es el equivalente al patrón Adapter del libro Gang of Four.

Camel provee tres formas de usar este patrón:

Usando un Processor

Usando beans

Usando <transform>

Page 10: Apache Camel - Parte II

Transformando usando un Processor

El Processor de Camel es una interface definida en org.apache.camel.Processor con un sólo método:

public void process(Exchange exchange) throws Exception;

El Processor es una API de bajo nivel donde trabajas directamente en la instancia Exchange de Camel. Te da acceso completo a todas las partes de Camel que se mueven del CamelContext, del cual puedes obtener del Exchange usando el método getCamelContext.

Veamos un ejemplo. En Autopartes Rider se te ha solicitado generar diariamente reportes de órdenes recientemente recibidas para ser sacadas a un archivo CSV. La compañía usa un formato personalizado para entradas de órdenes, pero hace las cosas fáciles, ellos ya tienen un servicio HTTP que retorna una lista de órdenes para cualquier dato que ingreses. El reto al que te enfrentas es mapear los datos retornados del servicio HTTP a un formato CSV y escribir el reporte a un archivo.

Page 11: Apache Camel - Parte II

Listado 3.1 Usando un Processor para traducir desde un formato personalizado a formato CSV

import org.apache.camel.Exchange;

import org.apache.camel.Processor;

public class OrderToCsvProcessor implements Processor {

public void process(Exchange exchange) throws Exception {

String custom = exchange.getIn().getBody(String.class);

String id = custom.substring(0, 9);

String customerId = custom.substring(10, 19);

String date = custom.substring(20, 29);

String items = custom.substring(30);

String[] itemIds = items.split("@");

StringBuilder csv = new StringBuilder();

csv.append(id.trim());

csv.append(",").append(date.trim());

csv.append(",").append(customerId.trim());

for (String item : itemIds) {

csv.append(",").append(item.trim());

}

exchange.getIn().setBody(csv.toString());

}

}

Obtiene payload personalizado

Extrae datos a variables locales

Mapea a formato CSV

Remplaza payload con payload CSV

Page 12: Apache Camel - Parte II

Primero coges el payload de formato personalizado del exchange. Es de tipo String, de forma que pasas una String como parametro de entrada para tener el payload retornado como una String. Luego extraes datos del formato personalizado a las variables locales. El formato personalizado podría ser cualquier cosa, pero en este ejemplo es un formato personalizado de longitud fija. Luego mapeas el formato CSV construyendo una string con valores separados por coma. Finalmente, remplaza el payload personalizado con tu nuevo payload CSV.

Usar un processor tiene una desventaja, requieres hacer uso de la API Camel.

Page 13: Apache Camel - Parte II

Importante Usando los métodos getIn y getOut en exchanges

El Exchange Camel define dos métodos para retomar mensajes: getIn y getOut. El método getIn retorna el mensaje entrante, y el método getOut accesa el mensaje saliente.

Hay dos escenarios donde el usuario final Camel tendrá que decidir entre usar estos métodos:

Un escenario de sólo lectura, como cuando envías a log el mensaje entrante.

Un escenario de escritura, como cuando estás transformando el mensaje

En el segundo escenario asumirías que se debería usar getOut. Eso es correcto acorde a la teoría, pero en la práctica hay una trampa cuando se usa getOut: los headers y attachments del mensaje entrante se perderán. Frecuentemente esto no es lo que quieres, de forma que copiar los headers y attachments del mensaje entrante al mensaje saliente, puede ser tedioso. La alternativa es asignar los cambios directamente en el mensaje entrante usando getIn, y no usar getOut en todo.

Page 14: Apache Camel - Parte II

Transformando usando Beans

Usar beans es una buena práctica debido a que te permite usar cualquier código Java y librerías que desees. Camel no impone restricciones en ninguna cosa. Camel puede invocar cualquier bean que elijas, de forma que puedes usar beans existentes sin tener que rescribirlos o recompilarlos.

Page 15: Apache Camel - Parte II

Listado 3.2 Usando un bean para traducir de un formato personalizado a formato CSV

public class OrderToCsvBean {

public static String map(String custom) {

String id = custom.substring(0, 9);

String customerId = custom.substring(10, 19);

String date = custom.substring(20, 29);

String items = custom.substring(30);

String[] itemIds = items.split("@");

StringBuilder csv = new StringBuilder();

csv.append(id.trim());

csv.append(",").append(date.trim());

csv.append(",").append(customerId.trim());

for (String item : itemIds) {

csv.append(",").append(item.trim());

}

return csv.toString();

}

}

Extrae datos a variables locales

Mapea a formato CSV

Retorna payload CSV

Page 16: Apache Camel - Parte II

La primer diferencia notable entre los listados 3.1 y 3.2 es que en el listado 3.2 no se usa ningún import de Camel. Esto significa que tu bean es totalmente independiente de la API Camel. La siguiente diferencia es que puedes nombrar la firma del método en el listado 3.2 –en este caso es un método estático llamado map.

La firma del método define el contrato, lo cual significa que el primer parámetro, (String custom), es el body del mensaje que vas a usar para la traducción. El método retorna una String, lo cual significa que los datos traducidos serán de tipo String. En ejecución, Camel vincula a esta firma del método.

Una ventaja más de usar beans sobre processors para mapeos es que las pruebas unitarias son mucho más fáciles.

Page 17: Apache Camel - Parte II

Transformando usando el método transform() del DSL Java

Transform() es un método en el DSL Java que puede ser usado en routes Camel para transformar mensajes. Permitiendo el uso de expresiones, transform() permite gran flexibilidad, y usando expresiones directamente en el DSL a veces puede ahorrar tiempo.

Supongamos que necesitas preparar algún texto para formateo HTML remplazando todos los saltos de línea con una tag <br/>. Esto puede hacerse con una expresión incorporada de Camel que busca y remplaza utilizando expresiones regulares.

from("direct:start")

.transform(body().regexReplaceAll("\n", "<br/>"))

.to("mock:result");

Page 18: Apache Camel - Parte II

Lo que este route hace es usar el método transform() para decirle a camel que el mensaje debe ser transformado usando una expresión. Camel provee lo que es conocido como el patrón Builder para construir expresiones desde expresiones individuales. Este se hace encadenando llamadas a métodos, lo cual es la esencia del patrón Builder.

En este ejemplo combinas body() y regexReplaceAll(). La expresión debe ser leída como sigue: toma el body y ejecuta una expresión regular que remplace todas las nuevas líneas (\n) con tags <br/>. Ahora hemos combinado dos métodos que conforman a una expresión Camel compuesta.

Page 19: Apache Camel - Parte II

Camel te permite usar tus propias expresiones personalizadas. Esto es útil cuando necesitas tener control completo y tener código Java en la punta de tus dedos. Por ejemplo, el ejemplo anterior podría haber sido implementado de la forma siguiente:

from("direct:start")

.transform(new Expression() {

public <T> T evaluate(Exchange exchange, Class<T> type) {

String body = exchange.getIn().getBody(String.class);

body = body.replaceAll("\n", "<br/>");

body = "<body>" + body + "</body>";

return (T) body;

}

}).to("mock:result");

Page 20: Apache Camel - Parte II

Transformando usando <transform> de Spring XML

Usar <transform> de Spring XML es un poco diferente que el DSL Java debido a que el DSL XML no es tan poderoso. No están disponibles las expresiones del patrón Builder debido a que XML no tiene un lenguaje de programación real tras él. Lo que puedes hacer tras ello es invocar un método en un bean o usar lenguajes de scripting.

Veamos cómo trabaja esto. El método del siguiente bean se usa dentro del route como la expresión: public class HtmlBean {

public static String toHtml(String body) {

body = body.replaceAll("\n", "<br/>");

body = "<body>" + body + "</body>";

return body;

}

}

Page 21: Apache Camel - Parte II

El route es el siguiente: <bean id="htmlBean" class="camelinaction.HtmlBean"/>

<camelContext id="camel"

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

<route>

<from uri="direct:start"/>

<transform>

<method bean="htmlBean" method="toHtml"/>

</transform>

<to uri="mock:result"/>

</route>

</camelContext>

Primero declaras un bean regular de Spring para ser usado para transformar el mensaje. Luego en el route, usas <transform> con una expresión llamada <method> para invocar el bean.

Page 22: Apache Camel - Parte II
Page 23: Apache Camel - Parte II

Usando el patrón EIP Content Enricher El EIP Content Enricher se ilustra en la figura 3.3. Este patrón documenta el escenario donde un mensaje es enriquecido con datos obtenidos de otro recurso.

Figura 3.3 En el EIP Content Enricher, un mensaje existente tiene datos agregados de otra fuente.

Page 24: Apache Camel - Parte II

Para ayudar a comprender este patrón, regresaremos a Aupartes Rider. Nos damos cuenta que el mapeo de datos que hicimos en el listado 3.1 no era suficiente. Las órdenes también son acumuladas en un servidor FTP, y tu job va de alguna manera a mezclar esta información en el reporte existente. La figura 3.4 ilustra este escenario.

En la figura 3.4, un consumidor calendarizado usando Quartz inicia el route cada día a las 6:00 am 1. Luego obtiene datos de un servidor FTP, el cual retorna órdenes en un formato personalizado 2, el cual luego es transformado en formato CSV 3. En este punto, tienes que ejecutar el paso de enriquecimiento de contenido adicional 4 con los datos obtenidos del servidor FTP 5. Después de esto, el reporte final es escrito al servidor FTP 6.

Page 25: Apache Camel - Parte II

Figura 3.4

Una vista general del route que genera el reporte de órdenes, ahora con el enriquecedor de contenido obteniendo datos de un servidor FTP.

Page 26: Apache Camel - Parte II

Antes de profundizarnos en el código y ver cómo implementar esto, necesitamos ir un paso atrás y ver cómo el EIP Content Enricher es implementado en Camel. Camel proporciona dos operaciones en el DSL para implementar el patrón.

pollEnrich – Esta operación mezcla datos retomados de otra fuente usando un consumidor.

enrich – Esta operación mezcla datos retomados de otra fuente usando un productor.

Camel usa la interfaz org.apache.camel.processor.

AgregationStrategy para mezclar los resultados de la fuente con el mensaje original, de la forma siguiente:

Exchange aggregate(Exchange oldExchange, Exchange newExchange);

Page 27: Apache Camel - Parte II

Este método aggregate es una callback que debes implementar. Este método tiene dos parámetros: el primero, llamado oldExchange, contiene el exchange original; el segundo, newExchange, es la fuente enriquecida. Tu tarea es enriquecer el mensaje usando código Java y retornar el resultado mezclado. Esto puede sonar un poco confuso, veámoslo en acción.

Para resolver el problema, necesitas usar pollEnrich debido a que es capaz de votear por un archivo de un servidor FTP.

Enriqueciendo usando POLLENRICH.

El listado 3.3 muestra cómo puedes usar pollEnrich para retomar las órdenes adicionales del servidor FTP remoto y agregar estos datos con el mensaje existente usando AggregationStrategy de Camel.

Page 28: Apache Camel - Parte II

Listado 3.3 Usando pollEnrich para mezclar datos adicionales con un mensaje existente

from("quartz://report?cron=0+0+6+*+*+?")

.to("http://riders.com/orders/cmd=received")

.process(new OrderToCSVProcessor())

.pollEnrich("ftp://riders.com/orders/?username=rider&password=secret",

new AggregationStrategy() {

public Exchange aggregate(Exchange oldExchange, Exchange newExchange){

if (newExchange == null) {

return oldExchange;

}

String http = oldExchange.getIn().getBody(String.class);

String ftp = newExchange.getIn().getBody(String.class);

String body = http + "\n" + ftp;

oldExchange.getIn().setBody(body);

return oldExchange;

}

})

.to("file://riders/orders");

1

2 Mezcla datos usando AggregationStrategy

3 Escribe la salida a archivo

Usa pollEnrich para leer un archivo FTP

Page 29: Apache Camel - Parte II

El route es disparado por Quartz para que corra todos los días a las 6 am. Invocas el servicio HTTP para retomar las órdenes y transformarlas a formato CSV usando un processor.

En este punto, necesitas enriquecer los datos existentes con las órdenes del servidor FTP remoto. Esto se hace usando pollEnrich 1, lo cual consume el archivo remoto.

Para mezclar los datos, usas AggregationStrategy 2. Primero, checas si algunos datos fueron o no consumidos. Si newExchange es null, no hay archivo remoto para consumir, y sólo retornas los datos existentes. Si hay un archivo remoto, mezclas los datos concatenando los datos existentes con los nuevos datos y asignándolos atrás en el oldExchange. Luego, retornas los datos mezclados retornando el oldExchange. Para escribir el archivo de reporte CSV, usas el componente file.

Page 30: Apache Camel - Parte II

PollEnrich utiliza un consumidor de voteo para retomar mensajes, y ofrece tres modos de timeout:

pollEnrich(timeout=-1)- Votea el mensaje y espera hasta que un mensaje llegue. Este modo bloqueará hasta un mensaje exista.

pollEnrich(timeout=0)- Inmediatamente votea el mensaje si alguno existe; de otra forma se retornará null. Nunca esperará para que los mensajes lleguen, así que este modo nunca bloqueará. Este es el modo default.

pollEnrich(timeout>0)- Votea el mensaje, si no existe mensaje, esperará por uno, esperando a lo más hasta que el timeout se dispare. Este modo potencialmente bloqueará.

Es una mejor práctica usar timeout=0 o asignar un valor de timeout cuando se usa pollEnrich para evitar esperar indefinidamente si no llegan mensajes.

Page 31: Apache Camel - Parte II

Enriqueciendo usando ENRICH

Enrich es usado cuando necesitas enriquecer el mensaje actual con datos de otra fuente usando mensajería request-response. Un primer ejemplo sería enriquecer el mensaje actual con la respuesta de una llamada a web service. Pero veremos otro ejemplo, usando Spring XML para enriquecer el mensaje actual usando el transporte TCP: <bean id="quoteStrategy“ class="camelinaction.QuoteStrategy"/>

<route>

<from uri="activemq:queue:quotes"/>

<enrich url="mina:tcp://riders.com:9876?textline=true&sync=true"

strategyRef="quoteStrategy"/>

<to uri="log:quotes"/>

</route>

Aquí usas el componente mina de Camel para transporte TCP, configurado para usar mensajería request-response usando la opción sync=true.

1

Bean implementando AggregationStrategy

Page 32: Apache Camel - Parte II

Para mezclar el mensaje original con datos del servidor remoto, <enrich> debe referir a una AggregationStrategy. Esto se hace usando el atributo strategyRef. Como lo puedes ver en el ejemplo, la quoteStrategy a la que está siendo referida es el id de un bean 1, el cual contiene la implementación real de la AggregationStrategy, donde la mezcla toma lugar.

Page 33: Apache Camel - Parte II

3.3 Transformando XML Camel proporciona dos formas de ejecutar transformaciones XML.

Componente XSLT –Para transformar un payload XML en otro formato usando hojas de estilo XSLT.

Marshaling XML –Para serializar y deserializar objetos a y desde XML.

Transformando XML con XSLT

XSL Transformations (XSLT) es un lenguaje XML declarativo usado para transformar documentos XML en otros documentos. Por ejemplo, XSLT puede ser usado para transformar XML en HTML para páginas web o para transformar un documento XML en otro documento XML con una estructura diferente. XSLT es poderoso y versátil, pero también es un lenguaje complejo y toma tiempo y esfuerzo comprenderlo y dominarlo. Hay que pensarlo dos veces.

Page 34: Apache Camel - Parte II

Camel provee XSLT como un componente en camel-spring.jar debido a que aprovecha la carga de recursos de Spring. Esto significa mayor flexibilidad en la carga de hojas de estilo debido a que Spring les permite ser cargadas de diferentes ubicaciones, como lo es del classpath, rutas de archivos, y sobre HTTP.

Figura 3.5 Un Camel route usando un componente XSLT para transformar un documento XML antes de que sea enviado a una queue JMS

Page 35: Apache Camel - Parte II

Usar el componente XSLT es sencillo, debido a que es sólo otro componente Camel. El siguiente route muestra un ejemplo de cómo podrías usarlo; este route también es ilustrado en la figura 3.5. from("file://rider/inbox")

.to("xslt://camelinaction/transform.xsl")

.to("activemq:queue:transformed")

El consumidor de archivos selecciona los nuevos archivos y los enruta al componente XSLT, el cual transforma el payload usando la hoja de estilos. Después de la transformación, el mensaje es enrutado a un productor JMS. Nota en el código anterior como es definida la URL para el componente XSLT: xslt://camelinaction/transform.xsl. La parte posterior del esquema es la ubicación URI de la hoja de estilo a usar. Por default Camel buscará en el classpath.

Page 36: Apache Camel - Parte II

El componente XSLT aprovecha Spring para cargar la hoja de estilos. Puedes prefijar el nombre del recurso con cualquiera de los tres prefijos listados en la tabla 3.2.

Prefijo Ejemplo Descripción

<ninguno> xslt://camelinaction/transform.xsl Si no se proporciona prefijo, camel carga el recurso del classpath

classpath xslt://classpath:com/mycompany/transform.xsl

Carga el recurso del classpath

file xslt://file:/rider/config/transform.xsl Carga el recurso del sistema de archivos local

http: xslt://http://rider.com/styles/transform.xsl

Carga el recurso de una URL

Page 37: Apache Camel - Parte II

Transformando XML con Marshalling de Objetos

Cualquier ingeniero de software quien ha trabajado con XML sabe que es un reto usar la API XML de bajo nivel que ofrece Java. En lugar de eso, la gente frecuentemente prefiere trabajar con objetos Java regulares y usar marshalling para transformar entre objetos Java y representaciones XML.

En Camel este proceso de marshalling es provisto en componentes listos-para-usar conocidos como formatos de datos.

Transformando usando XStream

XStream es una simple librería para serializar objetos a XML y hacia atrás. Para usarla, necesitas el camel-xstream.jar en el classpath y la misma librería de XStream.

Supón que necesitas enviar mensajes en formato XML a una queue JMS compartida, la cual luego es usada para integrar dos sistemas.

Page 38: Apache Camel - Parte II

Transformando usando Marshalling de Objetos

Listado 3.4 Usando XStream para transformar un mensaje en XML <camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">

<dataFormats>

<xstream id="myXstream"/>

</dataFormats>

<route>

<from uri="direct:foo"/>

<marshal ref="myXstream"/>

<to uri="activemq:queue:foo"/>

</route>

</camelContext>

Cuando usas el DSL XML, puedes declarar los formatos de datos usados arriba 1 del <CamelContext>. Haciendo esto, puedes compartir los formatos de datos en múltiples routes. En este primer route, donde envías mensajes a una queue JMS, usas marshall 2, lo cual se refiere al id de 1,, de forma que Camel sabe que el formato de datos XStream está siendo usado.

Especifica formato de datos XStream

Transforma a XML

Page 39: Apache Camel - Parte II

Transformando usando Marshalling de Objetos

También puedes usar directamente en el route, lo cual puede acortar un poco la sintaxis, como esto:

<route>

<from uri="direct:foo"/>

<marshal> <xstream/> </marshall>

<to uri="activemq:queue:foo"/>

</route>

</camelContext>

El mismo route es un poco más acortador para escribir en el DSL Java, debido a que puedes hacerlo con una línea por route: from("direct:foo").marshal().xstream().to("uri:activemq:queue:foo");

Sí, usar Xstream es tan fácil. Y la operación inversa, unmarshalling de XML a un objeto, es tan simple:

Page 40: Apache Camel - Parte II

Transformando usando Marshalling de Objetos

<route>

<from uri="activemq:queue:foo"/>

<unmarshal ref="myXstream"/>

<to uri="direct:handleFoo"/>

</route>

Ya has visto cuan fácil es usar XStream con Camel.

Page 41: Apache Camel - Parte II

Transformando usando JAXB

JAXB (Java Architecture for XML Binding) es una especificación estándar para XML binding, y es provista lista en el runtime Java. Como XStream, te permite serializar objetos a XML y hacia atrás otra vez. No es tan simple, pero ofrece más opciones para controlar la salida XML. Y debido a que es distribuido en Java, no necesitas algunos archivos Jars especiales en el classpath.

A diferencia de XStream, JAXB requiere que hagas un poco de trabajo para declarar el binding entre objetos Java y el formato XML. Esto frecuentemente se hace usando anotaciones. Supón que defines un modelo de bean para representar una orden, como se muestra en el listado 3.5, y quieres transformar esto en XML antes de enviarlo a una queue JMS. Luego quieres transformarlo atrás al bean de orden otra vez, cuando consumes de la queue JMS. Esto puede hacerse como se muestra en los listados 3.5 y 3.6.

Page 42: Apache Camel - Parte II

Transformando usando JAXB

package com.acme.order;

import javax.xml.bind.annotation.XmlAccessType;

import javax.xml.bind.annotation.XmlAccessorType;

import javax.xml.bind.annotation.XmlAttribute;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement

@XmlAccessorType(XmlAccessType.FIELD)

public class PurchaseOrder {

@XmlAttribute

private String name;

@XmlAttribute

private double price;

@XmlAttribute

private double amount;

}

1 PurchaseOrder en una clase JAXB anotada

Listado 3.5 Anotando un bean con JAXB de forma que pueda ser transformado a y desde XML

Page 43: Apache Camel - Parte II

Transformando usando JAXB

El listado 3.5 muestra como usar anotaciones JAXB para decorar tu objeto modelo (omitiendo los getters y setters usuales). Primero define @XmlRootElement 1 como una anotación a nivel de clase para indicar que esta clase es un elemento XML. Luego defines el @XmlAccessorType para permitir que JAXB accese los campos directamente. Para exponer los campos de este objeto modelo como atributos XML, los marcas con la anotación @XmlAttribute.

Usando JAXB, debes ser capaz de serializar un objeto modelo en una representación XML como esta: <purchaseOrder name="Camel in Action" price="4995" amount="1"/>

El listado 3.6 muestra como puedes usar JAXB en routes para transformar el objeto PurchaseOrder a XML antes de que sea enviado a una queue JMS, y luego atrás otra vez de XML al objeto PurchaseOrder cuando se consume de la misma queue JMS.

Page 44: Apache Camel - Parte II

Transformando usando JAXB

Listado 3.5 Usando JAXB para serializar objetos a y desde XML <camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">

<dataFormats>

<jaxb id="jaxb" contextPath="camelinaction"/>

</dataFormats>

<route>

<from uri="direct:order"/>

<marshal ref="jaxb"/>

<to uri="activemq:queue:order"/>

</route>

<route>

<from uri="activemq:queue:order"/>

<unmarshal ref="jaxb"/>

<to uri="direct:doSomething"/>

</route>

</camelContext>

1 Declara formato de datos JAXB

2 Transforma de modelo a XML

3 Transforma de XML a modelo

Page 45: Apache Camel - Parte II

Transformando usando JAXB

Primero necesitas declarar el formato de datos JAXB 1. Nota que también se define un atributo contextPath en el formato de datos JAXB –este es un nombre de package que instruye a JAXB que busque en este package clases que son anotadas de JAXB.

El primer route luego serializa a XML 2 y el segundo route deserializa para transformar el XML atrás al objeto PurchaseOrder 3.

Nota: Para decirle a JAXB qué clases son anotadas de JAXB, necesitas arrastrar un archivo especial jaxb.index en la ruta del contexto. Es un archivo de texto plano en el cual cada línea lista el nombre de la clase. En el ejemplo anterior, el archivo contiene una sola línea con el texto PurchaseOrder.

Page 46: Apache Camel - Parte II

3.4 Transformando con formatos de datos En Camel, los formatos de datos son transformadores pluggables que pueden transformar mensajes de un formato a otro y viceversa. Cada formato de dato es representado en Camel como una interface org.apache.camel.spi.DataFormat conteniendo dos métodos:

marshall – Para serializar un objeto en otro formato, tal como serializar objetos Java a XML, CSV, EDI, HL7, u otros modelos de datos conocidos.

unmarshall – Para ejecutar la operación inversa, la cual transforma datos de formatos bien conocidos atrás en un mensaje.

Figura 3.6 Un objeto es serializado a una representación binaria; unmarshall puede utilizarse para llevar el objeto atrás.

Page 47: Apache Camel - Parte II

Ya pudiste haberte dado cuenta que estas dos funciones son opuestas, significando que una es capaz de hacer lo inverso de lo que la otra ha hecho, como lo ilustra la figura 3.6.

En esta sección cubriremos los formatos de datos en más profundidad y usando otros tipos de datos diferentes a XML, como es CSV y JSON. Aún también veremos cómo puedes crear tus propios formatos de datos.

Comenzaremos nuestra jornada checando los formatos de datos Camel provistos listos para usar.

Formatos de datos provistos con Camel

Camel provee formatos de datos para un rango de modelos de datos bien conocidos, como se lista en la tabla 3.3.

Page 48: Apache Camel - Parte II

Tabla 3.3 Formatos de datos provistos listos con Camel

Formato de dato

Modelo de dato

Artefacto Descripción

Bindy CSV, FIX, longitud fija

camel-bindy Vincula varios modelos de datos para modelar objetos usando anotaciones

Castor XML camel-castor Usa Castor para vincular a y desde objetos Java

Crypto Cualquiera camel-crypto Encripta y desencripta datos usando la Java Cryotography Extension

CSV CSV camel-csv Transforma a y desde CSV usando la librería Apache Commons CSV

Flatpack CSV camel-flatpack Transforma a y desde CSV usando la librería FlatPack

GZip Cualquiera camel-gzip Comprime y descomprime archivos (compatible con las populares herramientas gzip/gunzip)

HL7 HL7 camel-hl7 Transforma a y desde HL7, el cual es un formato de datos bien conocido en la industria del cuidado de la salud

JAXB XML camel-jaxb Usa el estándar JAXB 2.x para vincular XML a y desde objetos Java

Jackson JSON camel-jackson Transforma a y desde JSON usando la ultrarrápida librería Jackson

Page 49: Apache Camel - Parte II

Tabla 3.3 Formatos de datos provistos listos con Camel (cont)

Formato de dato

Modelo de dato

Artefacto Descripción

Protobuf XML camel-protobuf

Transforma a y desde XML usando la librería Google Protocol Buffers

SOAP XML camel-soap Transforma a y desde SOAP

Serialization Objeto camel-core Usa serialización de objetos para transformar objetos a y desde un stream serializado

TidyMarkup HTML camel-tagsoup

xmalBeans XML camel-xmlsbeans

Usa XmlBeans para vincular XML a y desde objetos Java

XMLSecurity XML camel-xmlsecurity

Facilita la encripción y desencripción de documentos XML

XStream XML camel-xstream Usa XStream para vinculación XML a y desde objetos Java

XStream JSON camel-xstream Transforma a y desde JSON usando la librería XStream

Zip Cualquiera camel-core Usa el estándar JAXB 2.x para vincular XML a y desde objetos Java

Page 50: Apache Camel - Parte II

Como puedes ver, Camel proporciona 18 formatos de datos listos para usar. Aquí estaremos viendo 3 de ellos. Ellos son los formatos más comúnmente usados, y lo que aprendas sobre estos aplicarán también para los restantes formatos de datos.

Usando el formato de datos CSV de Camel

El formato de datos camel-csv es capaz de transformar a y desde formato CSV. Aprovecha Apache Commons CSV para hacer el trabajo real.

Supón que necesitas consumir archivos CSV, dividir cada fila, y enviarla a una queue JMS. Suena difícil de hacer, pero es posible con poco esfuerzo en un route Camel:

Page 51: Apache Camel - Parte II

from.(“file://rider/csvfiles”)

.unmarshall().csv()

.split(body()).to(“activemq:queue.csv.record”);

Todo lo que tienes que hacer es unmarshall los archivos CSV, lo cual leerá el archivo línea por línea y almacenará todas las líneas en el cuerpo del mensaje como un tipo java.util.List<List>. Luego usas el splitter para dividir el cuerpo, lo cual fraccionará la java.util.List<List<String>> en filas (cada fila representada como otra List<String> conteniendo los campos) y enviar la fila a la queue JMS. Podrías no querer enviar cada fila como un tipo List a la queue JMS, de forma que puedes transformar la fila antes de enviarla, quizás usando un processor.

El mismo ejemplo en Spring XML es un poco diferente, como se muestra aquí:

Page 52: Apache Camel - Parte II

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

<route>

<from uri="file://rider/csvfiles"/>

<unmarshal><csv/></unmarshal>

<split>

<simple>body</simple>

<to uri="activemq:queue.csv.record"/>

</split>

</route>

</camelContext>

La notable diferencia es como le dices a <split> que debe dividir el cuerpo del mensaje. Para hacer esto necesitas proveerle a <split> con una Expression, qué es lo que el splitter debe iterar cuando ejecute el splitting. Para hacerlo así, puedes usar el lenguaje de expresión incorporado de Camel llamado Simple, el cual sabe cómo hacer eso.

Page 53: Apache Camel - Parte II

A la primera, los tipos de datos que el formato de datos usa pueden parecer un poco confusos. Estos son listados en la tabla 3.4.

Un problema con camel-csv es que usa tipos de datos genéricos, como Maps o Lists, para representar registros CSV. Frecuentemente ya tienes modelos de datos para representar tus datos en memoria.

Operación De tipo A tipo Descripción

marshall Map<String,

Object>

OutputStream Contiene una sola fila en formato CSV

marshall List<Map<String,

Object>>

OutputStream Contiene múltiples filas en formato CSV donde cada fila está separada por \n (nueva línea)

unmarshall InputStream List<List<String>> Contiene una List de filas donde cada fila es otra List de campos

Page 54: Apache Camel - Parte II

Usando el formato de datos Bidy de Camel

Los dos formatos de datos existentes relacionados a CSV (camel-csv y camel-flatpack) son librerías más viejas que no toman ventaja de las nuevas características de Java 1.5, tales como anotaciones y generics. A la luz de esta deficiencia, Charles Moulliard salió y escribió el componente camel-bindy para tomar ventaja de estas nuevas posibilidades. Es capaz de vincular CSV, FIX, y formatos de datos de longitud fija a modelos de objetos existentes usando anotaciones. Esto es similar a lo que hace JAXB para XML.

Supón que tienes un objeto modelo que representa una orden de compra. Anotando el objeto modelo con anotaciones camel-bindy, puedes fácilmente transformar mensajes entre CSV y objetos modelo Java.

Page 55: Apache Camel - Parte II

package camelinaction.bindy;

import java.math.BigDecimal;

import org.apache.camel.dataformat.bindy.annotation.CsvRecord;

import org.apache.camel.dataformat.bindy.annotation.DataField;

@CsvRecord(separator = ",", crlf = "UNIX")

public class PurchaseOrder {

@DataField(pos = 1)

private String name;

@DataField(pos = 2, precision = 2)

private BigDecimal price;

@DataField(pos = 3)

private int amount;

}

Mapea a registro CSV 1

2 Mapea a columna en registro registro CSV

Page 56: Apache Camel - Parte II

Primero marcas la clase con la anotación @CsvRecord 1 para indicar que representa un registro en formato CSV. Entonces anotas los campos con @DateField acorde al layout del registro CSV 2. Usando el atributo pos, puedes dictar la orden en la cual son sacados en formato CSV; pos inicia con un valor de 1. Para campos numéricos, puedes adicionalmente declarar precision, la cual en este ejemplo es asignada a 2, indicando que el precio debe usar dos dígitos por centavo. Bindy tiene atributos para layout de grano fino de los campos, tales como pattern, trim y length. Puedes usar pattern para indicar el patrón de datos, trim para recortar la entrada, y length para restringir la descripción de un texto a un cierto número de caracteres.

Antes de ver cómo usar Bindy en routes Camel, necesitamos ir un paso atrás y checar los tipos de datos que Bindy espera usar. Ellos son listados en la tabla 3.5.

Page 57: Apache Camel - Parte II

Tabla 3.5 Tipos de datos que Bindy usa cuando transforma a y desde formato CSV

Operación De tipo A tipo Descripción de la salida

marshall List<Map<String, Object>>

OutputStream Contiene múltiples filas en formato CSV donde cada fila es separada por una \n (nueva línea)

unmarshall InputStream List<Map<String, Object>>

Contiene una List de filas donde cada fila contiene 1..n modelos de datos contenidos en un Map

La cosa importante a notar en la tabla 3.5 es que Bindy usa un Map<String, Object> para representar una fila CSV. A la primera puede parecer extraño. ¿Por qué no sólo usa un único objeto modelo para eso? La respuesta es que puedes tener múltiples objetos modelo con el registro CSV que está siendo dispersado a través de estos objetos. Por ejemplo, podrías tener los campos 1 a 3 en un objeto modelo, los campos 4 a 9 en otro, y los campos 10 al 12 en un tercero.

Page 58: Apache Camel - Parte II

La entrada del map <String, Object> es destilada como sigue:

Map key (String) –Debe contener el nombre de la clase completamente cualificado del objeto modelo.

Map value (Object) –Debe contener el objeto modelo.

Si te parece ser un poco confuso, no te preocupes. El siguiente ejemplo debe hacerlo más claro: Listado 3.8 Usando Bindy para transformar un objeto modelo a formato CSV

public class PurchaseOrderBindyTest extends TestCase {

public void testBindy() throws Exception {

CamelContext context = new DefaultCamelContext();

context.addRoutes(createRoute());

context.start();

MockEndpoint mock = context.getEndpoint("mock:result",

MockEndpoint.class);

Page 59: Apache Camel - Parte II

mock.expectedBodiesReceived("Camel in Action,49.95,1\n");

PurchaseOrder order = new PurchaseOrder();

order.setAmount(1);

order.setPrice(new BigDecimal("49.95"));

order.setName("Camel in Action");

ProducerTemplate template = context.createProducerTemplate();

template.sendBody("direct:toCsv", order);

mock.assertIsSatisfied();

}

public RouteBuilder createRoute() {

return new RouteBuilder() {

public void configure() throws Exception {

from("direct:toCsv")

.marshal().bindy(BindyType.Csv, "camelinaction.bindy")

.to("mock:result");

}

};

}

}

Crea el objeto modelo como es usual 1

Inicia el test 2

Transforma el objeto modelo a CSV

Page 60: Apache Camel - Parte II

En este listado, primero creas y llenas el modelo de orden usando setters Java regulares 1. Luego envías el modelo de orden al route enviándolo al endpoint direct:toCsv 2 que es usado en el route. El route luego serializará el modelo de orden a CSV usando Bindy 3. Nota cómo Bindy es configurado para usar modo CSV vía BindyType.Csv. Para permitirle a Bindy saber cómo mapear a objeto modelo orden, necesitas proporcionar un nombre de package que será escaneado para clases anotadas con anotaciones Bindy. Esta es la misma solución que ofrece JAXB.

Nota: El listado 3.8 usa MockEndpoint para probar fácilmente que el registro CSV está como se espera.

Page 61: Apache Camel - Parte II

Usando el formato de datos JSON de Camel

JSON (JavaScript Jason Notation) es un formato de intercambio de datos, y Camel proporciona dos componentes que soportan el formato de datos JSON: camel-xstream y camel-jackson. En esta sección nos enfocaremos en camel-jackson debido a que Jackson es una librería JSON muy popular.

Yendo atrás a Autopartes Rider, ahora tienes que implementar un nuevo servicio que retorna resúmenes de órdenes entregadas en formato JSON. Hacer esto con Camel es muy fácil, debido a que Camel tiene todos los ingredientes necesarios fabricar este servicio. El listado 3.9 muestra cómo puedes subir a un prototipo.

Page 62: Apache Camel - Parte II

<bean id="orderService" class="camelinaction.OrderServiceBean"/>

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

<dataFormats>

<json id="json" library="Jackson"/>

</dataFormats>

<route>

<from uri="jetty://http://0.0.0.0:8080/order"/>

<bean ref="orderService" method="lookup"/>

<marshal ref="json"/>

</route>

</camelContext>

Primero necesitas montar el formato de datos JSON y especificar que la librería Jackson debe ser usada 1. Luego defines un route que expone el servicio HTTP usando el endpoint Jetty. Este ejemplo expone el endpoint Jetty directamente en la URL. Usando http://0.0.0.0:8080/order , le dices a Jetty que cualquier cliente puede alcanzar este servicio en el puerto 8080.

Monta un formato de datos JSON 1

2 Invoca bean para retomar datos para respuesta

Page 63: Apache Camel - Parte II

Siempre que una request golpee este servicio HTTP, es enrutado al bean orderService 2 y el método lookup es invocado en ese bean. El resultado de la invocación a este bean es luego serializado a formato JSON y retornado atrás al cliente HTTP.

El bean order service podría tener una firma de método como esta: public PurchaseOrder lookup(@Header(name = "id") String id)

Esta firma te permite implementar la lógica de lookup como desees.

Nota que el bean del servicio puede retornar un POJO que la librería JSON es capaz de serializar. Por ejemplo, supón que usaste el PurchaseOrder del listado 3.7, y tuviste salida JSON como la siguente:

{"name":"Camel in Action","amount":1.0,"price":49.95}

El mismo servicio HTTP puede ser invocado por una request HTTP Get con el id de la orden como parámetro: http://0.0.0.0:8080/order/service?id=123.

Page 64: Apache Camel - Parte II

Configurando formatos de datos de Camel En secciones anteriores utilizaste el formato de datos CSV, pero este formato de datos ofrece muchas configuraciones adicionales. Este listado muestra como puedes configurar el formato de datos CSV. public void configure() {

CSVConfig custom = new CSVConfig();

custom.setDelimiter(';');

custom.setEndTrimmed(true);

custom.addField(new CSVField("id"));

custom.addField(new CSVField("customerId"));

custom.addField(new CSVField("date"));

custom.addField(new CSVField("item"));

custom.addField(new CSVField("amount"));

custom.addField(new CSVField("description"));

CsvDataFormat myCsv = new CsvDataFormat();

myCsv.setConfig(custom);

myCsv.setAutogenColumns(false);

from("direct:toCsv").marshal(myCsv).to("file://acme/outbox/csv");

}

Configura formato de datos CSV personalizado

1

Crea el formato de datos CSV personalizado

2

3 Usa el formato de datos CSV

Page 65: Apache Camel - Parte II

Configurar formatos de datos en Camel se hace usando código Java regular; usas la API que proporciona el formato de datos. En el listado 3.10, el formato de datos CSV ofrece un objeto CSVConfig 1 que es usado para asignar el punto y coma como delimitador y para especificar el orden de los campos. El mismo formato de datos es luego creado 2 y asignado para usar la configuración. El uso del formato de datos sigue siendo el mismo, así que todo lo que necesitas hacer es referirte a él desde los métodos marshall y unmarshall. Este mismo principio aplica a todos los formatos de datos en Camel. Puedes configurarlos usando las APIs que ellos proveen.

Ahora que sabes como usar los formatos de datos, vamos a ver como puedes escribir tus propios formatos de datos.

Page 66: Apache Camel - Parte II

Escribiendo tu propio formato de datos Pudieras encontrarte necesario transformar datos a y desde un formato de datos personalizado. En esta sección, veremos cómo puedes desarrollar un formato de datos que puede reversar strings.

Desarrollar tu propio formato de datos es muy fácil, debido a que Camel provee una sola API que debes implementar: org.apache.camel.spi.DataFormat. Vamos a ver cómo podrías implementar un formato de dato de reversa de string.

El formato de datos personalizado debe implementar la interface DataFormat, la cual te forza a desarrollar dos métodos: marshall 1 y unmarshall 2. Eso no es sorpresa, ya que ellos son los mismos métodos que usas en el route. El método marshall 1 necesita sacar el resultado al OutputStream. Para hacer esto necesitas obtener el payload del mensaje como un byte[], y luego reversarlo con un método helper. Luego escribes los datos al OutputStream.

Page 67: Apache Camel - Parte II

Nota que utilizas os convertidores de tipo de Camel para retornar el payload del mensaje como un byte[]. Esto es muy poderoso y te ahorra de hacer un typecast manual en Java o intentar convertir el payload por ti mismo.

El método unmarshall es cercanamente el mismo. Usas el mecanismo convertidor de tipo de Camel otra vez para proporcionar el payload del mensaje como un byte[]. unmarshall también reversa los bytes para obtener los datos atrás en su orden original. Nota que en este método retornas los datos en vez de escribirlos a un stream.

TIP: Como mejor práctica usa los convertidores de tipo Camel en vez del typecasting o convertirlos entre tipos por ti mismo.

Para usar este nuevo formato de datos en un route, todo lo que tienes que hacer es definirlo como un bean de Spring y referirte a él desde <marshall> y <unmarshall> de la forma siguiente:

Page 68: Apache Camel - Parte II

<bean id="reverse" class="camelinaction.ReverseDataFormat"/>

<camelContext id="camel"

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

<route>

<from uri="direct:marshal"/>

<marshal ref="reverse"/>

<to uri="log:marshal"/>

</route>

<route>

<from uri="direct:unmarshal"/>

<unmarshal ref="reverse"/>

<to uri="log:unmarshal"/>

</route>

</camelContext>

Page 69: Apache Camel - Parte II

3.5 Transformando con templates Camel proporciona una hábil integración con dos diferentes lenguajes de plantillas:

Apache Velocity-Probablemente el lenguaje de plantillas más conocido

FreeMarker –Otro lenguaje de plantillas popular que puede ser un poco más avanzado que Velocity.

Los dos lenguajes de plantilla son muy similares de usar, así que con Velocity nos basta.

Usando Apache Velocity

Autopartes Rider ha implementado un nuevo sistema de órdenes que debe enviar una respuesta de email cuando un cliente ha emitido una orden. Tu trabajo es implementar esta característica.

Page 70: Apache Camel - Parte II

El email de respuesta podría parecerse a este:

Querido cliente

Gracias por ordenar X pieza(s) de XXX a un costo de XXX.

Este es un email automatizado, por favor no responda.

Hay tres piezas de información en el email que deben ser remplazadas en tiempo de ejecución con valores reales. Lo que necesitas hacer es ajustar el email para usar el lenguaje de plantillas Velocity, y luego colocarlas en el repositorio de fuentes como /src/test/resources/email.vm:

Querido cliente

Gracias por ordenar ${body.amount} de ${body.name} a un

costo de ${body.price}.

Este es un email automatizado, por favor no responda.

Page 71: Apache Camel - Parte II

Nota que hemos insertado placeholders ${} en la plantilla, lo cual instruye a Velocity que las evalúe y las remplace en tiempo de ejecución. Camel prellena el contexto de Velocity con un número de entidades que luego están disponibles a Velocity. Estas entidades son listadas en la tabla 3.6.

NOTA: Las entidades en la tabla 3.6 también aplican para otros lenguajes de plantilla como FreeMarker.

Page 72: Apache Camel - Parte II

Entidad Tipo Descripción

camelContext org.apache.camel.CamelContext El Contexto de Camel

exchange org.apache.camel.Exchange El exchange actual

in org.apache.camel.Message El mensaje de entrada. Este puede chocar con alguna palabra reservada en algunos lenguajes. En su lugar usa request.

request org.apache.camel.Message El mensaje de entrada

body java.lang.Object El cuerpo del mensaje de entrada

headers java.util.Map Los headers del mensaje de entrada

response org.apache.camel.Message El mensaje de salida

out org.apache.camel.Message El mensaje de salida. Este puede chocar con alguna palabra reservada en algunos lenguajes. En su lugar usa response.

Page 73: Apache Camel - Parte II

3.6 A cerca de los convertidores de tipo de Camel Camel proporciona un sistema conversor de tipo incorporado que convierte automáticamente entre tipos bien conocidos. Este sistema le permite a los componentes de Camel trabajar juntos fácilmente sin tener diferencias de tipos. Y desde la perspectiva del usuario Camel, las conversiones de tipo están incorporadas en la API en muchos lugares sin ser invasivas. Por ejemplo, has usado en el listado 3.1:

String custom = exchange.getIn().getBody(String.class);

Al método getBody le es pasado el tipo que quieres que se le retorne. Tras bambalinas, el sistema conversor de tipo convierte el tipo retornado a un String si es necesario.

Aquí se explicará cómo Camel escanea el classpath al arranque para registrar dinámicamente los convertidores de tipo. También mostraremos cómo puedes usarlo desde un route Camel, y cómo construir tus propios convertidores de tipo.

Page 74: Apache Camel - Parte II

Cómo funciona el mecanismo convertidor de tipo de Camel

Para comprender el sistema conversor de tipo, primero necesitamos saber qué es un convertidor de tipo en Camel. La figura 3.7 ilustra la relación entre el TypeConverterRegistry y los TypeConverters que contiene.

El TypeConverteRegistry es donde todos los conversores de tipos son registrados cuando Camel inicia. En tiempo de ejecución, Camel usa el método lookup de TypeConverterRegistry para buscar un TypeConverter adecuado.

TypeConverter lookup(Class<?> toType, Class<?> fromType);

Figura 3.7 El TypeConverterRegistry contiene muchos TypeConverter

Page 75: Apache Camel - Parte II

Usando el TypeConverter, Camel entonces puede convertir un tipo a otro usando el método convertTo de TpeConverter, el cual está definido de la siguiente forma:

<T> T convertTo(Class<T> type, Object value);

Nota: Camel implementa alrededor de 150 o más conversores de tipo listos para usar, los cuales son capaces de convertir a y desde los tipos más comúnmente usados.

CARGA DE CONVERTIDORES DE TIPO EN EL REGSITRO

Al arranque, Camel carga todos los convertidores de tipo en el TypeConverterRegistry usando una solución de escaneo del classpath. Esto le permite a Camel elegir no sólo un conversor de tipo de camel-core sino también desde cualesquier a otros componentes de Camel.

Para escanear y cargar los conversores de tipo, Camel usa org.apache.camel.impl.converter.AnnotationTypeConverterLoader.

Page 76: Apache Camel - Parte II

Para evitar cargar silos de clases, lee un archivo de descubrimiento de servicio en la carpeta META-INF: META-INF/services/org/apache/camel/TypeConverter. Este es un archivo de texto plano que tiene una lista de paquetes que contiene conversores de tipo Camel. El archivo especial es necesario para evitar escanear cada JAR posible y todos sus packeges, lo cual sería consumidor de tiempo. El archivo especial le dice a Camel si el archivo JAR contiene o no conversores de tipo. Por ejemplo, el archivo en camel-core contiene las siguientes tres entradas:

org.apache.camel.converter

org.apache.camel.component.bean

org.apache.camel.component.file

La AnnotationTypeConverterLoader escaneará estos packages y sus subpackages para clases que han sido anotadas con @Converter, y busca en ellas métodos públicos que son anotados con @Converter. Cada uno de estos métodos es considerado un conversor de tipo.

Page 77: Apache Camel - Parte II

Esto es mejor ilustrado con un ejemplo. El siguiente código es un snippet de la clase IOConverter del JAR camel-core: @Converter

public final class IOConverter {

@Converter

public static InputStream toInputStream(URL url) throws IOException {

return url.openStream();

}

}

Camel irá sobre cada método anotado con @Converter y buscará la firma del método. El primer parámetro es del tipo from, y el tipo de retorno es del tipo to –en este ejemplo tienes un TypeConverter que puede convertir desde una URL a un InputStream. Haciendo esto, Camel carga todos los conversores de tipo incorporados, incluyendo aquellos de los componentes Camel en uso.

Page 78: Apache Camel - Parte II

Usando convertidores de tipo Camel

Podrías querer usar los convertidores de tipo para forzar un tipo específico para que sea usado en un route, como antes enviando datos atrás a un llamante o a un destino JMS. Vamos a ver cómo hacer eso:

Supón que necesitas enrutar archivos a una queue JMS usando java.jms.TextMessage. Para hacer lo así, puedes convertir cada archivo a una String, lo cual forza al componente JMS a usar TextMessage. Esto es fácil de hacer en Camel –puedes usar el método convertBodyTo, como es mostrado aquí:

from("file://riders/inbox")

.convertBodyTo(String.class)

.to("activemq:queue:inbox");

Page 79: Apache Camel - Parte II

Si estás usando Spring XML. en vez de ello proporcionas el tipo como un atributo, como lo siguiente: <route>

<from uri="file://riders/inbox"/>

<convertBodyTo type="java.lang.String"/>

<to uri="activemq:queue:inbox"/>

</route>

Puedes omitir el prefijo java.lang en el tipo, lo cual puede acortar la sintaxis un poco:

<convertBodyTo type="java.lang.String"/>

Otra razón para usar convertBodyTo es leer archivos usando un encoding fijo como UTF-8. Esto se hace pasando el encoding el segundo parámetro de entrada: from("file://riders/inbox")

.convertBodyTo(String.class, "UTF-8")

.to("activemq:queue:inbox");

Page 80: Apache Camel - Parte II

Escribiendo tu propio convertidor de tipo

Escribir tu propio convertidor de tipo es fácil en Camel. Ya has visto como un conversor de tipo se ve en la sección 3.6.1, cuando viste cómo funcionan los conversores de tipo.

Supón que necesitaras escribir un conversor de tipo personalizado que puede convertir un byte en un objeto modelo PurchaseOrder (un objeto que usaste en el listado 3.7). Como lo viste anteriormente, necesitas crear una clase @Converter conteniendo el método conversor de tipo: Listado 3.12 Un conversor de tipo personalizado para convertir de tipo byte[] a PurchaseOrden

@Converter

public final class PurchaseOrderConverter

@Converter

public static PurchaseOrder toPurchaseOrder(byte[] data,

Exchange exchange) {

TypeConverter converter = exchange.getContext()

.getTypeConverter(); Coge el TypeConverter a reutilizar

1

Page 81: Apache Camel - Parte II

String s = converter.convertTo(String.class, data);

if (s == null || s.length() < 30) {

throw new IllegalArgumentException("data is invalid");

}

s = s.replaceAll("##START##", "");

s = s.replaceAll("##END##", "");

String name = s.substring(0, 9).trim();

String s2 = s.substring(10, 19).trim();

BigDecimal price = new BigDecimal(s2);

price.setScale(2);

String s3 = s.substring(20).trim();

Integer amount = converter.convertTo(Integer.class, s3);

return new PurchaseOrder(name, price, amount);

}

}

Convierte de String a PurchaseOrden

2

Page 82: Apache Camel - Parte II

En el listado 3.12 el Exchange te da acceso al CamelContext y así al padre TypeConverter 1, el cual usas en este método para convertir entre cadenas y números. El resto del código. El resto del código es la lógica para parsear el protocolo personalizado y retornar el PurchaseOrder 2. Nota como puedes usar el converter para convertir fácilmente entre tipos bien conocidos.

Todo lo que necesitas hacer es agregar el archivo de descubrimiento de servicio, llamado TypeConverter, en el directorio META-INF. Como se explicó anteriormente, este archivo contiene una línea identificando cada package para ser escaneado por las clases @Converter.

Page 83: Apache Camel - Parte II

GRACIAS www.facebook.com/JavaDevelopersMexico

No olvides regalarnos un like.

Dudas y comentarios no dudes en escribir en la página o al siguiente correo

[email protected]