Interoperabilidad .NET – J2EE: Siempre Fuimos Compañeros

 Algunos  deben suponer que de rivales que son no se dicen ni "buen día". Pero la realidad ha sido siempre otra. Y cada día lo es más. Este artículo enumera, como siempre con pros y contras, las diversas estrategias disponibles para hacer hablar .NET y J2EE (y J2EE y .NET). También voy a destacar algunas de las mejores prácticas en integración de aplicaciones

Las estrategias que revisaremos son principalmente cuatro

  • Apoderados (Proxies) RMI – .NET Remoting
  • Colas de Mensajes
  • Intermediarios (Brokers) de Integración
  • Servicios Web

Apoderados (Proxies) RMI – .NET Remoting

Son soluciones propietarias que traducen, en ambos sentidos, las serializaciones de J2EE y .NET. Lo que logran con eso es que, dadas lado a lado dos aplicaciones (dos procesos en definitiva), una corriendo sobre la Máquina Virtual de Java (Java Virtual Machine o JVM), la otra sobre el Entorno Común de Ejecución de Lenguajes (Common Language Runtime o CLR), ambos procesos interoperen mediante llamadas a procedimientos remotos (remote procedure calls o RPCs)

Para ponerlo más claro, en Java, RPC está implementado mediante una API llamada Remote Method Invocation (RMI o Invocación a Método Remoto); en tanto que, analogamente, en .NET la implementación se llama .NET Remoting (.NET Remoto)

Ambas implementaciones operan de manera similar: el objeto que quiere invocar un método de un objeto remoto, lo hace a través de un apoderado (proxy) que expone la misma firma que el método remoto, en tanto que internamente serializa la invocación (incluyendo los argumentos recibidos) y los envía hacia el otro proceso mediante transporte TCP/IP. Este proceso que culmina en la serialización de la invocación se conoce como marshalling (formación)

Una vez al otro lado (donde necesariamente el proceso receptor tiene un puerto habilitado para recibir estos mensajes de invocaciones serializadas), se procede a la deserialización del flujo (stream) recibido, para reconstituir la invocación requerida, ya en el proceso remoto. A este proceso, analogamente, se lo llama unmarshalling (desmontado)

Tanto la JVM como el CLR tienen formatos propios de serialización de invocaciones. No obstante en el caso de .NET, éste se puede cambiar. Esto es aprovechado eficazmente por piezas de terceros, que se ubican en "la puerta" de .NET para filtrar este tipo de invocaciones salientes, mapeándolas adecuadamente a mensajes RMI serializados. Del mismo modo, reciben invocaciones desde JVM en formato RMI y las transforman en pedidos .NET Remoting

Este tipo de soluciones suele presentar alto rendimiento por dos razones. La primera, el protocolo de transporte es el TCP, menos cargado que HTTP. La otra razón es que la serialización es binaria (empaquetada). Comparado con la serialización orientada a carácter de los servicios web, se podría decir que c’est bon marché (barato)

En cambio, este enfoque presenta algunos inconvenientes: al permitir compartir objetos, sea por las interfases remotas invocadas como por los argumentos enviados, en la práctica esto genera acoplamiento entre las aplicaciones. Este problema, en verdad, ya estaba presente tanto en RMI como en .NET Remoting

Otras desventajas están relacionadas con el manejo de la seguridad o la coordinación de transacciones distribuidas. Las implementaciones de estas piezas pueden no contemplarlas

Por los motivos mencionados, es poco probable que este tipo de estrategia vaya a considerarse para integración entre empresas (Business to business o B2B). Lo más probable, de ser utilizada, es que sea para integración de aplicaciones dentro de la empresa (Enterprise Application Integration o EAI)

Voy a mencionar dos ejemplos, pero seguramente hay varios más. JNBridge y J-Integra. Algunos me habrán visto hacer demos con esta última. Sorprendentemente una aplicación puede generar java.lang.Exception que al otro lado se recibe System.ApplicationException, o viceversa. También, es sorprendente el manejo transparente del callback: al reconstituir en el proceso remoto un objeto recibido como argumento, una invocación a este objeto que llegó del más allá automáticamente se convierte en una llamada remota hacia la plataforma original. Transparencia tan sorprendente como peligrosa, si se pone en manos que desconsideren el tráfico de red que podría generarse

