Introducción a los Servicios Web en Java

Hoy en día, muchas aplicaciones implementan lógica de negocios de la 'tercera capa' como componentes estándar J2EE. Exponer estos componentes como Servicios Web SOAP los hace accesibles casi universalmente -- y proporciona un simple mecanimo para integrar estos componentes. La arquitectura modular J2EE hace que este proceso sea relativamente fácil.

En esta página veremos cómo exponer componentes J2EE como Servicios Web y cómo usar Java Message Service (JMS) para enviar mensajes SOAP de forma eficiente. Principalmente hablaremos sobre Enterprise Java Beans (EJBs), porque son los componentes J2EE más ampliamente utilizados para la implemetnación de lógica de negocios, pero todas las técnicas demostradas también se aplican a otros componentes J2EE, como fuentes de datos JDBC o colas JMS.

. Procesamiento Estándar J2EE

Primero sumarizaremos algunos hechos importantes sobre la plataforma J2EE. Tradicionalmente, la aplicación cliente J2EE usa JNDI para encontrar componentes J2EE en el lado del servidor. Por ejemplo, la aplicación cliente busca la referencia EJB en JNDI y recibe un cliente proxy EJB, que luego el cliente puede usar para acceder al cliente EJB. Toda la comunicación J2EE normalmente ocurre sobre RMI.

. J2EE -- Aproximaciones Básicas

Hay dos aproximaciones básicas para acceder a recursos J2EE mediante SOAP.

Empezaremos con la aproximación más óbiva, que es crear una Servicio Web envoltura alrededor del EJB. Esta aproximación es apropiada particularmente en situaciones donde la aplicación del Servicio Web no se mapea directamente a las capacidades de un EJB individual y requiere alguna orquestación adicional de componentes J2EE.

En nuestro segundo ejemplo prosentaremos una aproximación de integración transparente con menos código. Su objetivo principal es exponer aplicaciones J2EE existentes como Servicios Web tan rápida y dinámicamente como sea posible. Esta aproximación nos permitirá acceder efectivamente a aplicaciones J2EE existentes sobre SOAP sin escribir o modificar ningún código.

. El ejemplo "Stock Quote EJB Wrapper"

En este ejemplo presentaremos la aproximación del Servicio Web envoltura alrededor de un EJB para acceder a un simple bean de sesión sin estado. Esta aproximación de envoltura es muy simple, y se utiliza ampliamente en muchos marcos de trabajo SOAP. Sólo hay ligeras diferencias entre las distintas implementaciones, que generalmente pertenecen al nivel de automátización del proceso de desarrollo. Esta aproximación requiere el despliegue de un Servicio Web que envuelva uno o más componentes J2EE existentes. Esta envoltura actúa como un puente entre el mundo SOAP y el mundo RMI. Los clientes envían solicitudes SOAP a la envoltura, y la envoltura las traduce en solicitudes RMI al componente EJB. Esta aproximación se recomienda para usarla con recursos J2EE sin estado, como beans de sesión sin estado. Para acceder a recursos con estado usando esta técnica necesitaríamos configurar servicios de ciclo de vida adiconales para manejar la eliminación apropiada de los recursos con estado huérfanos.

