lunes, 2 de diciembre de 2013

Interoperabilidad entre visual .net J2EE

La Necesidad es del Negocio

Más allá de lo estrictamente técnico, existen variadas razones que provocan la existencia de ambientes heterogéneos en las empresas. Casi una mezcla de circo y museo, se conjuga lo último y lo antiguo con distintos sistemas operativos, bases de datos y plataformas de aplicaciones.
Es que al entrar en juego factores tan diversos tales como los cambios de estrategia desde la alta dirección, la selección de nuevos proveedores de software, el outsourcing del desarrollo, los recortes presupuestarios, las fusiones entre empresas, crisis y otras cuestiones político/económicas, hacen que inevitablemente las medianas y grandes empresas tengan “un poco de cada cosa”.
Veamos algunos escenarios posibles que pueden hacer necesaria la integración entre aplicaciones J2EE y .Net:
Surge un proyecto nuevo basado en .Net que tiene que reutilizar un set de librerías Java cuya migración es inviable, ya sea por su complejidad, por la inexistencia de documentación o bien porque se dispone únicamente de binarios desarrollados por terceros (que no participan del proyecto, claro).
El mecanismo estándar definido por las políticas internas de la empresa para comunicarse con otros sistemas propietarios, SAP, AS400 o el Datawarehouse está implementado únicamente en una sola de las dos plataformas.
El directorio de una empresa toma una decisión estratégica que dicta la migración de todos los sistemas a una plataforma única. La casa matriz decide que a partir de ahora “todas las aplicaciones serán construidas en la plataforma X”.
Se decide instalar una misma aplicación para todas las empresas de un mismo grupo y surgen casos donde la plataforma dominante es distinta a la prevista. Se prueban prototipos que una vez aprobados tienen que pasar a producción interactuando con plataformas distintas a la original. Dado que .Net y J2EE tienen fortalezas y debilidades que se complementan, se decide que una solución mixta podría hacer valer las fortalezas de cada una.
Encontraremos que casi la totalidad de las soluciones que existen apuntan a resolver el problema haciendo que la lógica desarrollada en Java/J2EE sea accedida desde C#/.Net y, en muy pocos casos, en sentido contrario. Esto tiene que ver con el hecho de que Java, y en particular J2EE, dominan fuertemente el ambiente corporativo donde se establecieron bastante antes del surgimiento de Microsoft .Net.
Tengamos en cuenta que la publicación del modelo J2EE impactó fuertemente en este sector al establecer una infraestructura basada en estándares abiertos para aplicaciones distribuidas incluyendo transacciones, seguridad, componentes server-side, acceso a datos, persistencia, clustering y balanceo de carga. El bonus adicional del modelo tiene que ver con asegurarse la independencia de un proveedor específico ya que teóricamente un desarrollo particular puede instalarse en cualquier plataforma que respete el estándar (esta “compatibilidad asegurada” se vio afectada negativamente cuando los proveedores empezaron a incorporar mejoras adicionales cubriendo las falencias del estándar). En este sentido, a pesar de que .Net es una excelente opción para el desarrollo de software, como participante tardío de esta carrera todavía tiene camino por recorrer para consolidarse.

¿Cómo lograrlo?

La aproximación más sencilla que podemos implementar es una base de datos de uso compartido, donde a través de JDBC y ADO .Net nuestras aplicaciones J2EE y .Net, respectivamente, puedan acceder para escribir y leer información. Esta opción tiene mucho sentido si consideramos componentes que en ambos casos se encuentren en la capa de negocios. Para hacer interoperar componentes que se encuentren en diferentes capas, el mecanismo a emplear dependerá del punto de interconexión que estemos considerando