Colas de Mensajes

La siguiente estrategia es un mecanismo de alto rendimiento que permite conectar aplicaciones en forma asíncrona. Las colas de mensaje trabajan en una modalidad orientada a eventos. Un proceso que pone un mensaje en una cola se dice que "publica un evento". Previamente otros procesos han de haberse suscripto a ese tipo de eventos en dicha cola. De esta manera, cada vez que un evento es publicado, la cola les pasa el mensaje a los suscriptores. Estos a su vez, al ser notificados del evento, pueden tomar acciones propias (es la idea) que cambien el estado de los recursos propios y/o compartidos. Quien publicó el mensaje en la cola no se entera de los resultados de esas reacciones excepto que consulte explicitamente. De ahí que sea asíncrono el proceso global: quien publica el mensaje no queda bloqueado a la espera de los resultados

Las colas más tradicionales, IBM WebSphere MQ o MSMQ por nombrar dos, contienen mecanismos para poder participar en transacciones. Garantizan también, que un mensaje queda en la cola hasta tanto no haya sido consumido en forma exitosa. Eso se conoce como mensajería confiable: el mensaje no se va a perder sin ser consumido

Para acceder a una cola en .NET existe una API llamada System.Messaging. Primariamente está pensada para hablar con MSMQ pero realmente la API es extensible para soportar otras colas. De hecho IBM implementó esas extensiones para su WebSphere MQ, algo que también es lograble con TIBCO Rendezvous. En Java tenemos disponible una especificación llamada JMS (Java Messaging Service) para que los vendors de Middlewares Orientados a Mensajes (Message-Oriented Middlewares o MOM) implementen su respectiva API, de modo de no acoplar el producto a la aplicación

Intermediarios (Brokers) de Integración

Estas piezas se encargan de conectar a ciertos consumidores de servicios con los proveedores de los mismos. Como no todas ambas partes podrían no tener implementados los mismos protocolos de comunicación, el Intermediario se encarga de hacer de intérprete hacia ambas márgenes. Esto ha dado pie a un nuevo rango de productos de integración, conocido como Bus de Servicios Empresariales (Enterprise Service Bus o ESB), aunque originalmente el concepto de ESB era más bien un patrón de integración. Las sucesivas implementaciones de este patrón han dado forma al "producto". La gran virtud de estas piezas es que acercan fronteras de aplicaciones con mínimo impacto en las mismas, ya que estas no deben implementar nuevos protocolos de transporte o comunicación si el ESB "entiende" múltiples dialectos

Como características salientes los ESB provéen ruteo y direccionamiento, transformaciones de protocolos de comunicación, entre otras. Los principales vendors están incluyendo numerosas capacidades adicionales, especialmente para definir procesos de negocio en base a varios servicios. Cada uno de estos servicios desconoce de la presencia o de la mera existencia de los otros. De este modo el Intermediario juega un rol de "director de orquesta"; orquesta en la que cada servicio sería un músico diferente. Es así como a los Intermediarios se los llama comunmente orquestadores de procesos de negocio. Desde luego que esta separación entre la lógica de orquestación y las capas de las aplicaciones (los servicios) tornan al desarrollo más simple, mantenible, en tanto que protegen inversiones ya realizadas

Normalmente se incluye un motor de reglas de negocio y mecanismos sofisticados de monitoreo, tanto a nivel de infraestructura (procesos, servidores) como a nivel de negocio (transacciones de negocio en el bus). Asimismo, consideran transacciones largas, compensaciones, correlación de mensajes y flujos de control

En el contexto de las Arquitecturas Orientadas a Servicio (Service-Oriented Architecture o SOA), hay productos que permiten acelerar el montado de toda o parte de la infraestructura. Existen propuestas muy sofisticadas como el ya conocido Biztalk Server, cuya versión 2006 está próxima a aparecer. Otros candidatos a considerar incluyen al IBM WebSphere Message Broker o al BEA Aqualogic

