análisis de capacidades de transformacion de mensajes en jboss-esb

21
Proyecto: análisis de capacidades de transformación de mensajes en JBossESB Informe final Pablo Pazos Gutierrez Taller de Sistemas de Información 4, Instituto de Computación Facultad de Ingeniería, Universidad de la República

Upload: pablo-pazos

Post on 05-Dec-2014

1.242 views

Category:

Documents


1 download

DESCRIPTION

Análisis de capacidades de transformacion de mensajes HL7 en JBossESB usando Smooks y otras técnicas.

TRANSCRIPT

Page 1: Análisis de capacidades de transformacion de mensajes en jboss-esb

Proyecto: análisis de capacidades de

transformación de mensajes en JBossESB

Informe final

Pablo Pazos Gutierrez

Taller de Sistemas de Información 4, Instituto de Computación Facultad de Ingeniería, Universidad de la República

Page 2: Análisis de capacidades de transformacion de mensajes en jboss-esb

Resumen

Hoy en día los sistemas informáticos orientados al dominio de la salud han crecido en importancia, tamaño y complejidad, siendo la interoperabilidad entre los sistemas existentes, y también con nuevos sistemas que se generen, el problema a resolver. Para intentar resolver los problemas de interoperabilidad entre estos sistemas informático-clínicos existen distintos estándares propuestos por la industria, dentro de estos HL7 es un grupo de estándares para resolver la interoperabilidad entre sistemas de registro clínico orientado al intercambio de mensajes. El presente proyecto intenta explorar una solución de comunicación entre sistemas que son compatibles con HL7 y sistemas que no lo son y que manejan su propio formato de mensajes para interoperar con otros sistemas. Esta solución se basará en un middleware JBossESB en donde se resolverán las diferencias entre los formatos de mensajes que soportan los sistemas que intentan interoperar, de modo de que no sea necesario modificar un sistema existente para hacerlo compatible con mensajes HL7, si no que dicha “compatibilización” se hará dentro del ESB.

Page 3: Análisis de capacidades de transformacion de mensajes en jboss-esb

Planteo del problema El objetivo principal del presente proyecto es investigar distintas opciones para implementar la comunicación entre sistemas que se comuniquen mediante mensajes y que utilicen distintos formatos para dichos mensajes, en particular se buscará la interoperación entre sistemas cuyos mensajes sigan el estándar HL7 (*) y sistemas que utilicen otro formato de mensajes distinto a HL7. El objetivo es que ninguno de los sistemas deba adaptar su formato de mensajes al formato del otro sistema, es decir que la conversión entre los distintos sistemas de mensajes será realizada dentro de un ESB. Dicho ESB manejará los distintos aspectos referentes a la comunicación entre los sistemas. Este enfoque de manejo de transformaciones entre distintos formatos de mensajes en el propio ESB permite que los sistemas existentes no necesiten ser modificados para poder interactuar con otros sistemas compatibles con HL7. El ESB que se estará probando es JBossESB, un subproyecto dentro de JBoss. Sobre JBossESB se desea relevar y probar las distintas capacidades de transformación entre formatos de mensajes y también relevar las distintas capacidades de ruteo de mensajes con las que cuente JBossESB. La siguiente figura muestra opciones de transformación de mensajes entre sistemas. En la opción “clásica” se agrega un componente dentro del mismo sistema para que realice la estandarización de sus mensajes a HL7. En la opción centrada en “ESB” la estandarización de los mensajes se realiza en el ESB y es transparente a ambos sistemas, este es el enfoque que se intentará seguir.

Figura 1: opciones de estandarización de mensajes entre sistemas

(*) HL7 es un conjunto de estándares de comunicación orientados a mensajes, para el dominio de la salud. Los mensajes están basados en el modelo de referencia de HL7 llamado RIM por sus siglas en inglés. Estos mensajes se utilizan para comunicar información clínica y demográfica de pacientes entre sistemas, como por ejemplo: resultados de estudios, medidas de presión de sangre, datos personales, etc.

Page 4: Análisis de capacidades de transformacion de mensajes en jboss-esb

Introducción a JBossESB

JBossESB es la solución de middleware para comunicación del stack de proyectos JBoss. JBossESB sirve como infraestructura de comunicación para otros proyectos como jBMP, la solución de BPM del stack JBoss. Con JBossESB se pueden definir servicios que consisten en una serie de acciones que se ejecutan en secuencia llamado “pipeline” de acciones. Estos servicios pueden tener diversos puntos de entrada a los cuales se puede acceder mediante distintos protocolos de comunicación, por ejemplo mediante mensajería JMS, FTP, o vía Web Services, entre otros. La siguiente imagen muestra un ejemplo de un servicio con su pipeline de acciones al cual se accede mediante el protocolo FTP y termina ejecutando lógica de negocio. [1]

Figura 2: Acciones en un servicio del ESB

El pipeline de acciones se parece mucho al estilo de arquitectura “tubos y filtros”, donde un mensaje es pasado por cada acción, las cuales realizan algún tipo de transformación en los datos del mensaje, y el resultado queda disponible para la siguiente acción. Es necesario diferenciar los conceptos de “mensaje del ESB” [2] y “mensaje”. El ESB utiliza un mensaje interno para transportar los mensajes que le son enviados, ese es el “mensaje del ESB”. El “mensaje del ESB” podría transportar múltiples mensajes distintos dentro del ESB, y las acciones podrías acceder a diferentes mensajes para obtener datos o modificarlos. Por ejemplo, el mensaje del ESB podría contener varios mensajes XML distintos en su Body. Para el caso del taller, el mensaje del ESB contendrá solo un mensaje XML en su Body. El mensaje del ESB cuenta con los siguientes campos :

• Body : mantiene información arbitraria que puede ser agregada y modificada por el usuario y por las acciones del canal.

• Attachment : contiene información extra a la que aparece en el Body. • Context : el contexto es la sección del mensaje que contiene información para manejar la sesión,