Si el punto de interconexión se extiende a través de Internet o una intranet, la alternativa natural es la utilización deWeb Services. Tenemos herramientas para generarlos y consumirlos en ambas plataformas y cuentan con la ventaja de ser estándares abiertos que nos permitirán conservar la compatibilidad con futuras aplicaciones. Estaremos limitados, eso sí, por la velocidad del enlace y el costo de emplear XML para transmitir los mensajes.
Si no disponemos de un enlace permanente o el que tenemos es poco confiable, tendremos que considerar servicios de mensajería asíncrona que usualmente emplearemos para la interacción entre las capas de negocios y de datos. Lo que se hace es implementar colas de mensajes en cada una de las plataformas e interconectarlas a través de un bridge (cada aplicación produce y consume mensajes a través de la API habitual). Por otra parte, si podemos sacrificar el soporte de transacciones y callbacks, es posible generar un wrapper que permita el acceso a través de un Web Service.
Cuando lo que estamos buscando es acceder a nivel de clases y métodos, tendremos que optar por un runtime bridge. Estos componentes se agregan en cada plataforma para administrar la interacción y comunicación entre objetos de diferente origen.
Si nos animamos a una solución de más bajo nivel podemos llegar a eliminar el overhead de la comunicación remota aprovechando los métodos de interacción con código nativo que tiene cada plataforma.
Por último, si estamos encarando una migración masiva a gran escala, lo que seguramente resulte más conveniente sea emplear herramientas de conversión de plataforma, las cuales se hacen cargo del mapeo de tipos y de proporcionar las APIs correspondientes.

Mecanismos Orientados a Servicios

¿Qué es lo primero que nos viene a la mente cuando nos hablan de integración de aplicaciones? Algo que la maquinaria publicitaria viene machacando desde hace algunos años: ¡Los Web Services, por supuesto! Tenemos la ventaja de que se trata de un mecanismo estándar y que existen en ambas plataformas (aunque lo de “estándar” es algo bastante relativo pues todavía falta un poco para que los proveedores se pongan de acuerdo al respecto).
Repasemos brevemente el funcionamiento de un Web Service como mecanismo de integración  que se muestra a continuación:




REST vs SOAP

En el momento que decidimos desarrollar nuestros servicios web, tenemos que tomar la decisión de que arquitectura será la más apropiada para nuestro sistema y el uso que vayamos a darle. En esta entrada os voy a presentar las características de SOAP y REST, dos técnicas de arquitectura software orientadas a Webservices.
SOAP (siglas de  Simple Object Access Protocol)
Es un protocolo estándar que define cómo dos objetos en diferentes procesos pueden comunicarse por medio de intercambios de datos XML, el punto identificativo de SOAP es que las operaciones son definidas como puertos WSDL (Web Services Description Language). Es por esto que será aconsejable utilizar este protocolo en entornos donde se establecerá un contrato formal y donde se describirán todas las funciones de la interfaz así como el tipo de datos utilizados tanto de entrada como de salida. el lenguaje WSDL nos permitirá definir claramente cualquier detalle de las funciones de nuestro WS.
REST (Representational State Transfer)
Es  un estilo de arquitectura de software para sistemas distribuidos tales como la web, a diferencia de SOAP, se centra en el uso de los estándares HTTP y XML para la transmisión de datos sin la necesidad de contar con una capa adicional. Las operaciones( o funciones)  se solicitarán mediante GET, POST, PUT y DELETE, por lo que no requiere de implementaciones especiales para consumir estos servicios. Además se podrá utilizar JSON en vez de XML como contenedor de la información, por lo que será aconsejable utilizar este protocolo cuando busquemos mejorar el rendimiento, o cuando disponemos de escasos recursos, como sería el caso de los dispositivos móviles.

Antes de elegir una de las opciones, deberemos valorar cuál de ellas se adapta mejor al entorno de nuestra aplicación.


Ejecutar servicio WCF con Jquery

Les mostrare un ejemplo práctico de como ejecutar servicio WCF desde Jquery  ,el cual nos permite hacer llamados asíncronos al servidor desde un webform en asp.net .