En concreto, pues, tanto una aplicación Java como una aplicación .NET deberán ser capaces de al menos alcanzar al Intermediario para que sea éste quien establezca la comunicación con la otra punta. Como decíamos algunos párrafos más arriba, ambas márgenes es posible que no tengan practicamente impacto o necesidad de implementar protocolos adicionales que los que ya tenían, dado que el Intermediario tiene, entre sus tantas funciones, hacer de intérprete

Para quien quiera conocer más sobre estos conceptos, existen un par de artículos muy recomendables de IBM: An introduction to the IBM Enterprise Service Bus y especialmente Understand Enterprise Service Bus scenarios and solutions in SOA, Part 1

Por el lado de Microsoft, como comentaba, el intermediario es Biztalk Server. Un excelente artículo para conocer integralmente cómo Biztalk Server integra ambientes heterogéneos y todo lo que en base a ello se puede hacer, es Understanding Biztalk Server 2004. La potencia de Biztalk Server supera -como producto- largamente lo esperable por un ESB. Como solución a todo a nada suscita ciertas polémicas frente a propuestas de la competencia más granulares. Esto motivó a Microsoft a publicar su posición respecto de ESB como producto: Microsoft on the Enterprise Service Bus (ESB)

Servicios Web

Éste es un estándar de comunicación relativamente reciente, que logró en pocos años lo que no lograron otras soluciones integrales como CORBA (Common Object Request Broker Architecture, o Arquitectura Común de Intermediarios de Requerimientos para Objetos), muy complejas de por sí; u otras más simples pero no multiplataforma como Microsoft COM (y sus variantes COM+ y DCOM), o los Enterprise JavaBeans (EJB), disponibles allí donde Java estuviera disponible (… y siempre que las puntas a conversar hablen Java, es decir, afuera COBOL CICS, afuera Windows DNA, afuera .NET, etc)

Los servicios Web maduran gracias al esfuerzo de grandes vendors como Microsoft, IBM, BEA, Oracle, Sun y otros, así como también empresas de distintos rubros, no tecnológicas, como Ford, United Airlines, KPMG, Daimler-Chrysler, etc. Todas conforman un comité conocido como Web Services Interoperability, o WS-I

En un primer período, este comité se abocó a construir los fundamentos de un estándar con la premisa de lograr una solución débilmente acoplada, fácil de implementar. Así nació el Perfil Básico (WS-I Basic Profile), apoyado a su vez en estándares como WSDL (para describir contratos entre el consumidor y el proveedor de un servicio), UDDI (para descubrir servicios) y SOAP (para invocar servicios)

El Gartner Group año tras año destacó a Microsoft como líder tanto en visión como en habilidad de ejecución respecto a implementación de servicios Web y herramientas "ad hoc". Recientemente incorporó a IBM en el podio, en un virtual empate técnico con el gigante del software (ver más información al respecto acá)

Hoy los estándares de servicios Web siguen madurando bajo un esquema conocido como WS-*, que persigue como propósito proveer seguridad a las conversaciones dentro de esta tecnología, mensajería confiable y coordinación transaccional. De las tres directrices, la de seguridad está bastante avanzada tanto en definición como en implementación (de hecho hay talleres de interoperabilidad entre Microsoft y BEA, Microsoft e IBM, etc). Un poco más atrás viene la mensajería confiable y más lejos aún el soporte a transacciones coordinadas

La seguridad se sostiene con tres estándares. WS-Security otorga elementos para firmar mensajes o encriptar (parte o todo). WS-SecureConversation contempla la posibilidad de acordar claves específicas entre las partes para conversaciones más largas. Finalmente, WS-Trust está para delegar relaciones de confianza a servicios distribuidos (mediante la creación, intercambio y validaciónd de tokens de seguridad)