transacciones, seguridad, etc. • Fault : sirve para especificar distintas fallas que se podrían dar en la comunicación y devolver un

mensaje acorde (es similar a una excepción). • Header : es el cabezal del mensaje. • Properties : mantiene propiedades arbitrarias del mensaje en la forma de un mapa <String,

Object>. En la figura 3 se muestra la arquitectura de JBossESB, con las diversas opciones de puntos de entrada al ESB y las opciones de comunicación con componentes de más bajo nivel (lógica de negocio).

Page 5: Análisis de capacidades de transformacion de mensajes en jboss-esb

Figura 3: Arquitectura de JBossESB

Page 6: Análisis de capacidades de transformacion de mensajes en jboss-esb

Alternativas de transformación de mensajes JBossESB utiliza como principal componente de transformación al proyecto Smooks. Smooks es un componente de transformación que “vive” dentro de otro proyecto mayor relacionado con el procesamiento y transformación de datos, el proyecto Milyn (Data Transformation and Análisis). [5][6][7] Smooks ofrece transformaciones entre distintos formatos de mensajes, por ejemplo brinda transformación desde y hacia: XML, EDI, Java (*), CSV, JSON, entre otros. Los mecanismos de transformaciones que ofrece Smooks y que pueden ser de utilidad para este taller son:

� Transformaciones Java � Transformaciones XSLT � Transformaciones FreeMarker � Transformaciones basadas en modelos (model-driven transformations) � Transformaciones Java-Java mediante configuración Smooks (Java Bindings)

Los tipos de transformación a analizar serían ente los siguientes formatos (**):

� Transformación Java-Java � Transformación XML-XML � Transformación XML-Java [7.2] � Transformación Java-XML [7.3]

Los mecanismos de transformación indican “como” es que se lleva a cabo la transformación, los tipos de transformación indican “entre que” formatos se está transformando, es decir que el “tipo” de transformación es independiente del “mecanismo” que se aplique para implementar la transformación. Por ejemplo, se podría hacer una transformación XML-XML mediante XSLT o FreeMarker, o una transformación Java-Java basada en modelos o mediante configuración Smooks. (*) Cuando se menciona que un mensaje se encuentra en formato Java, se refiere a que el mensaje es una instancia en memoria de un modelo de datos particular. (**) La notación de los tipos de transformación es: formato_origen – formato_destino, por ejemplo: XML-Java, es la notación de una transformación que toma un mensaje en formato XML y devuelve un mensaje en formato Java.

Mecanismos de transformación

Transformación Java Este mecanismo de transformación es implementada directamente con lógica Java. La configuración es mínima y la lógica hace todo el trabajo de transformación, el tipo de transformación puede ser Java-Java o XML-XML. La transformación Java puede hacerse dentro de Smooks o directamente como una acción del pipeline de acciones de un canal del ESB. Para implementar una acción en el ESB, es necesario crear una clase que extienda a org.jboss.soa.esb.actions.AbstractActionPipelineProcessor, si la clase que implementa dicha acción se llama ActionProcessor, el segmento de configuración del ESB necesario para agregar dicha acción al pipeline de acciones de un servicio del ESB sería similar al siguiente: <actions> ... <action class="paquete.ActionProcessor" name="ContentFilter"/> ... </actions> La propia implementación de la transformación dependerá del formato del mensaje de entrada y del formato del mensaje de salida. Este mecanismo puede ser ventajoso cuando se necesiten hacer transformaciones muy particulares o que necesiten poco código para ser implementadas, comparando con otros mecanismos que para algún caso particular necesiten más código que la implementación directa en Java.

Page 7: Análisis de capacidades de transformacion de mensajes en jboss-esb

Transformación XSL En la transformación XSL el trabajo de transformación se configura mediante reglas XSL en un documento XML. La configuración de Smooks es mínima, solo se necesita decir donde está el archivo XSL con la transformación, y el intérprete XSL es el que ejecuta la transformación. También se puede poner la transformación XSL directamente dentro del mismo archivo de configuración de Smooks, por lo que no es obligatorio tener un archivo XSL aparte. Ejemplo de configuración de Smooks para una transformación XSL: <smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.0.xsd"> <resource-config selector="$document"> <resource>/ConversionXSL/transformacion.xsl</resource> </resource-config> </smooks-resource-list> En las últimas versiones de JBossESB se ha agregado también la posibilidad de realizar transformaciones XSL nativas, es decir, sin necesidad de utilizar Smooks para dicho propósito. Entonces con transformaciones XSL nativas se utilizaría el intérprete XSL de JBossESB en lugar del intérprete XSL de Smooks.

