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

1,355 views

Published on

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

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
1,355
On SlideShare
0
From Embeds
0
Number of Embeds
5
Actions
Shares
0
Downloads
48
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

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

  1. 1. 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
  2. 2. ResumenHoy 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 nuevossistemas que se generen, el problema a resolver. Para intentar resolver los problemas deinteroperabilidad entre estos sistemas informático-clínicos existen distintos estándares propuestos por laindustria, dentro de estos HL7 es un grupo de estándares para resolver la interoperabilidad entre sistemasde registro clínico orientado al intercambio de mensajes. El presente proyecto intenta explorar unasolución de comunicación entre sistemas que son compatibles con HL7 y sistemas que no lo son y quemanejan su propio formato de mensajes para interoperar con otros sistemas. Esta solución se basará enun middleware JBossESB en donde se resolverán las diferencias entre los formatos de mensajes quesoportan los sistemas que intentan interoperar, de modo de que no sea necesario modificar un sistemaexistente para hacerlo compatible con mensajes HL7, si no que dicha “compatibilización” se hará dentrodel ESB.
  3. 3. Planteo del problemaEl objetivo principal del presente proyecto es investigar distintas opciones para implementar lacomunicación entre sistemas que se comuniquen mediante mensajes y que utilicen distintos formatospara dichos mensajes, en particular se buscará la interoperación entre sistemas cuyos mensajes sigan elestándar HL7 (*) y sistemas que utilicen otro formato de mensajes distinto a HL7. El objetivo es queninguno de los sistemas deba adaptar su formato de mensajes al formato del otro sistema, es decir que laconversión entre los distintos sistemas de mensajes será realizada dentro de un ESB. Dicho ESBmanejará los distintos aspectos referentes a la comunicación entre los sistemas. Este enfoque de manejode transformaciones entre distintos formatos de mensajes en el propio ESB permite que los sistemasexistentes 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 sedesea relevar y probar las distintas capacidades de transformación entre formatos de mensajes y tambiénrelevar 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 susmensajes a HL7. En la opción centrada en “ESB” la estandarización de los mensajes se realiza en el ESBy 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. Losmensajes están basados en el modelo de referencia de HL7 llamado RIM por sus siglas en inglés. Estos mensajes seutilizan para comunicar información clínica y demográfica de pacientes entre sistemas, como por ejemplo: resultadosde estudios, medidas de presión de sangre, datos personales, etc.
  4. 4. Introducción a JBossESBJBossESB es la solución de middleware para comunicación del stack de proyectos JBoss. JBossESBsirve como infraestructura de comunicación para otros proyectos como jBMP, la solución de BPM delstack JBoss. Con JBossESB se pueden definir servicios que consisten en una serie de acciones que seejecutan en secuencia llamado “pipeline” de acciones. Estos servicios pueden tener diversos puntos deentrada a los cuales se puede acceder mediante distintos protocolos de comunicación, por ejemplomediante mensajería JMS, FTP, o vía Web Services, entre otros. La siguiente imagen muestra un ejemplode un servicio con su pipeline de acciones al cual se accede mediante el protocolo FTP y terminaejecutando lógica de negocio. [1] Figura 2: Acciones en un servicio del ESBEl pipeline de acciones se parece mucho al estilo de arquitectura “tubos y filtros”, donde un mensaje espasado por cada acción, las cuales realizan algún tipo de transformación en los datos del mensaje, y elresultado queda disponible para la siguiente acción. Es necesario diferenciar los conceptos de “mensajedel ESB” [2] y “mensaje”. El ESB utiliza un mensaje interno para transportar los mensajes que le sonenviados, ese es el “mensaje del ESB”. El “mensaje del ESB” podría transportar múltiples mensajesdistintos dentro del ESB, y las acciones podrías acceder a diferentes mensajes para obtener datos omodificarlos. 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 alESB y las opciones de comunicación con componentes de más bajo nivel (lógica de negocio).
  5. 5. Figura 3: Arquitectura de JBossESB
  6. 6. Alternativas de transformación de mensajesJBossESB utiliza como principal componente de transformación al proyecto Smooks. Smooks es uncomponente de transformación que “vive” dentro de otro proyecto mayor relacionado con elprocesamiento 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óndesde y hacia: XML, EDI, Java (*), CSV, JSON, entre otros. Los mecanismos de transformaciones queofrece 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 detransformación indican “entre que” formatos se está transformando, es decir que el “tipo” detransformació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 unatransformació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 instanciaen 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 lanotación de una transformación que toma un mensaje en formato XML y devuelve un mensaje en formato Java.Mecanismos de transformaciónTransformación JavaEste mecanismo de transformación es implementada directamente con lógica Java. La configuración esmínima y la lógica hace todo el trabajo de transformación, el tipo de transformación puede ser Java-Javao XML-XML. La transformación Java puede hacerse dentro de Smooks o directamente como una accióndel pipeline de acciones de un canal del ESB. Para implementar una acción en el ESB, es necesario crearuna clase que extienda a org.jboss.soa.esb.actions.AbstractActionPipelineProcessor, si la clase queimplementa dicha acción se llama ActionProcessor, el segmento de configuración del ESB necesario paraagregar 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 delformato del mensaje de salida. Este mecanismo puede ser ventajoso cuando se necesiten hacertransformaciones muy particulares o que necesiten poco código para ser implementadas, comparandocon otros mecanismos que para algún caso particular necesiten más código que la implementacióndirecta en Java.
  7. 7. Transformación XSLEn la transformación XSL el trabajo de transformación se configura mediante reglas XSL en undocumento XML. La configuración de Smooks es mínima, solo se necesita decir donde está el archivoXSL con la transformación, y el intérprete XSL es el que ejecuta la transformación. También se puedeponer la transformación XSL directamente dentro del mismo archivo de configuración de Smooks, por loque 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 transformacionesXSL nativas, es decir, sin necesidad de utilizar Smooks para dicho propósito. Entonces contransformaciones XSL nativas se utilizaría el intérprete XSL de JBossESB en lugar del intérprete XSL deSmooks.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 enel ESB. En XSLActionProcessor se implementa la configuración, carga de la transformación XSL yejecución de la transformación.El constructor de la clase oficia de operación de configuración de la transformación, que previamente aque el ESB ejecute la acción XSLActionProcessor en el pipeline de acciones, configura la acción para sucorrecta operación obteniendo el nombre del archivo donde se encuentra la transformación y el mensajedel 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 laacción, en este caso, es donde se carga la transformación XSL, donde se realiza la transformación ydonde se coloca el resultado en el mensaje del ESB para que pueda ser accedido por la próxima accióndel 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 realizaefectivamente 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);
  8. 8. 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; }}
  9. 9. Transformaciones Java-Java mediante configuración SmooksEste mecanismo de transformación se utiliza cuando se tiene una instancia de un modelo Java y sequiere generar una instancia de otro modelo con la información que tiene el primer modelo. Existen dosconceptos importantes llamados “Binding” y “Wiring”. “Binding” se utiliza para generar las instanciassimples 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 unaestructura de datos completa. Ambas tareas se realizan mediante reglas de configuración de Smooksbasadas en XML. [7.1]Una característica interesante es que para seleccionar los valores de los atributos del modelo origen seutilizan expresiones XPath sobre un XML. Dicho XML es generado internamente por Smooks, a partir delmodelo de entrada, mediante XStream. XStream es una librería que permite serializar cualquier instanciade modelo de datos a XML y volver del XML a la instancia. Entonces es necesario saber que forma tieneel 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 elmodelo origen no existen restricciones particulares, ya que en realidad la transformación se realizatomando 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 siguientesreglas: • 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á comoestablecer 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 dereferencia de HL7 (RIM), se debe utilizar alguna implementación de RIM. JavaSIG es una librería paragenerar y consumir mensajes HL7, pero su implementación no sigue las reglas impuestas por estemecanismo de transformación, por lo que no es posible utilizar dicha implementación. Se hizo una pruebade esto, constatándose la imposibilidad de generar una instancia del RIM mediante este mecanismo detransformación.Para resolver este problema se plantearon las siguientes 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 provistospor la especificación de HL7 mediante la librería JAXB. En el anexo “Prueba transformación Java-JavaBindingAndWiring” se comenta cómo se generó el modelo a partir del XSD utilizando JAXB. Uno de losproblemas que tiene este modelo de clases es que para las colecciones no tiene un método del tipo “addto collection”, que es lo que necesita Smooks para generar la colección y agregarle elementos, las clasesque 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 modificaralgunas clases que tenían atributos de tipo colección, agregando un método “addXXX” donde “XXX” es elnombre 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
  10. 10. mensaje original, este tipo de transformación no permite generar el modelo extra. Una posible solución esla de agregar la información faltante en una etapa posterior a la transformación Java-Java, por ejemploutilizando 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” quese correspondan en la estructura CDA, pero CDA necesita mucha más información, porque tiene unaestructura más compleja y muchos atributos que no existen en el modelo “custom”, esto es porque CDAagrega información para poder hacer una interpretación semántica de los documentos CDA. Por ejemploexisten muchos atributos con valores fijos que no pueden ser generados directamente con estemecanismo 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íanutilizados para generar la instancia del modelo “custom”.En conclusión, esta prueba necesita gran cantidad de código de configuración para poder hacer latransformación Java-Java mediante Binding y Wiring, y las restricciones sobre el modelo de salida lohacen poco flexible. Además la configuración es como implementar la transformación directamente enJava, lo que no solo llevaría menos código, si no sería más flexible, ya que puedo utilizar la API que meprovea 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 deimplementación de la transformación probada y los resultados obtenidos. 2. Implementar la transformación directamente en Java:Esta opción no fue probada por ser considerada la opción más simple, ya que lo único que se necesitahacer es generar una instancia del modelo destino, e ir pidiendo los valores a la instancia del modeloorigen y setear dichos valores en la instancia destino. Esta opción no tendría inconvenientes con la APIdel modelo destino, al contrario del mecanismo de Bindings y Wirings que pone estrictas reglas sobredicha API. De modo que esta es una solución más genérica que la antes mencionada y también necesitaescribir 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ónsi se tiene un modelo de datos destino con una API que no cumple con las restricciones del métodoanterior, o también si la estructura es muy compleja, cuanto más compleja es la estructura más reglasdeben ser escritas y cada regla lleva en promedio 8 líneas de código, si en una implementación Java senecesitaran menos líneas que estas, al agregar atributos o relaciones, se necesitaría mucho menoscódigo para implementar la misma transformación. 3. Modificar JavaSIGEsta puede ser la opción más costosa, ya que la API de la implementación del RIM de JavaSIG es muyparticular, no solo habría que modificar decenas de clases, si no que habría que escribir gran cantidad dereglas 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.
  11. 11. Transformación FreeMarkerEn la transformación FreeMarker, la información de la transformación se encuentra en la configuración deSmooks (definido mediante reglas FreeMarker) o también puede estar en un archivo aparte (los archivosFreeMarker tienen extensión .FTL por FreeMarker Template). El formato en el que se especifica es elmarcado 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 sehace lo mismo.FreeMarker es una herramienta de “templating” o aplicación de plantillas que sirve para recorrer unarchivo 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, loscuales lo prefieren sobre XSL.Comparándolo con XSL, FreeMarker está hecho para transformación de texto en general, mientras queXSL solo sirve para procesar texto estructurado en forma de árbol, como ser un archivo XML. Por otraparte, FreeMarker tiene constructores basados en programación imperativa, mientras que XSL losconstructores están basados en programación funcional, esto se refiere a la característica de poder definirfunciones y variables en cada una de las herramientas, los desarrolladores de FreeMarker afirman queesto 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 quesea más fácil de aprender y entender que XSL. Obviamente la principal diferencia entre ambastecnologías es que XSL es un estándar y tiene diversas implementaciones, y FreeMarker una herramientaJava.Si bien se realizó una implementación de transformación utilizando FreeMarker, sería interesante podercomparar más a fondo las opciones de transformación utilizando FreeMarker y XSL, y verificar cual es laopción más simple de utilizar, la que posee más funcionalidades que ayuden a realizar transformacionessegún las características particulares de las transformaciones que se necesiten, si es necesario escribirmás o menos reglas de transformación, y evaluar la capacidad, características y estabilidad de cadaherramienta. Lo que si se constató es que en las pruebas, para lograr el mismo resultado en latransformación XML custom a XML CDA, el trabajo que tomó hacer funcionar la transformación XSL fueaproximadamente una semana, mientras que el trabajo que tomó hacer funcionar la transformacióncompleta con FreeMarker fueron alrededor de cuatro horas. También se pudo ver que el código necesariopara especificar la transformación XSL es aproximadamente tres veces más que el código necesario paraespecificar el template FreeMarker. Obviamente esta es una comparación en solo una transformaciónparticular, por lo que no se puede derivar un resultado general y decir que estas diferencias se dan enotras transformaciones.Se realizó una implementación de una transformación de un mensaje XML “custom” a un mensaje XMLCDA, la documentación se encuentra en el documento anexo “Prueba transformación XML-XMLFreeMarker”.Transformación basada en modeloPor último, para las transformaciones basadas en modelos, es necesario definir un modelo de datosintermedio entre la entrada y la salida, al cual se pueda mapear la entrada y desde el cual se puedagenerar la salida. El único código Java a implementar es dicho modelo de datos y la transformación seespecifica en la configuración de Smooks, donde se definen las correspondencias del formato de entradaal modelo y del modelo al formato de salida. La ventaja de este esquema es la posibilidad de definirmúltiples formatos de entrada y salida pudiendo hacer combinaciones de éstos sin necesidad deespecificar todas las combinaciones entrada-salida, solo se especifican las diferentes combinacionesentrada-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ónbasada en modelos para especificar dicha transformación. Esta notación está basada en “bindings” quetoman 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.
  12. 12. Propuesta de transformación Java-Java híbridaSi bien se analizaron varias opciones de transformación entre instancias de modelos Java, hasta ahora nose 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 lainstancia del modelo origen a un XML mediante la librería XStream.Transformación Java-Java mediante FreeMarker + XStreamEsta opción utilizaría la transformación FreeMarker y la funcionalidad de convertir XML a una instancia yviceversa 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+XStreamAunque esta opción es similar a la prueba de transformación XML-XML mediante FreeMarker, los pasosde transformación no son los mismos (por lo menos no en el mismo orden). La documentación completasobre 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 elXML 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énse pueden utilizar otras herramientas como XStream, esto posibilita la realización de distintos pasos detransformación de uno o más mensajes de forma simultánea, donde se pueden realizar distintastransformaciones mediante distintos mecanismos para diferentes secciones de los mensajes que seprocesen.
  13. 13. Especificación Técnica de Implementación de HL7Si bien todos los mecanismos vistos hasta el momento son válidos para definir transformaciones entredistintos formatos de mensajes, siendo uno de los formatos HL7, la mayoría de los mismos no cumplencon la especificación técnica de implementación de HL7 (ITS por sus siglas en inglés). La ITS es la formaestándar en la que dos sistemas deberían interoperar utilizando mensajes HL7. En la misma se definenuna serie de transformaciones que son necesarias para que dos sistemas que tengan su propio modelode datos, lo puedan hacer corresponder en el RIM (modelo de referencia de HL7). Estascorrespondencias 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 noson más que instancias de RIM serializadas a XML. En la siguiente imagen se muestran los pasosdefinidos por la ITS: Figura 5: Especificación Técnica de ImplementaciónEl escenario que plantea la ITS es el de dos sistemas, con un sistema emisor y otro receptor. Cada unocon un modelo particular de datos, más algún mecanismo que ejecute transformaciones entre esos datose 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).
  14. 14. Propuesta de transformación siguiendo la HL7 ITSPor lo mencionado anteriormente resulta interesante plantear una solución de transformación que cumplacon la ITS y utilice el ESB para realizar las transformaciones necesarias. Figura 6: Arquitectura del problema de envío y transformación de mensajesEn el caso de que el Sistema A (emisor) envíe mensajes “custom” al Sistema B (receptor) el cual losrecibe en formato HL7, donde las comunicaciones se realizan mediante Web Services, en el ESB se haríael pasaje del modelo SOAP a una instancia del modelo de datos del Sistema A. Luego, dentro del ESB sehace la conversión entre ese modelo de datos y el modelo RIM, por último se genera el XML a partir de lainstancia RIM obtenida (esta transformación es automática utilizando una librería como JavaSIG). EseXML 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 instanciadel RIM (esta transformación es automática utilizando una librería como JavaSIG). Luego, con los datosdel modelo RIM se genera el modelo de datos que espera recibir el Sistema A, ese modelo es enviadodirectamente al Sistema A mediante Web Services SOAP (SOAP se encarga de hacer la serialización aXML).
  15. 15. Relevamiento de opciones para ruteo de mensajesEl 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 otrofiltro. En este esquema debe haber algún mecanismo que decida en que tubo se coloca la salida de cadafiltro, porque puede haber varios tubos distintos de donde elegir. La selección del tubo en el que secolocará la salida del filtro es llamado “ruteo” del mensaje, y se realiza mediante la verificación de unconjunto de condiciones, lo que permite tomar la decisión de cual será el tubo destino, entonces la rutadel 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 BasedRouter” podría ser de gran utilidad porque podría detectar que tipo de mensaje está recibiendo el ESB, yen base a reglas que se definan, rutear el mensaje al servicio correcto que pueda procesarlo. El CBRpuede 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 serconfigurado 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 enXPath, 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 dichoenví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.
  16. 16. Descripción del prototipo logradoPara la implementación del prototipo se eligió la transformación XML-XML mediante XSL. El prototipodefine dos canales, uno que recibe un mensaje XML “custom”, mediante un punto de entrada expuestomediante Web Services, y genera un mensaje CDA que es enviado a un sistema externo mediante WebServices, el otro recibe un mensaje CDA, mediante un punto de entrada expuesto mediante WebServices, y genera un mensaje XML “custom” que es enviado a otro sistema mediante Web Services. Elprototipo fue implementado en el proyecto “ESBWS” incluido en el directorio de código de la entrega deltaller.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. • ContentFilter: 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 unaimplementación por defecto provista por el ESB, este último caso es el de las acciones: print-before, print-after y soap-ws-client.
  17. 17. Descripción de la implementación de las accionesAcción ContentFilterEsta acción está asociada a la clase XSLActionProcessor, que es la clase en la que se implementó dichaacción. La funcionalidad básica de esta clase es cargar la transformación XSL del filesystem, obtener elmensaje XML a transformar (el cual se encuentra en el body del mensaje del ESB), y aplicarefectivamente 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 unatransformación XSL nativa sobre un mensaje XML. Esta acción es utilizada en ambos canales, al que seenvía un mensaje custom y al que se envía un mensaje HL7, como implementación de sus respectivasacciones ContentFilter. Obviamente, en cada canal debe implementar una transformación distinta, esto selleva 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 susuperclase 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 elfilesystem, de manera que se tenga que hacer una sola lectura del filesystem y en posteriores ejecucionesse 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 puedaejecutarse correctamente. Por ejemplo se extrae el nombre de la transformación XSL que hay que leer, ytrae 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);
  18. 18. Se lee el archivo de la transformación del filesystem, o desde cache si ya fue cargado, y se pone sucontenido en la variable “theXSL”. String theXSL = getStylesheet(xsl);Mediante el transformador, se ejecuta la transformación XSL sobre el input, obteniéndose el resultado enla 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óndel 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 elfilesystem, 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; }}
  19. 19. Acción testStoreEsta 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úmerosrandom, de forma de generar un archivo diferente a los demás. Luego obtiene el contenido del mensaje aalmacenar, esto lo saca del mensaje del ESB a través del payloadProxy que se utiliza para acceder a losdatos del mensaje del ESB. Por último se llama al método “appendToFile” para generar el archivo con elcontenido 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; }}
  20. 20. Acción request-ws-processorEsta 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 claseAbstractActionLifecycle [3]. Antes se vio que las acciones extendían a la claseAbstractActionPipelineProcessor, esto es porque AbstractActionPipelineProcessor a su vez extiende aAbstractActionLifecycle.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 alWeb Service. En este caso se obtiene el mensaje XML transformado, que está dentro del mensaje delESB. Luego se agrega a un mapa de parámetros mediante la clave “sendMessage.message”, esto quieredecir que se va a invocar al método “sendMessage” del Web Service, en donde existe un parámetrollamado “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-processorEsta 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 unprint 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; }}
  21. 21. 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 JBossESBhttp://www.jboss.org/jbossesb/[2] Mensaje de JBossESBhttp://www.jboss.org/jbossesb/docs/4.4.GA/javadoc/esb/org/jboss/soa/esb/message/package-summary.html[3] Clase AbstractActionLifecyclehttp://www.jboss.org/jbossesb/docs/4.4.GA/javadoc/esb/org/jboss/soa/esb/actions/AbstractActionLifecycle.html[4] Clase AbstractActionPipelineProcessorhttp://www.jboss.org/jbossesb/docs/4.4.GA/javadoc/esb/org/jboss/soa/esb/actions/AbstractActionPipelineProcessor.html[5] Message Transformation on JBossESBhttp://www.jboss.org/community/docs/DOC-11397[6] Smooks User Guidehttp://docs.codehaus.org/display/MILYN/Smooks+User+Guide[7] Smooks Exampleshttp://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 JavaBeanshttp://milyn.codehaus.org/javadoc/v1.0/smooks-cartridges/javabean/[9] Enterprise Integration Patternshttp://www.enterpriseintegrationpatterns.com/eaipatterns.html

×