Aspx :

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="AJAXJQuery.aspx.cs" Inherits="TestJquery1.AJAXJQuery" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<head runat="server">
    <title>AJAX JQuery</title>
    <script type="text/javascript" src="Scripts/jquery-1.6.1.min.js"></script>
    <script type="text/javascript" language="javascript">
        $(document).ready(function () {
            /***************************************
            Llamado AJAX Service Method WebForm
            ****************************************/
            $("#BtGetData").click(function () {
                $("#DivPersons").html("");
                $.ajax({
                    //Permite definir el tipo de llamado GET o POST siendo POST el mejor
                    type: "POST",
                    //Define la ruta al WebMethod existente en el code-behind
                    url: "AJAXJQuery.aspx/GetPersons",
                    //Permite pasar parametro en formato JSON (string) Hay Utilities como JSON2,
                    //que permite serializar falcilmente a este fomato
                    data: {},
                    //Define el content type que viajara en los bloques HTTP
                    contentType: "application/json; chartset:utf-8",
                    //Define el tipo de datos que se va a manejar xml, json
                    dataType: "json",
                    //Esta sección permite definir una función anónima, la cual se encargará de
                    //recibir los resultados provenientes del servidor,
                    //en este caso serializados en formato JSON
                    success:
                        function (result) {
                            if (result.d) {
                                var table = $("<table></table>");
                                table.append($("<tr style='background:#EBEBEB;'></tr>").append("<td>Código</td><td>Nombre</td>"))
                                $(result.d).each(function () {
                                    table.append($("<tr></tr>").append("<td>" + this.Id + "</td><td>" + this.Name + "</td>"));
                                });
                                $("#DivPersons").append(table);
                            }
                        },
                    //Esta función permite definir una función anónima que
                    //se encargara de gestionar los posibles erroes.
                    error:
                    function (XmlHttpError, error, description) {
                        $("#DivPersons").html(XmlHttpError.responseText);
                    },
                    async: true
                });
            });
 
/*****************************************************
            Llamado AJAX Servicio ASMX
            Es importante descomentar :  [System.Web.Script.Services.ScriptService]
            En la clase del servicio, pues esto habilita la
            funcionalidad  AJAX.
            ******************************************************/
            $("#TextBoxDesc").blur(function () {
                $.ajax({
                    //Permite definir el tipo de llamado GET o POST siendo POST el mejor
                    type: "POST",
                    //Define la ruta al WebMethod existente en el Servicio ASMX
                    url: "Service/WebServicePerson.asmx/HelloWorld",
                    //Permite pasar parametro en formato JSON (string) Hay Utilities como JSON2,
                    //que permite serializar falcilmente a este fomato
                    data: '{ "name":"' + $(this).val() + '" }',
                    //Define el content type que viajara en los bloques HTTP
                    contentType: "application/json; chartset:utf-8",
                    //Define el tipo de datos que se va a manejar xml, json
                    dataType: "json",
                    //Esta sección permite definir una función anónima, la cual se encargará de
                    //recibir los resultados provenientes del servidor, en este caso
                    //serializados en formato JSON
                    success:
                        function (result) {
                            if (result.d) {
                                alert(result.d);
                            }
                        },
                    //Esta función permite definir una función anónima que se
                    //encargara de gestionar los posibles erroes.
                    error:
                    function (XmlHttpError, error, description) {
                        $("#DivPersons").html(XmlHttpError.responseText);
                    },
                    async: true
                });
            });
 
            /*****************************************************
            Llamado AJAX Servicio WCF
            Ver WebConfig para la definición de los parámetros
            de configuración.
            ******************************************************/
            $("#TxtDetailsPerson").blur(function () {
                $("#DivPersonsByFilter").html("");
                $.ajax({
                    //Permite definir el tipo de llamado GET o POST siendo POST el mejor
                    type: "POST",
                    //Define la ruta al WebMethod existente en el code-behind
                    url: "Service/ServicePerson.svc/GetPersonByFilter",
                    //Permite pasar parametro en formato JSON (string)
                    data: '{ "filter":"' + $(this).val() + '"}',
                    //Define el content type que viajara en los bloques HTTP
                    contentType: "application/json; chartset=utf-8",
                    //Define el tipo de datos que se va a manejar xml, json
                    dataType: "json",
                    //Esta sección permite definir una función anónima,
                    //la cual se encargará de recibir los resultados provenientes
                    //del servidor, en este caso serializados en formato JSON
                    success:
                        function (result) {
                            if (result) {
                                var table = $("<table></table>");
                                table.append($("<tr style='background:#EBEBEB;'></tr>").append("<td>Código</td><td>Nombre</td>"))
                                $(result.GetPersonByFilterResult).each(function () {
                                    table.append($("<tr></tr>").append("<td>" + this.Id + "</td><td>" + this.Name + "</td>"));
                                });
                                $("#DivPersonsByFilter").append(table);
                            }
                        },
                    //Esta función permite definir una función anónima que
                    //se encargara de gestionar los posibles erroes.
                    error:
                    function (XmlHttpError, error, description) {
                        $("#DivPersonsByFilter").html(XmlHttpError.responseText);
                    },
                    async: true
                });
            });
        });
    </script>