Ejemplo de transformación XSL nativa: Fue creada una clase llamada XSLActionProcessor extiende la clase AbstractActionPipelineProcessor, esto es para poder ser llamada como una acción cuando se ejecute el pipeline de acciones del servicio en el ESB. En XSLActionProcessor se implementa la configuración, carga de la transformación XSL y ejecución de la transformación. El constructor de la clase oficia de operación de configuración de la transformación, que previamente a que el ESB ejecute la acción XSLActionProcessor en el pipeline de acciones, configura la acción para su correcta operación obteniendo el nombre del archivo donde se encuentra la transformación y el mensaje del ESB que contiene el mensaje XML a ser transformado. Luego, automáticamente, el ESB invoca al método “process”, que es donde se ejecuta la lógica de la acción, en este caso, es donde se carga la transformación XSL, donde se realiza la transformación y donde se coloca el resultado en el mensaje del ESB para que pueda ser accedido por la próxima acción del pipeline. El método “getStylesheet” se utiliza como auxiliar para cargar el archivo XSL (con la transformación) desde el sistema de archivos. Luego se creó la clase XSLTransformer, que es en la que se realiza efectivamente la transformación XLS. public class XSLActionProcessor extends AbstractActionPipelineProcessor { private static Map<String, String> cache = new HashMap<String, String>(); private String xsl; private MessagePayloadProxy payloadProxy; public XSLActionProcessor(ConfigTree configTree) { xsl = configTree.getAttribute("xsl"); payloadProxy = new MessagePayloadProxy(configTree); } public Message process(Message message) throws ActionProcessingException { try { XSLTransformer transformer = new XSLTransformer(); System.err.println("XSLActionProcessor INPUT: " + message); byte[] bytes = ((String) payloadProxy.getPayload(message)).getBytes(); ByteArrayInputStream input = new ByteArrayInputStream(bytes);

Page 8: Análisis de capacidades de transformacion de mensajes en jboss-esb

String theXSL = getStylesheet(xsl); ByteArrayOutputStream output = (ByteArrayOutputStream) transformer.transform(input, theXSL); message.getBody().add(output); return message; } catch (Exception e) { throw new ActionProcessingException(e); } } /** * Carga hoja de estilo XSL de un archivo o del cache. * * @return * @throws IOException */ private String getStylesheet(String stylesheetFileName) throws IOException { String result = null; synchronized (cache) { result = cache.get(stylesheetFileName); if (result == null) { File file = new File(stylesheetFileName); if(!file.exists()) { throw new IllegalArgumentException("Input message file [" + file.getAbsolutePath() + "] not found."); } result = FileUtil.readTextFile(file); cache.put(stylesheetFileName, result); } else { System.err.println("Stylesheet retrieved from cache"); } return result; } } } public class XSLTransformer { private static TransformerFactory tf = TransformerFactory.newInstance(); /** * Ejecuta la transformacion XSL. * * @param input stream con datos de entrada * @param xsl transformacion XSL (no el nombre del archivo es el contenido) * @return La informacion transformada * @throws TransformerException */ public OutputStream transform(InputStream input, String xsl) throws TransformerException { if (input == null || xsl == null) throw new IllegalArgumentException("input cannot be null"); Transformer t = tf.newTransformer( new StreamSource( new StringReader(xsl) ) ); ByteArrayOutputStream output = new ByteArrayOutputStream(4096); t.transform(new StreamSource(input), new StreamResult(output)); return output; } }

Page 9: Análisis de capacidades de transformacion de mensajes en jboss-esb

Transformaciones Java-Java mediante configuración S mooks Este mecanismo de transformación se utiliza cuando se tiene una instancia de un modelo Java y se quiere generar una instancia de otro modelo con la información que tiene el primer modelo. Existen dos conceptos importantes llamados “Binding” y “Wiring”. “Binding” se utiliza para generar las instancias simples de las clases del modelo destino, a las cuales se les setean los datos del modelo origen. “Wiring” se utiliza para generar relaciones entre las instancias simples del modelo destino para formar una estructura de datos completa. Ambas tareas se realizan mediante reglas de configuración de Smooks basadas en XML. [7.1] Una característica interesante es que para seleccionar los valores de los atributos del modelo origen se utilizan expresiones XPath sobre un XML. Dicho XML es generado internamente por Smooks, a partir del modelo de entrada, mediante XStream. XStream es una librería que permite serializar cualquier instancia de modelo de datos a XML y volver del XML a la instancia. Entonces es necesario saber que forma tiene el XML generado a partir del modelo de entrada para poder escribir las expresiones XPath. Este mecanismo de transformación tiene varias restricciones, sobre todo en el modelo destino. En el modelo origen no existen restricciones particulares, ya que en realidad la transformación se realiza tomando la serialización a XML del modelo origen y no el propio modelo. Para la transformación Java-Java es necesario que el modelo destino se comporte según las siguientes reglas:

• Debe tener un constructor simple (sin argumentos). • Debe tener setters públicos para las propiedades de cada clase. No es necesario seguir algún

formato de nombres particular para estos métodos, pero se aconseja seguir el estándar de nombres para setters de Java.

• Setear una propiedad de forma directa no está soportado, es necesario hacerlo invocando al setter de la propiedad.

Si no se siguen estas reglas, el transformador Smooks configurado mediante XML no sabrá como establecer los valores, extraídos del modelo origen, en el modelo destino. El problema que presenta esto, es que en la transformación de un modelo “custom” en el modelo de referencia de HL7 (RIM), se debe utilizar alguna implementación de RIM. JavaSIG es una librería para generar y consumir mensajes HL7, pero su implementación no sigue las reglas impuestas por este mecanismo de transformación, por lo que no es posible utilizar dicha implementación. Se hizo una prueba de esto, constatándose la imposibilidad de generar una instancia del RIM mediante este mecanismo de transformación. Para resolver este problema se plantearon las sigui entes alternativas:

1. Probar la transformación generando el RIM a partir de esquemas XSD mediante JAXB y probar utilizar esas clases.

2. No hacer la transformación mediante configuración XML, si no hacer una transformación directamente implementada en Java.

3. Modificar el RIM implementado en JavaSIG para que las clases sigan las reglas antes mencionadas.

1. Utilizando JAXB y transformación Java-Java: Se pudo generar un modelo de clases a partir de los esquemas XSD para los mensajes CDA provistos por la especificación de HL7 mediante la librería JAXB. En el anexo “Prueba transformación Java-Java BindingAndWiring” se comenta cómo se generó el modelo a partir del XSD utilizando JAXB. Uno de los problemas que tiene este modelo de clases es que para las colecciones no tiene un método del tipo “add to collection”, que es lo que necesita Smooks para generar la colección y agregarle elementos, las clases que tienen atributos de tipo colección tienen un método get para la colección y luego se invoca el “add” directamente sobre la colección. Para poder ejecutar una transformación mínima se tuvo que modificar algunas clases que tenían atributos de tipo colección, agregando un método “addXXX” donde “XXX” es el nombre del campo de tipo colección. Un tema a tener en cuenta es que Smooks sirve para colocar el modelo origen en un modelo destino, pero no se puede generar más modelo. Por ejemplo, si el modelo destino tiene más información que el