No voy a extenderme comentando todos los estándares de WS-*, pero sí destacar que Windows Communication Foundation (WCF o Indigo) es la próxima tecnología de Servicios Web a liberarse con Windows Vista (mediados de 2006), promoviendo un modelo para construir servicios web en base a composición de canales y conductas. Por canales, podremos optar por HTTP, TCP, UDP, colas e incluso canales personalizados (no necesariamente uno a elegir de todos sino que podremos habilitar canales simultáneos). También, agregar seguridad o mensajería confiable, de ida y vuelta, etc, al canal, con la sola declaración de atributos (sin líneas de código). Respecto de las conductas, podremos combinar políticas de activación (para controlar las instancias), la concurrencia, las demarcaciones transaccionales, etc. WCF va a ser la forma natural de comunicarse con y desde Biztalk Server, SQL Server o cualquier producto de la familia Microsoft. Sun Microsystems no está mirando todo esto desde afuera: bajo el nombre de Project Tango, sus ingenieros trabajan lado a lado con ingenieros de Microsoft para garantizar que la implementación de Servicios Web del Java hecho por Sun converse 100% con WCF . Un post sobre este proyecto está disponible haciendo click aquí

 

 

Mejores prácticas de Interoperabilidad

Las aplicaciones modernas, independientemente de cuál de las dos plataformas se trate, suelen tener un descomposición en capas que a grosso modo representan lógica de presentación, lógica de negocio y lógica de acceso a datos

Los escenarios de integración suelen involucrar aplicaciones que ya están hechas o semi hechas. Si se trata de integración EAI (dentro de la organización) es porque tenían ya cierta plataforma en explotación y están ahora agregando la otra (sea porque pasa a ser un nuevo estándar, sea porque algún proveedor de soluciones les entregó una aplicación en la plataforma nueva). Con esta realidad en vista, es posible que la necesidad de integración se pueda dar entre capas cualesquiera de ambas plataformas. El siguiente esquema resume, para cada capa arquitectónica, la API más representativa de la misma