Primero necesitamos realizar algunos pasos de instalación y configuración muy sencillos:

  1. Instalar el servidor WASP.
  2. Descargar el código fuente y descomprimirlo en el directorio c:\wasp_demo.
  3. Usar la implementación de referencia de J2EE 1.3 que puedes descargar desde la website de Sun.
  4. Después de instalar el J2EE, tenemos que configurar el entorno de ejecución de WASP para que lo utilice haciendo un par de cambios en el script env.bat localizado en el subdirectorio bin en el directorio de instalación de WASP.
    • Primero comenta la siguiente línea (poniendo rem al principio de la línea:
      set INSTALLATION_TYPE=standalone
    • Luego quita el comentario de la siguiente línea del mismo fichero:
      set INSTALLATION_TYPE=j2ee
  5. También necesitamos modificar el script env.bat localizado en el directorio c:\wasp_demo\bin. Por favor, localiza los valores correctos para las variables de entorno J2EE_HOME, WASP_HOME y WASP_DEMO.

Una vez completados los pasos de instalación y configuración de arriba, arrancamos el servidor J2EE y el entorno de ejecución WASP usando los scripts startJ2EE y startserver. Luego ejecutamos el script deploy_j2ee para compilar el código fuente Java y desplegar los EJB que usaremos en estos ejemplos.

NOTA:
Necesitarás reiniciar el servidor J2EE después de desplegar los EJBs.

Puedes ver el código fuente Java en el paquete com.systinet.demos.stock para ver que las clases StockQuote, StockQuoteHome y StockQuoteBean implementan un bean de sesión sin estado bastante sencillo con un sólo método getQuote. Ya hemos desplegado este EJB al llamar al script deploy. Podemos asegurarnos que todos los EJBs se han desplegado correctamente usando la herramienta de administración de J2EE. Ejecuta el script J2EEAdmin desde el directorio bin del ejemplo para arrancar la herramienta de administración.

Ahora nos concentraremos en la implementación del Servicio Web Envoltura, listado abajo. Implementa un método getQuote, que contiene una simple invocación EJB. Primero recupera la referencia home del EJB desde JNDI y crea un ejemplar EJB. Luego invoca el método getQuote del EJB, y elimina el EJB. Finalmente se devuelve el resultado de la invocación al cliente del Servicio Web. Podemos ver estos pasos en el código de abajo:

package com.systinet.demos.stock;

import javax.naming.InitialContext;
import javax.naming.Context;
import javax.naming.NamingException;
import java.rmi.RemoteException;


public class StockQuoteService {
	
    public double getQuote(String symbol) throws Exception {
        
        // get the JNDI initial context
        System.err.println("Getting J2EE initial context");
        Context jndiContext = new InitialContext();
        // lookup the EJB home
        System.err.println("Looking up EJB Home");
        Object homeRef = jndiContext.lookup("Stock");
        StockQuoteHome home = 
        (StockQuoteHome)javax.rmi.PortableRemoteObject.narrow(
        homeRef, StockQuoteHome.class);
        // create the EJB instance
        System.err.println("Creating EJB");
        StockQuote ejb = home.create();
        // call the getQuote method
        System.err.println("Calling getQuote");
        double quote = ejb.getQuote("SUNW");
        System.err.println("SUNW "+quote);
        // remove the EJB
        System.err.println("Removing EJB");
        ejb.remove();
        return quote;
    }
}

Ahora podemos desplegar el Servicio Web ejecutando el script deploy_service. Luego ejecutamos el script run_wrapper para arrancar el cliente del Servicio Web que invoca al Servicio Web.

NOTA:
Hemos hecho este ejemplo tan sencillo para ilustrar los principipos básicos de la aproximación envoltura; sin embargo, las aplicaciones del mundo real normalmente son un poco más complejas. Un servicio envoltura normalmente se usa para ensamblar funcionalidades de varios EJBs y otros recursos J2EE. En dichos casos, el servicio envoltura normalmente expone diferentes interfaces programáticos de los beans originales.

. Integración J2EE Transparente

Otra forma de acceder a recursos J2EE es usar el marco de integración transparente. Con 'transparente' queremos decir que no es necesario escribir un servicio envoltura o modificar el código J2EE original. Esta aproximación es más útil si tenemos recursos J2EE existentes que queremos hacer disponibles para clientes SOAP o si tenemos clientes J2EE que necesitan acceder a recursos J2EE a través de Internet.

El marco de integración transparente J2EE descrito abajo explita las fortalezas de la arquitectura JNDI, que proporciona un mecanismo abstracto para acceder a recursos J2EE. Como dijimos anteriormente, en el procesamiento normal J2EE, un cliente J2EE llama el método lookup de JNDI, y el proveedor JNDI del ciente le pasa esta solicitud mediante RMI a otro servicio JNDI dentro del servidor J2EE. JNDI devuelve un proxy J2EE al cliente, que éste utiliza para invocar a los métodos del recurso J2EE remoto sobre RMI. En este ejemplo, usaremos un proveedor JNDI en el lado del cliente que habla SOAP en vez de RMI. Como puedes ver en la siguiente figura, cuando el cliente envía una llamada JNDI usando este proveedor, la solicitud se envía sobre SOAP a un Servicio Web JNDI. Este Servicio Web JNDI realiza la búsqueda real en el servidor de aplicaciones JNDI, obteniendo el proxy J2EE. Entonces el Servicio Web JNDI devuelve una del proxy J2EE. Después la aplicación cliente puede usar estas referencia remota para invocar los métodos del recurso J2EE. Cada invocación de método se transporta sobre SOAP al proxy J2EE, que redirige la solicitud al recurso J2EE real. Observarás que no se requieren modificaciones de código ni en el recurso J2EE ni en el cliente. Sólo se requieren unos cambios de configuración en el cliente para que apunte al proveedor JNDI basado en SOAP.

NOTA:
La mayoría de los servidores de ejecución de Servicios Web operan en el mismo contexto que el servidor de aplicacion, por eso la invocación redirigida es muy rápida y no degrada el rendimiento.

Esta aproximación también funciona para clientes no-Java. Ya que el Servicio Web JNDI es un Servicio Web estándar, cualquier cliente SOAP se puede aprovechar de este marco de invocación transparente. Por ejemplo, un cliente Microsoft Visual Basic puede llamar al método lookup del Servicio Web JNDI y obtener el proxy Servicio Web del recurso J2EE solicitado.

El Servicio Web JNDI realiza recolección de basura automática de todos los componentes creados en el entorno de ejecución de Servicios Web. La mayoría de esos componentes se descartan bajo demanda cuando una aplicación cliente descarta explícitamente el componente remoto, pero no hay garantía de la eliminación correcta en el mundo de los Servicio Web de acoplamiento ligero. Esto es por lo que a los recursos creados dinámicamente les sigue la pista y los maneja el servicio LifeCycle.

La mayor ventaja de esta aproximación es que proporciona acceso SOAP inmediato y transparente a cualquier recurso J2EE registrado en JNDI, incluyendo todo tipo de componentes EJB (beans de sesión con o sin estado, beans de entidad, y beans dirigidos por mensaje), además de JMS, JDBC y otros recursos J2EE. No se necesitan modificaciones ni envolturas para los recursos J2EE. Esta aproximación obviamente es muy útil para proporcionar un acceso rápido y fácil a sistemas existentes mediante SOAP. Veamos un ejemplo.

. Ejemplo de Integración Transparente J2EE

Este ejemplo usa un Servicio Web llamando a un EJB que se está ejecutando en el motor de la implementación de referencia de Sun (J2EE 1.3).

Primero chequeemos el código del EJB del lado del servidor. Como puedes ver, es un bean de sesión con estado estándar que mantiene el estado de un simple contador:

package com.systinet.demos.counter;

import javax.ejb.CreateException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
import javax.ejb.SessionSynchronization;

public class CounterEJB implements SessionBean {
    
    private SessionContext context;
    
    private int count = 0;

    /**
     * No argument constructor required by container.
     */
    public CounterEJB() {
    }

    /**
     * Create method specified in EJB 1.1 section 6.10.3
     */
    public void ejbCreate() {
    }

    /* Methods required by SessionBean Interface. EJB 1.1 section 6.5.1. */

    /**
     * @see javax.ejb.SessionBean#setContext(javax.ejb.SessionContext)
     */
    public void setSessionContext(SessionContext context){
        this.context = context;
    }

    /**
     * @see javax.ejb.SessionBean#ejbActivate()
     */
    public void ejbActivate() {
    }

    /**
     * @see javax.ejb.SessionBean#ejbPassivate()
     */
    public void ejbPassivate() {
    }

    /**
     * @see javax.ejb.SessionBean#ejbRemove()
     */
    public void ejbRemove() {
    }
    
    public long getCount() {
        return count++;
    }
}

Los otros recursos EJB son bastante obvios. Podemos ver el código del Counter remoto y los interfaces home CounterHome. Ya hemos desplegado el EJB en el primer ejemplo invocando el script deploy_j2ee. Por favor, observa que no necesitamos desplegar ningún Servicio Web específico en el entorno de ejecución de Servicios Web.

La aplicación cliente es un cliente estándard EJB con la excepción de que usa propiedades JNDI diferentes en el método getInitialContext.

package com.systinet.demos.counter;

import javax.naming.InitialContext;
import javax.naming.Context;
import javax.naming.NamingException;
import java.rmi.RemoteException;

public class CounterClient {

    public CounterClient() {
    }

   public static void main(String [] args){
        try {
            
           // get the JNDI initial context
           System.err.println("Getting J2EE initial context");	
           Context jndiContext = getInitialContext();
           // lookup the EJB home
           System.err.println("Looking up EJB Home");
           Object homeRef = jndiContext.lookup("Counter");
           CounterHome home = 
              (CounterHome)javax.rmi.PortableRemoteObject.narrow(homeRef, CounterHome.class);
           // create the EJB instance
           System.err.println("Creating EJB");
           Counter ejb = home.create();
           System.out.println("Calling count "+ejb.getCount());
           System.out.println("Calling count "+ejb.getCount());
           System.out.println("Calling count "+ejb.getCount());
           // remove the EJB
           System.err.println("Removing EJB");
           ejb.remove();
        
        } 
        catch(java.rmi.RemoteException re) {
            re.printStackTrace();
        }
        catch(Throwable t) {
            t.printStackTrace();
        }
    }
  
    static public Context getInitialContext() throws javax.naming.NamingException {
        
        java.util.Properties jndiProperties = new java.util.Properties();
        
        jndiProperties.put("java.naming.factory.initial","com.idoox.jndi.InitialContextFactoryImpl");
        jndiProperties.put("java.naming.provider.url","http://localhost:6060");
        
        return new InitialContext(jndiProperties);
    }
}

Por favor, observa que para mentener la simplicidad hemos codificado en el código todos los parámetros específicos JNDI. Hay dos parámetros que necesitamos definir para poder redigir la consolta JNDI al Servicio Web JNDI: java.naming.factory.initial y java.naming.provider.url. Estos parametros normalmente se utilizan en el fichero .properties de la aplicación en vez de estar codificados en la aplicación cliente. En dicho caso, no sería necesaría la recompilación del código.

El siguiente paso es compilar y ejecutar la aplicación cliente J2EE usando el script run. Deberías ver que se ha llamado al método getCount del EJB tres veces. Toda la comunicación entre la aplicación cliente y el lado del servidor se hace a través de mensajes SOAP. También podemos ver que el estado (el valor del contador) se mantiene correctamente.

. Enviar Mensajes SOAP de Forma Eficiente sobre JMS

Hoy en día la mayoría de los Servicios Web usan el protocolo de transporte HTTP para sus comunicaciones. HTTP es muy adecuado para muchas aplicaciones. Su principal ventaja es la flexibilidad de integración de aplicaciones a través de proxys y firewalls HTTP. Pero para algunas aplicaciones, HTTP podría no ser suficiente. HTTP es unidireccional y bloquea mucha de las características típicas empresariales como, la eficiencia, la persistencia y las transaciones. Su soporte de escenarios de enrutado de mensajes asíncronos tampoco es ideal. Una aproximación para corregir estos problemas es usar JMS para transportar mensajes SOAP. JMS puede proporcionar beneficios sustanciales para la comunicación de aplicaciones empresariales ya que soporta entrega de mensajes garantizada, colado y 'des-colado' de mensajes transacioanles, y semántica de mensajes síncronos y asíncronos. JMS también ofrece mucho mejor rendimiento y escalibilidad que el protocolo HTTP.

Este último ejemplo demuestra que SOAP es realmente independiente del protocolo de transporte. Accederemos al mismo Servicio Web que desarrollamos en el primer ejemplo, pero esta vez enviaremos los mensajes SOAP mediante JMS. La implementación es muy simple, sólo tenemos que cambiar la url de búsqueda pasada al método lookup al lado del cliente:

package com.systinet.demos.jms;

import org.idoox.wasp.Context;
import org.idoox.webservice.client.WebServiceLookup;

public final class StockClient {

  /**
   * @param args  not used.
   */
    public static void main( String[] args ) throws Exception {
      
      System.setProperty("java.naming.factory.initial","com.idoox.jndi.InitialContextFactoryImpl");  
      System.setProperty("java.naming.provider.url","http://localhost:6060");
      System.setProperty("idoox.demo.transport.j2ee", "true");
        
      // lookup service
      WebServiceLookup lookup = (WebServiceLookup)Context.getInstance(Context.WEBSERVICE_LOOKUP);
      // bind to StockQuoteService
      StockQuoteServiceProxy quoteService = (StockQuoteServiceProxy)lookup.lookup(
"jms://jms/[email protected]/QueueConnectionFactory~/StockEJBService/",StockQuoteServiceProxy.class);
      

      // use StockQuoteService
         
      System.out.println("Getting SUNW quote");
      System.out.println("------------------------");
      System.out.println("SUNW "+quoteService.getQuote("SUNW"));
      System.out.println("");
    }
}

Compilaremos y ejecutaremos el cliente del ejemplo JMS ejecutando el script run_jms. El marco de trabajo SOAP proporciona un mapeo completamente transparente del protocolo de transporte subyacente, por eso todas las características que mencionamos anteriormente (Servicios Web con estado, referencias remotas, fallos SOAP, etc.) funcionarán sobre JMS sin modificar nada en el código Java.

NOTA:
El entorno de ejecución de Servicios Web está pre-configurado para escuchar mensajes SOAP en la cola JMS jms/Queue.

. Limpieza

Ahora que hemos completado nuestros ejemplo, usamos los scripts undeploy_j2ee y undeploy_service para eliminar los EJB del servidor de aplicaciones y el Servicio Web del entorno de ejecución de Servicios Web.

COMPARTE ESTE ARTÍCULO

ENVIAR A UN AMIGO
COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN GOOGLE +
ARTÍCULO ANTERIOR

SIGUIENTE ARTÍCULO

¡SÉ EL PRIMERO EN COMENTAR!
Conéctate o Regístrate para dejar tu comentario.