Page 10: Análisis de capacidades de transformacion de mensajes en jboss-esb

mensaje original, este tipo de transformación no permite generar el modelo extra. Una posible solución es la de agregar la información faltante en una etapa posterior a la transformación Java-Java, por ejemplo utilizando transformaciones XSL o templates FreeMarker. Esto sucede cuando se tiene un modelo “custom” y se quiere llevar a un CDA, se pueden colocar los valores presentes en el modelo “custom” que se correspondan en la estructura CDA, pero CDA necesita mucha más información, porque tiene una estructura más compleja y muchos atributos que no existen en el modelo “custom”, esto es porque CDA agrega información para poder hacer una interpretación semántica de los documentos CDA. Por ejemplo existen muchos atributos con valores fijos que no pueden ser generados directamente con este mecanismo de transformación. En el caso inverso no habría problemas, es decir pasar CDA a el modelo “custom”, porque se toman del CDA solo los valores que se necesiten, los demás valores no serían utilizados para generar la instancia del modelo “custom”. En conclusión, esta prueba necesita gran cantidad de código de configuración para poder hacer la transformación Java-Java mediante Binding y Wiring, y las restricciones sobre el modelo de salida lo hacen poco flexible. Además la configuración es como implementar la transformación directamente en Java, lo que no solo llevaría menos código, si no sería más flexible, ya que puedo utilizar la API que me provea el modelo destino sin ninguna restricciones sobre los métodos que debe proveer. En el anexo “Prueba transformación Java-Java BindingAndWiring” están las referencias de implementación de la transformación probada y los resultados obtenidos.

2. Implementar la transformación directamente en Ja va: Esta opción no fue probada por ser considerada la opción más simple, ya que lo único que se necesita hacer es generar una instancia del modelo destino, e ir pidiendo los valores a la instancia del modelo origen y setear dichos valores en la instancia destino. Esta opción no tendría inconvenientes con la API del modelo destino, al contrario del mecanismo de Bindings y Wirings que pone estrictas reglas sobre dicha API. De modo que esta es una solución más genérica que la antes mencionada y también necesita escribir menos código para ser implementada, la diferencia es que mientras aquí se escribe código Java, en la opción anterior se escribían reglas en formato XML. Como conclusión, esta sería una buena opción si se tiene un modelo de datos destino con una API que no cumple con las restricciones del método anterior, o también si la estructura es muy compleja, cuanto más compleja es la estructura más reglas deben ser escritas y cada regla lleva en promedio 8 líneas de código, si en una implementación Java se necesitaran menos líneas que estas, al agregar atributos o relaciones, se necesitaría mucho menos código para implementar la misma transformación.

3. Modificar JavaSIG Esta puede ser la opción más costosa, ya que la API de la implementación del RIM de JavaSIG es muy particular, no solo habría que modificar decenas de clases, si no que habría que escribir gran cantidad de reglas para poder realizar la transformación. Esta opción no se llevó a cabo por las razones mencionadas. En este caso, implementar la transformación directamente en Java sería una mejor opción.

Page 11: Análisis de capacidades de transformacion de mensajes en jboss-esb

Transformación FreeMarker En la transformación FreeMarker, la información de la transformación se encuentra en la configuración de Smooks (definido mediante reglas FreeMarker) o también puede estar en un archivo aparte (los archivos FreeMarker tienen extensión .FTL por FreeMarker Template). El formato en el que se especifica es el marcado por FreeMarker y se puede acceder aquí: http://www.milyn.org/xsd/smooks/freemarker-1.1.xsd, es declarativo como el XSL pero parece menos verboso que este último, o sea, con menos código se hace lo mismo. FreeMarker es una herramienta de “templating” o aplicación de plantillas que sirve para recorrer un archivo de texto (estructurado o no), extrayendo su información y generando un nuevo archivo te texto (estructurado o no) con un formato distinto. FreeMarker es soportado por los creadores de Smooks, los cuales lo prefieren sobre XSL. Comparándolo con XSL, FreeMarker está hecho para transformación de texto en general, mientras que XSL solo sirve para procesar texto estructurado en forma de árbol, como ser un archivo XML. Por otra parte, FreeMarker tiene constructores basados en programación imperativa, mientras que XSL los constructores están basados en programación funcional, esto se refiere a la característica de poder definir funciones y variables en cada una de las herramientas, los desarrolladores de FreeMarker afirman que esto es una ventaja porque la mayoría de los desarrolladores conocen la programación procedural-imperativa, mientras que pocos conocen la programación funcional. FreeMarker también apunta a que sea más fácil de aprender y entender que XSL. Obviamente la principal diferencia entre ambas tecnologías es que XSL es un estándar y tiene diversas implementaciones, y FreeMarker una herramienta Java. Si bien se realizó una implementación de transformación utilizando FreeMarker, sería interesante poder comparar más a fondo las opciones de transformación utilizando FreeMarker y XSL, y verificar cual es la opción más simple de utilizar, la que posee más funcionalidades que ayuden a realizar transformaciones según las características particulares de las transformaciones que se necesiten, si es necesario escribir más o menos reglas de transformación, y evaluar la capacidad, características y estabilidad de cada herramienta. Lo que si se constató es que en las pruebas, para lograr el mismo resultado en la transformación XML custom a XML CDA, el trabajo que tomó hacer funcionar la transformación XSL fue aproximadamente una semana, mientras que el trabajo que tomó hacer funcionar la transformación completa con FreeMarker fueron alrededor de cuatro horas. También se pudo ver que el código necesario para especificar la transformación XSL es aproximadamente tres veces más que el código necesario para especificar el template FreeMarker. Obviamente esta es una comparación en solo una transformación particular, por lo que no se puede derivar un resultado general y decir que estas diferencias se dan en otras transformaciones. Se realizó una implementación de una transformación de un mensaje XML “custom” a un mensaje XML CDA, la documentación se encuentra en el documento anexo “Prueba transformación XML-XML FreeMarker”.