</head>

 
body>
    <form id="form1" runat="server">
    Consultar todas las persona:<br />
    <asp:Button ID="BtGetData" runat="server" OnClientClick="return false;" Text="WebMethod JQuery" />
    <br />
 
    <br />
    <div id="DivPersons">
    </div>
    <br />
    ASMX JQuery<br />
    WebMethod<br />
    Cuál es tu nombre?:<br />
    <asp:TextBox ID="TextBoxDesc" runat="server" ></asp:TextBox>
    <div id="DivASMXHelloWorld">
    </div>
    <br />
    <br />
    WCF - JQuery <br />
    Windows Comunication Foundation WebInvoke
    <br />
    Consultar personas por filtro de búsqueda:<br />
    <asp:TextBox ID="TxtDetailsPerson" runat="server" ></asp:TextBox>
    <div id="DivPersonsByFilter">
    </div>
 
    </form>
</body>
</html>

Modelo de datos usado Class Person
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
 
namespace TestJquery1
{
    /// <summary>
    /// Clase que permite simular el acceso a datos
    /// </summary>
    public class Person
    {
        public int Id { get; set; }
        public string Name { get; set; }
        /// <summary>
        /// Inicializa la simulación de un contexto de datos
        /// </summary>
        /// <returns></returns>
        public List<Person> GetPersons()
        {
            List<Person> persons = new List<Person>();
            persons.Add(new Person { Id = 1, Name = "Pedro" });
            persons.Add(new Person { Id = 2, Name = "Andrés" });
            persons.Add(new Person { Id = 3, Name = "Juan" });
            persons.Add(new Person { Id = 4, Name = "Julián" });
            persons.Add(new Person { Id = 5, Name = "Felipe" });
            return persons;
        }
        /// <summary>
        /// Permite filtrar personas y realizar una búsqueda
        /// tanto por Cod si el filtro es numerico o por
        /// coincidencia con el nombre si el filtro es texto
        /// </summary>
        /// <param name="filter"></param>
        /// <returns></returns>
        public List<Person> PersonsByFilter(string filter)
        {
            double id = -1;
            string name = string.Empty;
            var persons = GetPersons();
            if (Double.TryParse(filter, out id))
            {
                var result = from p in persons
                             where p.Id == id
                             select p;
                return result.ToList();
            }
            else {
                var result = from p in persons
                             where p.Name.Contains(filter)
                             select p;
                return result.ToList();
            }
        }
    }
}
(Code Behind) Static WebMethod
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.Services;
 
namespace TestJquery1
{
    public partial class AJAXJQuery : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
 
        }
        /// <summary>
        /// Método de servicio
        /// </summary>
        /// <returns></returns>
        [WebMethod]
        public static List<Person> GetPersons()
        {
            Person person = new Person();
            var list = person.GetPersons();
            return list;
        }
    }
 
}
ASMX Service Code-Behind
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
 
namespace TestJquery1.Service
{
    /// <summary>
    /// Summary description for WebServicePerson
    /// </summary>
    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [System.ComponentModel.ToolboxItem(false)]
    // To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
    [System.Web.Script.Services.ScriptService]//Es importante descomentar esta linea si es que está comentada
    public class WebServicePerson : System.Web.Services.WebService
    {
 
        [WebMethod]
        public string HelloWorld(string name)
        {
            return String.Format("Hello World {0}, te saludo desde un ASMX Service", name);
        }
    }
}
WCF Code Behind
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.ServiceModel.Web;
 