Quiero recalcar el hecho de haber optado por señalar en cada capa la API más representativa, sin perjuicio de que deba aplicarse en forma prescriptiva (ejemplo, en mis tiempos de J2EE trataba de evitar los EJB en la capa de negocio más allá de que los blueprints oficiales de J2EE los "recomendasen" como componentes escalables, de alto rendimiento, etc. Digo lo mismo de los componentes servidos en .NET (serviced components): no usarlos by the book sino cuando los requerimientos de la aplicación (integridad transaccional, seguridad en el acceso a propiedades, etc) los justifiquen. La siguiente tabla resume, para cada capa arquitectónica, cuál o cuáles de las API’s estándares (o quizás piezas de terceros) permite llegar al otro lado

 

Swing

EJB

JSP/Servlet

BD

Mensajería Asíncrona

Windows Forms

Swing form interop

WS, RMI/IIOP

HTML render, WS

ADO.NET

System.
Messaging

Componente .NET

JNI, WS, RMI/IIOP

 

ASP.NET

WS, HTML render

Estado de sesión, portlet

BD

JDBC

Específico de las BDs

Mensajería Asíncrona

JMS, JNI

HIS, Falcon MQ, Biztalk

No obstante, más allá de las API’s (que ganan mayor relevancia a la hora de implementar la integración) existen recomendaciones de arquitectura para dejar lista una aplicación de n-capas de manera que al introducir lógica de interoperabilidad, no se acople aquel código involucrado con la tecnología de interoperabilidad elegida con el código existente en la aplicación. La idea entonces es modificar las n capas para pasar a un esquema orientado a servicios

Así, para un "lado cliente" o "consumidor" de servicios que la otra plataforma le brinde, no estará de más que defina sus propios adaptadores: que hacia afuera exhiba una interfaz conocida al caso de uso, en tanto que por dentro esconda los detalles de bajo nivel de la estrategia involucrada en la comunicación con el proveedor (cola de mensajería, servicio web, apoderado, intermediario, etc). Del mismo modo, así implementemos uno o más adaptadores, no es malo que sea en forma declarativa (vía archivo de configuración) y no programática, que se defina con cuál de los adaptadores implementados vamos a interoperar. Esto nos va a librar de recompilar la aplicación si elegimos cambiar de estrategia. Por consiguiente, no es malo que la instanciación del adaptador configurado se haga a través de una fábrica, que en base a la configuración sepa cuál adaptador instanciar

El esquema arquitectónico de arriba muestra el modelo terminado de la transición desde 3 capas hacia la incorporación del paradigma orientado a servicios, que desacople las aplicaciones de las estrategias de interoperabilidad

Respecto del intercambio de datos, es muy probable que las clases de objetos de una aplicación no concuerde estrictamente con la otra. En efecto, las dos pueden tener entidades similares como Cliente, Pago, Factura, pero las propiedades pueden no estar implementadas de la misma forma más allá de la equivalencia semántica. Intentar forzar a ambas aplicaciones a que compartan estrictamente el modelo de datos conlleva a acoplamientos innecesarios. La solución entonces es que ambas aplicaciones intercambien entre sí unas entidades conocidas como Objetos de Transferencia de Datos (Data Transfer Objects o DTOs). Los mismos se suelen limitar a contener estrictamente la información que necesita ser traficada en la interacción. Como contraparte hay que admitir que tanto los adaptadores del lado cliente como los proveedores del servicio deberán encargarse de mapear propiedades del DTO en propiedades del modelo de objetos propio. En honor a la productividad, existen piezas que graficamente permiten hacer ese mapeo entre clases, e incluso generar el código del DTO en las dos plataformas

También es aconsejable contar con mecanismos de detección de mensajes duplicados en aquellos contextos en que las retransmisiones por timeout puedan llegar a ser posibles. Para una explicación más detallada de este problema y su solución recomiendo visitar este link y buscar la sección Idempotent Receiver (Receptor Idempotente)

Hablemos un poquito de escalabilidad y rendimiento. El hacer conversar dos aplicaciones tiene un primer beneficio que es el reuso y maximización de ROI para componentes existentes (probados, maduros, conocidos). No obstante, el proceso de cambio de plataforma es bastante más complejo de ejecutar que las invocaciones in-process (invocaciones a rutinas dentro de la misma plataforma): recordemos lo que contábamos al explicar la estrategia del Apoderado (Proxy) en que los parámetros se serializan, se transmiten a través de una red que hasta hoy es más lenta (y en cierto orden de magnitud) respecto del bus de datos que mueve bits entre la memoria y la CPU, y se deserializan al otro lado; para entonces ejecutar el servicio y a posteriori repetir con la respuesta a devolver la serialización, transmisión y deserialización ya de vuelta en el cliente

Cómo mitigar este efecto? La primera recomendación es evitar las interfaces charlatanas (chatty), de granularidad fina (fine-grained) que conllevan, por cada transacción de negocio, a demasiadas idas y vueltas (round-trips) entre consumidor de servicio y proveedor. A cambio, entonces, exponer interfaces de granularidad más gruesa (coarse-grained). Para esto los DTOs serán aliados ideales

Eventualmente, si bien genera cierta redundancia, replicar la validación de los datos de entrada (que no deben faltar del lado servidor) en el lado cliente. No dije "mover" la validación sino "copiar" la validación siempre que sea útil evitar el viaje de datos que no van a ser aceptados. La contracara de esto es que genera acoplamiento: si una validación replicada cambiase, ese cambio impacta en ambas márgenes

Siguiendo con recomendaciones de escalabilidad, considerar seriamente aquellos casos en que una respuesta inmediata del servicio no sea un imperativo, para cambiar la semántica de la comunicación a un esquema asincrónico. Esto va a mejorar la sensación de respuesta de la aplicación ante el usuario. Por supuesto, si como resultado de la invocación remota el usuario requiere una respuesta, la semántica asíncrona no será aplicable

Una última recomendación de rendimiento consiste en considerar mecanismos de caching (antememoria) en el lado consumidor del servicio, a fin de evitarnos viajes para consultas de datos con baja frecuencia de variabilidad

Respecto de la integridad de datos, especialmente al lado servidor, una premisa es no confiar en que los procesos consumidores del servicio detectarán la anomalía y la corregirán. Por eso, se debe evitar dejar estados inconsistentes. Si no se cuenta con un coordinador transaccional distribuido habrá que considerar compensaciones para corregir, en última instancia, operaciones inconclusas

Para culminar el artículo (y las recomendaciones), invito a leer un paper de John Evdemon sobre buenas y malas prácticas en diseño de servicios

Esta entrada fue publicada en Software Architecture. Guarda el enlace permanente.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s