Transformación basada en modelo Por último, para las transformaciones basadas en modelos, es necesario definir un modelo de datos intermedio entre la entrada y la salida, al cual se pueda mapear la entrada y desde el cual se pueda generar la salida. El único código Java a implementar es dicho modelo de datos y la transformación se especifica en la configuración de Smooks, donde se definen las correspondencias del formato de entrada al modelo y del modelo al formato de salida. La ventaja de este esquema es la posibilidad de definir múltiples formatos de entrada y salida pudiendo hacer combinaciones de éstos sin necesidad de especificar todas las combinaciones entrada-salida, solo se especifican las diferentes combinaciones entrada-modelo y modelo-salida, luego automáticamente se obtienen todas las combinaciones entrada-modelo-salida. Para la transformación Java-Java se utiliza la notación que se utiliza en la transformación basada en modelos para especificar dicha transformación. Esta notación está basada en “bindings” que toman un valor de la fuente y lo ponen en un lugar del destino, en el caso Java-Java, esto es hacer “gets” en el modelo fuente y “sets” en el modelo destino.

Page 12: Análisis de capacidades de transformacion de mensajes en jboss-esb

Propuesta de transformación Java-Java híbrida Si bien se analizaron varias opciones de transformación entre instancias de modelos Java, hasta ahora no se analizó la posibilidad de mezclar otros mecanismos de transformación para lograrlo. Como se comentó en la respectiva sección, para realizar la transformación Java-Java, Smooks internamente serializa la instancia del modelo origen a un XML mediante la librería XStream. Transformación Java-Java mediante FreeMarker + XStr eam Esta opción utilizaría la transformación FreeMarker y la funcionalidad de convertir XML a una instancia y viceversa de XStream, los pasos serían los siguientes:

1. Recibir una instancia del modelo origen. 2. Registrarla como bean en el mapa de beans del contexto de la transformación Smooks. 3. Tener un template FreeMarker que ejecuta la transformación obteniendo los datos desde el bean

registrado con el modelo original y generando el XML del modelo destino, siguiendo el formato de XStream de una instancia válida de dicho modelo.

4. Mediante XStream, levantar el XML generado y convertirlo a la instancia del modelo destino en memoria.

Figura 4: Transformación Java-Java con FreeMarker+XStream

Aunque esta opción es similar a la prueba de transformación XML-XML mediante FreeMarker, los pasos de transformación no son los mismos (por lo menos no en el mismo orden). La documentación completa sobre la transformación XML-XML mediante FreeMarker se encuentra en el documento anexo llamado “Prueba transformacion XML-XML FreeMarker”. Otra opción sería teniendo una transformación XSL en lugar del template FreeMarker para generar el XML de salida compatible con XStream para que este pueda generar una instancia del modelo de salida. Distintas opciones de transformación se pueden generar mezclando los mecanismos ya vistos, también se pueden utilizar otras herramientas como XStream, esto posibilita la realización de distintos pasos de transformación de uno o más mensajes de forma simultánea, donde se pueden realizar distintas transformaciones mediante distintos mecanismos para diferentes secciones de los mensajes que se procesen.

Page 13: Análisis de capacidades de transformacion de mensajes en jboss-esb

Especificación Técnica de Implementación de HL7 Si bien todos los mecanismos vistos hasta el momento son válidos para definir transformaciones entre distintos formatos de mensajes, siendo uno de los formatos HL7, la mayoría de los mismos no cumplen con la especificación técnica de implementación de HL7 (ITS por sus siglas en inglés). La ITS es la forma estándar en la que dos sistemas deberían interoperar utilizando mensajes HL7. En la misma se definen una serie de transformaciones que son necesarias para que dos sistemas que tengan su propio modelo de datos, lo puedan hacer corresponder en el RIM (modelo de referencia de HL7). Estas correspondencias de su modelo de datos al RIM y viceversa garantiza que el intercambio de datos será semánticamente correcto, y esto habilita a los sistemas para poder enviar y recibir mensajes HL7, que no son más que instancias de RIM serializadas a XML. En la siguiente imagen se muestran los pasos definidos por la ITS:

Figura 5: Especificación Técnica de Implementación

El escenario que plantea la ITS es el de dos sistemas, con un sistema emisor y otro receptor. Cada uno con un modelo particular de datos, más algún mecanismo que ejecute transformaciones entre esos datos e instancias de RIM. Los pasos que sigue la ITS son:

1. El sistema “emisor” tiene los datos que desea enviar al receptor en su propio modelo. 2. Mediante una transformación, genera una instancia de RIM a la cual le incluye los datos de su

modelo. 3. Ahora se tiene una instancia de RIM con todos los datos que se quieren enviar. Esta instancia no

es más que el mensaje que se va a enviar, en memoria. 4. Mediante algún mecanismo que serialice modelos RIM, se genera un mensaje XML válido según

los esquemas XSD distribuidos por HL7 (dependiendo del tipo de mensaje, el XSD puede variar). 5. Ahora el mensaje en formato XML es enviado a través de un canal hacia el sistema “receptor”. 6. El “receptor” recibe el XML y reconstruye la instancia de RIM a partir del mensaje XML. 7. Se obtiene una instancia de RIM análoga a la que se obtuvo en el paso 3 dentro del sistema

“emisor”. 8. A través de una transformación definida en el sistema “receptor”, se genera una instancia del

modelo de datos de dicho sistema a partir de los datos contenidos en la instancia de RIM. 9. Se tiene la instancia del modelo de información del sistema “receptor”, que es semánticamente

equivalente a la instancia del sistema de información del sistema “emisor” (la información que este sistema quería transmitir).

Page 14: Análisis de capacidades de transformacion de mensajes en jboss-esb

Propuesta de transformación siguiendo la HL7 ITS Por lo mencionado anteriormente resulta interesante plantear una solución de transformación que cumpla con la ITS y utilice el ESB para realizar las transformaciones necesarias.