namespace TestJquery1
{
    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IServicePerson" in both code and config file together.
    [ServiceContract]
    public interface IServicePerson
    {
        [OperationContract]
        [WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.Wrapped, ResponseFormat = WebMessageFormat.Json)]
        List<Person> GetPersonByFilter(string filter);
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
 
namespace TestJquery1
{
    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "ServicePerson" in code, svc and config file together.
    public class ServicePerson : IServicePerson
    {
        public List<Person> GetPersonByFilter(string filter)
        {
            Person persons = new Person();
            var personsList = persons.PersonsByFilter(filter);
            return personsList;
        }
    }
}
 
Es necesario configurar los servicios de WCF de forma tal que sean accesibles desde el Browser, como aclaración es necesario configurar los endpoints y bindings correspondiente al servicio, así cómo establecer la propiedad <serviceMetadata httpGetEnabled=”True/> lo cual habilitara la definición del documento WSDL el cual contendrá la Metadata del Servicio
 
<system.serviceModel>
   <behaviors>
     <serviceBehaviors>
       <behavior name="ServicePersonBeheavor">
         <serviceMetadata httpGetEnabled="True"/>
         <serviceDebug includeExceptionDetailInFaults="True"/>
       </behavior>
     </serviceBehaviors>
     <endpointBehaviors>
       <behavior name="ServicePersonBeheavor">
         <webHttp/>
       </behavior>
     </endpointBehaviors>
   </behaviors>
   <services>
     <service behaviorConfiguration="ServicePersonBeheavor" name="TestJquery1.ServicePerson">
       <endpoint address="" binding="webHttpBinding" contract="TestJquery1.IServicePerson" behaviorConfiguration="ServicePersonBeheavor"></endpoint>
     </service>
   </services>
 </system.serviceModel>
 

Arquitectura SOA

La solución de Informática para las arquitecturas orientadas a servicios (SOA) permite a su organización de IT crear una capa de servicios de datos basados en metadatos reutilizables en una SOA, un elemento crucial que a menudo se pasa por alto. Esta solución responde a todos los requisitos de datos de una SOA, incluidos el acceso, el volumen, la latencia, la transformación, la calidad y la entrega de datos. Esta solución permite a su organización de IT ofrecer datos oportunos y fiables (en el momento lugar y forma que su empresa los necesite) para mejorar la agilidad.
  • Mejorar la toma de decisiones proporcionando un acceso rápido y directo a datos coherentes y precisos que el negocio necesita y en los que confía
  • Aumentar la satisfacción y fidelidad de los clientes ofreciendo una perspectiva unificada y completa del cliente que incluye datos transaccionales importantes
  • Acelerar el tiempo de comercialización y reducir los costes de desarrollo proporcionando una capa de servicios de datos comunes basados en metadatos y en modelos y transformaciones complejas integradas que aumentan la productividad y la reutilización
  • Mejorar la adaptabilidad y la agilidad de IT en respuesta a las cambiantes necesidades del negocio con una capa de abstracción de datos flexible y proporcionando un acceso en tiempo real a datos con cualquier latencia
  • Reutilizar activos y recursos existentes aprovechando los conjuntos de competencias y servicios existentes de Informática en los proyectos, así como el mayor ecosistema de socios y desarrolladores de integración de datos independientes  
  • Reducir la complejidad de la integración sustituyendo la integración de datos y aplicaciones punto a punto tradicionales con servicios de datos comunes, reutilizables y flexibles
  • Minimizar los riesgos de tiempo de inactividad o interrupciones de servicio proporcionando una base de integración de datos y servicios escalables, seguros, fiables y de alto rendimiento
  • Minimizar el riesgo de que los proyectos se alarguen con un marco de integración de datos ágil y una certificación de calidad de datos inicial
  • Respaldar el cumplimiento de las normativas y reducir los riesgos implicando desde el principio a los usuarios de negocio en la creación y validación de reglas y permitiendo una gestión de cambios eficaz a través del análisis gráfico de impacto y el linaje de datos.