Figura 6: Arquitectura del problema de envío y transformación de mensajes

En el caso de que el Sistema A (emisor) envíe mensajes “custom” al Sistema B (receptor) el cual los recibe en formato HL7, donde las comunicaciones se realizan mediante Web Services, en el ESB se haría el pasaje del modelo SOAP a una instancia del modelo de datos del Sistema A. Luego, dentro del ESB se hace la conversión entre ese modelo de datos y el modelo RIM, por último se genera el XML a partir de la instancia RIM obtenida (esta transformación es automática utilizando una librería como JavaSIG). Ese XML es el que es enviado al Sistema B. Para el caso inverso, donde el Sistema B envía un XML HL7 al ESB, el ESB lo transforma a una instancia del RIM (esta transformación es automática utilizando una librería como JavaSIG). Luego, con los datos del modelo RIM se genera el modelo de datos que espera recibir el Sistema A, ese modelo es enviado directamente al Sistema A mediante Web Services SOAP (SOAP se encarga de hacer la serialización a XML).

Page 15: Análisis de capacidades de transformacion de mensajes en jboss-esb

Relevamiento de opciones para ruteo de mensajes El esquema general de procesamiento de un mensaje recibido es similar al estilo de arquitectura de “tubos y filtro”, donde un mensaje es recibido por un filtro, procesado y luego puesto en un tubo hacia otro filtro. En este esquema debe haber algún mecanismo que decida en que tubo se coloca la salida de cada filtro, porque puede haber varios tubos distintos de donde elegir. La selección del tubo en el que se colocará la salida del filtro es llamado “ruteo” del mensaje, y se realiza mediante la verificación de un conjunto de condiciones, lo que permite tomar la decisión de cual será el tubo destino, entonces la ruta del mensaje será el conjunto de tubos y filtros por los que ha pasado el mismo. JBossESB ofrece cuatro tipos de ruteo:

1. Aggregator: es una implementación del patrón “Aggregator” de los “Enterprise Integration Patterns”, el mismo se utiliza para combinar información de varios mensajes distintos. [9]

2. Content Based Router (CBR): acción de ruteo basado en el contenido de los mensajes y la

definición de reglas. 3. Static Router: versión simplificada del Content Based Router que no soporta reglas de ruteo

basadas en contenido.

4. Notifier: envía notificaciones a una lista de destinos configurable. Está implementada como ejemplo, no debería ser usada directamente, si no que debería ser extendida por el usuario.

Considerando los casos de prueba planteados para realizar en el presente trabajo (*), el “Content Based Router” podría ser de gran utilidad porque podría detectar que tipo de mensaje está recibiendo el ESB, y en base a reglas que se definan, rutear el mensaje al servicio correcto que pueda procesarlo. El CBR puede ser utilizado para enrutar el mensaje al próximo destino basándose en el contenido del mensaje. Por defecto el ESB utiliza “JBossRules” como motor de evaluación de reglas, aunque puede ser configurado para utilizar otro motor. Para utilizar el CBR se debe levantar el servicio de CBR. Luego se deben definir un conjunto de reglas (**). Para la definición de reglas sobre mensajes XML es conveniente utilizar evaluaciones basadas en XPath, especialmente diseñado para poder extraer valores presentes en el XML de forma sencilla. Una vez que se tiene el CBR andando, se le pueden enviar mensajes. Para esto se debe agregar dicho envío como una acción en algún lugar del pipeline de acciones. Luego existen tres modos de ejecución:

1. routeAndDeliver: rutea y entrega el mensaje al(los) destino(s)

2. route: rutea el mensaje y retorna un mensaje con una lista de destinos adjuntos a él.

3. deliver: simplemente entrega el mensaje, un mensaje ruteado previamente será entregado a su(s) destino(s).

(*) Los casos de prueba se mencionan en el documento “TSI4 Anexo 1 – Bitácora.doc”, en la sección “Etapa 2: Definición de la arquitectura y casos de prueba”. (**) Estas pueden ser creadas mediante JBossIDE [Eclipse+JBossTools] con el plugin de JBossRules.

Page 16: Análisis de capacidades de transformacion de mensajes en jboss-esb

Descripción del prototipo logrado Para la implementación del prototipo se eligió la transformación XML-XML mediante XSL. El prototipo define dos canales, uno que recibe un mensaje XML “custom”, mediante un punto de entrada expuesto mediante Web Services, y genera un mensaje CDA que es enviado a un sistema externo mediante Web Services, el otro recibe un mensaje CDA, mediante un punto de entrada expuesto mediante Web Services, y genera un mensaje XML “custom” que es enviado a otro sistema mediante Web Services. El prototipo fue implementado en el proyecto “ESBWS” incluido en el directorio de código de la entrega del taller. Los pipelines de acciones de ambos canales son similares:

Figura 7: Canales en el ESB prototipo.

Lista de acciones y su funcionalidad:

• print-before : se encarga de mostrar en pantalla el mensaje entrante.

• ContentFilte r: transforma el mensaje en el formato de entrada al formato de salida.

• print-after : muestra en pantalla el mensaje de salida.

• testStore : almacena el mensaje de salida en el filesystem.

• request-ws-processor : se encarga de preparar la llamada al Web Service, especificando que método del servicio será invocado y con que parámetros.

• soap-ws-client : esta acción invoca al Web Service.

• response-ws-processor: muestra en pantalla el resultado de la invocación al Web Service.

Algunas acciones tienen asociadas una clase Java que es la que implementa la acción, otras poseen una implementación por defecto provista por el ESB, este último caso es el de las acciones: print-before, print-after y soap-ws-client.

Page 17: Análisis de capacidades de transformacion de mensajes en jboss-esb

Descripción de la implementación de las acciones

Acción ContentFilter Esta acción está asociada a la clase XSLActionProcessor, que es la clase en la que se implementó dicha acción. La funcionalidad básica de esta clase es cargar la transformación XSL del filesystem, obtener el mensaje XML a transformar (el cual se encuentra en el body del mensaje del ESB), y aplicar efectivamente la transformación al mensaje. El mensaje XML resultante se coloca en el mensaje del ESB, así la próxima acción en el pipeline de acciones puede acceder al mensaje transformado. A continuación se presenta la implementación de la clase XSLActionProcessor, la cual ejecuta una transformación XSL nativa sobre un mensaje XML. Esta acción es utilizada en ambos canales, al que se envía un mensaje custom y al que se envía un mensaje HL7, como implementación de sus respectivas acciones ContentFilter. Obviamente, en cada canal debe implementar una transformación distinta, esto se lleva a cabo mediante la configuración de una transformación XSL distinta para cada caso. Todas las acciones en el pipeline deben extender la clase AbstractActionPipelineProcessor [4], o su superclase AbstractActionLifecycle [3], la cual define el método “process” que será descrito más adelante. public class XSLActionProcessor extends AbstractActionPipelineProcessor { El campo “cache” es utilizado para almacenar la transformación XSL que es levantada desde el filesystem, de manera que se tenga que hacer una sola lectura del filesystem y en posteriores ejecuciones se obtiene el XSLT desde el cache. private static Map<String, String> cache = new HashMap<String, String>(); El campo “xsl” es el que almacena el nombre del archivo XSL que deberá ser leído. private String xsl; El campo “payloadProxy” se utiliza para acceder a los contenidos del mensaje del ESB. private MessagePayloadProxy payloadProxy; El constructor recibe la configuración del ESB con toda la información necesaria para que la acción pueda ejecutarse correctamente. Por ejemplo se extrae el nombre de la transformación XSL que hay que leer, y trae el mensaje del ESB de donde se leerá el mensaje XML a ser transformado. public XSLActionProcessor(ConfigTree configTree) { xsl = configTree.getAttribute("xsl"); payloadProxy = new MessagePayloadProxy(configTree); } El método “process” es donde se realiza la transformación. public Message process(Message message) throws ActionProcessingException { Aquí se crea la instancia del transformador, donde se realiza la transformación. try { SAXONTransformer transformer = new SAXONTransformer(); System.err.println("XSLActionProcessor INPUT: " + message); Se leen los bytes del mensaje XML de entrada y ponen dentro de la variable “input”. byte[] bytes = ((String) payloadProxy.getPayload(message)).getBytes(); ByteArrayInputStream input = new ByteArrayInputStream(bytes);

Page 18: Análisis de capacidades de transformacion de mensajes en jboss-esb

Se lee el archivo de la transformación del filesystem, o desde cache si ya fue cargado, y se pone su contenido en la variable “theXSL”. String theXSL = getStylesheet(xsl); Mediante el transformador, se ejecuta la transformación XSL sobre el input, obteniéndose el resultado en la variable “output”. ByteArrayOutputStream output = (ByteArrayOutputStream) transformer.transform(input, theXSL); System.err.println("XSLActionProcessor OUTPUT: " + output); El mensaje obtenido se agrega al mensaje del ESB para que quede disponible para la siguiente acción del pipeline. message.getBody().add(output); return message; } catch (Exception e) { throw new ActionProcessingException(e); } } El método “getStylesheet” se utiliza para leer el archivo que contiene la transformación desde el filesystem, o desde el cache si es que ya ha sido leído. private String getStylesheet(String stylesheetFileName) throws IOException { String result = null; synchronized (cache) { Se intenta obtener el contenido de la transformación desde el cache. result = cache.get(stylesheetFileName); if (result == null) { System.err.println("Loading stylesheet from file "+stylesheetFileName); Se lee el archivo de disco. File file = new File(stylesheetFileName); if(!file.exists()) { throw new IllegalArgumentException("Input message file [" + file.getAbsolutePath() + "] not found."); } result = FileUtil.readTextFile(file); Se agrega el contenido leído al cache para acelerar futuras cargas. cache.put(stylesheetFileName, result); } else { System.err.println(" Stylesheet retrieved from cache"); } return result; } }

Page 19: Análisis de capacidades de transformacion de mensajes en jboss-esb

Acción testStore Esta acción almacena el mensaje transformado en el filesystem del sistema donde esté corriendo el ESB. La misma está implementada en la clase StoreMessage. Como toda acción del pipeline, la clase StoreMessage extiende la clase AbstractActionPipelineProcessor. public class StoreMessage extends AbstractActionPipelineProcessor { private MessagePayloadProxy payloadProxy; public StoreMessage(ConfigTree config) { payloadProxy = new MessagePayloadProxy(config); } El método “appendToFile” toma cualquier contenido en forma de String y lo agrega al final de un archivo, conservando su contenido actual. El archivo es creado si es que no existe. public static void appendToFile(String dir, String fileName, String content) { BufferedWriter bw = null; try { bw = new BufferedWriter(new FileWriter(dir + "/" + fileName, true)); bw.write(content); bw.newLine(); bw.flush(); } catch (IOException ioe) { ioe.printStackTrace(); } finally { // always close the file if (bw != null) try { bw.close(); } catch (IOException ioe2) { /* just ignore it */ } } // end try/catch/finally } El método “process” genera un nombre de archivo basado en la fecha actual y una serie de números random, de forma de generar un archivo diferente a los demás. Luego obtiene el contenido del mensaje a almacenar, esto lo saca del mensaje del ESB a través del payloadProxy que se utiliza para acceder a los datos del mensaje del ESB. Por último se llama al método “appendToFile” para generar el archivo con el contenido obtenido. public Message process(Message message) throws ActionProcessingException { Date d = new Date(); String name = ""+(d.getYear()+1900)+(d.getMonth()+1)+d.getDate(); try { System.err.println("PayloadMessageClass: "+ payloadProxy.getPayload(message).getClass()); byte[] btextToStore = ((ByteArrayOutputStream) payloadProxy.getPayload(message)).toByteArray(); System.err.println( "textToStore: " + new String(btextToStore) ); String textToStore = new String(btextToStore); appendToFile ("./", "message_"+name+"["+(Math.random()*5)+"].xml", textToStore.toString()); } catch (MessageDeliverException e) { throw new ActionProcessingException(e); } return message; } }

Page 20: Análisis de capacidades de transformacion de mensajes en jboss-esb

Acción request-ws-processor Esta acción se utiliza para preparar la llamada al Web Service al que se enviará el mensaje transformado, esto está implementado en la clase MyRequestAction. Esta clase MyRequestAction extiende a la clase AbstractActionLifecycle [3]. Antes se vio que las acciones extendían a la clase AbstractActionPipelineProcessor, esto es porque AbstractActionPipelineProcessor a su vez extiende a AbstractActionLifecycle. public class MyRequestAction extends AbstractActionLifecycle { protected ConfigTree _config; private MessagePayloadProxy payloadProxy; En el constructor se recibe la configuración del ESB y el estado actual del mensaje. public MyRequestAction(ConfigTree config) { _config = config; payloadProxy = new MessagePayloadProxy(config); } El método “process” es quien obtiene el mensaje a enviar y configura los parámetros para la invocación al Web Service. En este caso se obtiene el mensaje XML transformado, que está dentro del mensaje del ESB. Luego se agrega a un mapa de parámetros mediante la clave “sendMessage.message”, esto quiere decir que se va a invocar al método “sendMessage” del Web Service, en donde existe un parámetro llamado “message” que tendrá como valor el mensaje XML transformado. public Message process(Message message) throws Exception { byte[] btext = ((ByteArrayOutputStream) payloadProxy.getPayload(message)).toByteArray(); String msgBody = new String(btext); HashMap requestMap = new HashMap(); // add paramaters to the web service request map // Esto es: metodo.paramName requestMap.put("sendMessage.message", msgBody); message.getBody().add(requestMap); System.out.println("Request map is: " + requestMap.toString()); return message; } }

Acción response-ws-processor Esta acción simplemente muestra en pantalla el resultado de la invocación al Web Service. Está implementada en la clase MyResponseAction, la cual es análoga a la anterior, solo que en el método “process” se obtiene el resultado de la invocación al Web Service desde el mensaje del ESB y se hace un print del mismo a la consola. public class MyResponseAction extends AbstractActionLifecycle { protected ConfigTree _config; public MyResponseAction(ConfigTree config) { _config = config; } public Message process(Message message) throws Exception { Map responseMsg = (Map)message.getBody().get(Body.DEFAULT_LOCATION); System.out.println("Response Map is: " + responseMsg); return message; } }

Page 21: Análisis de capacidades de transformacion de mensajes en jboss-esb

Trabajo futuro y mejoras

1. Transformación XSL genérica: sería el generar una transformación XSL que sea equivalente al modelo CDA, de esa forma se podría consumir o producir cualquier CDA válido. Posteriormente a esta prueba se buscarían las conclusiones de si es posible crear tal transformación, de que tan costosa sería crearla (comparándola con otros tipos de transformaciones), verificar si tiene problemas de performance y probar varios casos con distintos CDA.

2. Ruteo del mensaje: si bien en el presente trabajo se relevaron las opciones de ruteo que ofrece JBossESB, no se agregó la funcionalidad de ruteo al prototipo. Sería interesante probar, por lo menos, el ruteo basado en contenido, de forma que si se recibe un mensaje CDA dependiendo de sus características sea enviado a un determinado canal del ESB (o a un servicio externo al ESB) para ser procesado total o parcialmente en dicho canal.

3. Solicitar información a servicios externos: un posible caso es que el mensaje entrante tenga menos información de la necesaria para generar el mensaje saliente, sería interesante que, basándose en la información que si se tiene, se consulten determinados servicios que provean la información faltante, de forma de contar con toda la información necesaria para generar el mensaje saliente.

4. Comparar en profundidad las alternativas de utilizar XSL y FreeMarker como opciones de transformación de mensajes, probando y comparando las funcionalidades que sean útiles según los casos particulares de prueba, con cual alternativa se necesita escribir menos código, cual es más sencilla de usar y aprender, etc.

Referencias [1] Proyecto JBossESB http://www.jboss.org/jbossesb/ [2] Mensaje de JBossESB http://www.jboss.org/jbossesb/docs/4.4.GA/javadoc/esb/org/jboss/soa/esb/message/package-summary.html [3] Clase AbstractActionLifecycle http://www.jboss.org/jbossesb/docs/4.4.GA/javadoc/esb/org/jboss/soa/esb/actions/AbstractActionLifecycle.html [4] Clase AbstractActionPipelineProcessor http://www.jboss.org/jbossesb/docs/4.4.GA/javadoc/esb/org/jboss/soa/esb/actions/AbstractActionPipelineProcessor.html [5] Message Transformation on JBossESB http://www.jboss.org/community/docs/DOC-11397 [6] Smooks User Guide http://docs.codehaus.org/display/MILYN/Smooks+User+Guide [7] Smooks Examples http://www.smooks.org/documentation/documentation-smooks-1-1-x/examples [7.1] Smooks Example JavaToJava: http://docs.codehaus.org/display/MILYN/Smooks+Example+-+java-to-java [7.2] Smooks Example XMLToJava: http://docs.codehaus.org/display/MILYN/Smooks+Example+-+xml-to-java [7.3] Smooks Example JavaToXML: http://svn.codehaus.org/milyn/trunk/smooks-examples/java-to-xml/ [8] Componente Smooks de JavaBeans http://milyn.codehaus.org/javadoc/v1.0/smooks-cartridges/javabean/ [9] Enterprise Integration Patterns http://www.enterpriseintegrationpatterns.com/eaipatterns.html