En esta página:
Introducción
La fuerza conductora que hay detrás de EJB 2.1 es la necesidad de la tecnología Enterprise JavaBeans para soportar Servicios Web -- una necesidad que a su vez está dirigida por la liberación que ha hecho Microsoft de su plataforma .NET, que es una amenaza significante para J2EE, y para la tecnología Java en general. El soporte de Servicios Web se incorpora en tres nuevos APIs:
- JAX-RPC (Java API for XML-RPC). Es básicamente RMI Java sobre SOAP; proporciona una interface remoto para mensajería SOAP al estilo RPC.
- SAAJ (SOAP API with Attachments for Java). Es un API que mdoela la estructura de un mensaje SOAP y también tiene algunas capacidades limitadas de entrega de mensajes.
- JAXM (Java API for XML Messaging). Es similar a JMS (Java Message Service); proporciona una infraestructura de mensajería robusta para enviar y recibir mensajes SOAP.
Entender los Servicios Web
Los Servicio Web representan la última moda en programación dictribuida y quizás a innovación más importante desde la presentaciónd e Java en 1995 y XML en 1998. Aunque el término "Servicios Web" esta sobre-utilizado, llegar a una definición concreta es muy dificil porque los Servicios Web no son, al nivel más algo, especificos de cualquier tecnología o plataforma. Normalmente se les define con términos bastante abstractos como "un substrato para construir aplicaciones distribuidas usando software que se ejecuta en diferentes sistemas operativos y dispositivos" o "aplicaciones modulares auto-contenidas que se auto-describen y que pueden ser publicadas, localizadas e invocadas a través de la red". Por supuesto que estas citas se han tomado fuera de contexto pero este es el punto esencial: Necesitamos algún tipo de contexto para definir los Servicios Web.
Aquí tenemos una definición de los Servicios Web que tiene su significado en el contexto de J2EE, EJB, .NET y la mayoría de las otras plataformas de Servicios Web.
Los Servicios Web son aplicaciones de red que usan SOAP y WSDL para intercambiar información en la forma de documentos XML.
Para entender esta definición, necesitamos entender SOAP y WSDL. Aquí tenemos unas breves definiciones.
-
XML:
eXtensible Markup Language (XML) es una recomendación de la W3C (World Wide Web Consortium) que define las reglas para crear lenguajes de marcas XML. Un lenguaje de marcas XML es un conjunto de etiquetas (llamadas etiquetas) que se utilizan para describir información en texto Unicode y organizarla en forma de árbol. Los lenguajes de marcas se pueden describir utilizando un Squema XML. Un documento de texto que se adhiere a las reglas descritans en una gramática XML es conocido como ejemplar XML. Las tecnologías XML son ubiquas, y se emplean frecuentemente en el intercambio y almacenamiento de datos de aplicación, y para organizar documentos literalmente.
-
SOAP 1.1
Simple Object Access Protocol (SOAP), una gramática XML desarrollada por Microsoft, IBM y otros, está actualmente bajo el auspicio de la W3C. Es un protocolo de aplicación utilizado en mensajería RPC y mensajería asíncrona. SOAP es muy flexible y extensible al contrario que sus predecesores (DCE RPC, CORBA IIOP, Java RMI-JRMP, y DCOM) ha sido endorsado y adoptado por las mayoría de los vendedores principales.
-
WSDL 1.1
Web Service Description Language (WSDL), también es una gramática XML, desarroolada por Microsoft e IBM bajo el auspicio de la W3C. Es un IDL (Interface de Definición de Lenguaje) basado en XML que se puede utilizar para describir Servicios Web, incluyendo el tipo de formato de mensaje espereado, el protocolo de Internet utilizado, y la dirección de Internet del servicio wev.
En esencia, los Servicios Web representan una nueva tecnología de objetos distribuidos similar a CORBA IIOP y Java RMI, pero con algunas diferencias críticas. La principal diferencia es que los Servicios Web son, en mi opinión, realmente independientes de la plataforma. Aunque Java RMI y CORBA IIOP hacen la misma declaración, de hecho estas viejas tecnologías requieren sus propias plataformas. Para utilizar Java RMI necesitamos una máquina virtual Java y el lenguaje de programación Java, Para los desarrolladores que trabajan con otros lenguajes, como Visual Basic o C++, Java RMI no es independiente de la plataforma. CORBA IIOP también es restrictivo, porque el protocolo IIOP normalmente requiere una infraestructura elaborada como CORBA ORB, que limita a los desarrolladores a aquellos pocos vendedores que soportan CORBA, o al entorno Java (que incluye soporte interno para CORBA IIOP).
Por el contrario, los Servicios Web, no están atados a una plataforma específica porque están enfocados a los protocolos utilizados para intercambiar mensajes -- SOAP y WSDL -- no a la implementación que soporta estos protocolos. En otras palabras, podemos construir Servicios Web en cualquier plataforma, usando cualquier lenguaje en cualquier lugar que nos apetezca.
Otra ventaja de los Servicios Web sobre otros sistemas de objetos distribuidos es que están basados en una infraestructura de tecnología existente y por lo tanto los vendedores tienen más fácil implementarla. SOAP y WSDL están basados en XML, para el que ya existe un amplio soporte, en la forma de analizadores XML. Los analizadores ZML están disponibles para casi cualquier lenguaje de programación moderno (Java, C, C++, C#, VB, Perl, Python, etc.), por eso la infreaestructura para procesar mensajes SOAP y documentos WSDL ya existe. Además, los mensajes de Servicios Web normalmente se intecambian mediante TCP/IP, que está soportado por todas las plataformas de software modernas y por cualquier lenguaje de programación disponible hoy en día. Como los Servicios Web se construyen sobre una infraestructura existende de XML y TCP/IP, la adopción de los Servicios Web se ha incrementado rápidamente.
Herramientas SOAP contra Herramientas basadas en Stub
Un conjunto de herramietnas SOAP es un API usado para enviar y recibir mensajes SOAP. Como protocolo, SOAP define la estructura de los mensajes, pero no define la implementación de herramientas SOAP. Ya hemos menciando que literalmente hay docenas de conjuntos de herramientas SOAP en muchos lenguajes de programación diferentes como Java, C y C++, C#, VB.NET, Perl, etc. La proliferación de herramientas SOAP es a la vez sobresaltada y valiente, porque indica lo fácil que es escribir API para el estándar SOAP.
Cada conjunto de herramientas define su propio modelo de programación, y son ligeramente diferentes. Algunas herramientas modelan la estructura de los mensajes SOAP, con objeto como Envelope, Header, y Body, mientras que otras adoptan el modelo de programación de stubs RPC, que hace que un Servicio Web se parezca a una llamada a un método o a un procedimiento. Las herramientas SOAP que modelan la estructura de un mensaje SOAP se pueden categorizar como herramientas basadas en API, mientras que las las herramientas SOAP que usan stubs RPC se pueden llamar herramientas basadas en stub.
Las herramientas basadas ens tub están dirigidas al modelo de programación tradicional RPC. Usan un stub (esqueleto) RPC para comunicar con el Servicio Web. Hacen que el Servicio Web aparezca, desde la perspectiva del cliente, como un objeto con métodos. JAX-RPC (Java API for XML-RPC) es el modelo de programación estándar basado en stubs para Enterprise JavaBeans 2.1.
Las herramientas SOAP se utilizan para construir explicitamente un mensaje SOAP, usando tipos complejos como estructuras u objetos, que modela las partes de un mensaje SOAP (Envelope, Header, Body, etc.) y APIs basados en árboles XML como DOM (Document Object Model) de la W3C. SAAJ (SOAP with Attachments API for Java) es el API SOAP estándar para EJB 2.1. Proporciona un API orientado a objetos para construir mensajes SOAP basados en el patrón de la Factoría Abstracta. SAAJ se puede utilizar en solitario, pero normalmente se utiliza en combinación con JAXM (Java API for XML Messaging), que comunica con los Servicios Web usando SAAJ para modelar los mensajes SOAP de entrada y salida.
Herramientas WSDL y SOAP
Sin importar la herramienta utilizada para generar e intercambiar mensjes SOAP, WSDL define, de una forma independiente de la plataforma, cómo se estructuran e intercambian los mensajes SOAP.
Cuando se utiliza una herramienta SOAP basada en API como aquellas que tratan con SAAJ y JAXM, un desarrollador de clientes tratará el documento WSDL como una guia para construir e intercambiar mensajes SOAP. En este caso, el desarrollador debe poder leer y entender el documento WSDL. WSDL le dice al desarrollador el estilo de codificación, los espacios de nombres XML empleados, qué elementos usar en el los elementos Body y Header SOAP.
Cuando los desarrolladores de una aplicación cliente usan una herramienta basada en stub como JAX-RPC, usarán una herramietna de generador de stubs suministrada por el vendedor. Esta herramienta lee el documento WSDL y automáticamente genera el stub y el interface remoto (ver la siguiente figura). El nombre del interface remoto, sus métodos y sus parámetros se derivan directamente del documento WSDL. El stub también se derivaría del documento WSDL, e implementaría el protocolo (por ejemplo, HTTP), el estilo de codificación, y la dirección de Internet descrita en el documento WSDL.

JAX-RPC y EJB 2.1
Ya hemos visto que JAX-RPC esencialmente es Java RMI sobre SOAP. Cada vendedor de EJB proporciona su propia implementación de JAX-RPC, pero las diferencias seran menores porque todas las implementaciones se deben adherir a la especificación JAX-RPC. JAX-RPC se puede utilizar como un API cliente para obtener acceso a otros servicios web, pero también es la base para un nuevo tipo de interface de beans enterprise llamado "interface de punto final", sobre una pareja con los tipos de interfaces local y remoto.
JAX-RPC como un API cliente
Como un API cliente, JAX-RPC se puede utilizar desde beans de sesión, de entidad o dirigidos a mensaje para intercambiar mensajes SOAP con Servicios Web de cualquier plataforma. JAX-RPC define tres modelos de programación: stubs generados, proxy dinámico, y el DII (Dynamic Invocation Interface). Stubs Generados
Si estamos usando JAX-RPC para acceder a un Servicio Web, el Servicio Web debe publicar un documento WSDL. La herramienta JAX-RPC, proporcionada por el vendedor EJB, genera los interfaces Java RMI y los stubs que implementan las operaciones del Servicio Web descritas por el documento WSDL. Una vez que se han generado los stubs y los interfaces podemos unirlos al contexto JNDI ENC (Environment Naming Context) de cualquier bean enterprise y usarlos para comunciarnos con esos Servicios Web. En est emodelo, los strubs se generan durante el tiempo de despligue, como muestra la siguiente figura.

WSDL describe los interface de los Servicio Web como puertos; cada puerto podría tener una o más operaciones. Puertos y operaciones son análogos a los interfaces y los métodos Java, respectivamente. De hecho, JAX-RPC define un mapeo entre WSDL y Java RMI que genera interfaces remotos desde los puertos, con los métodos que corresponden a loas operaciones del puerto. Por ejemplo, un documento WSDL podría describir un puerto llamado BookPrice con una sola operación llamada getBookPrice. Abajo podemos ver una versión reducida del documento WSDL de BookPrice:
<?xml version="1.0"?> <definitions name="BookPrice" targetNamespace="http://www.xyz.com/GetBookPrice" xmlns:tns="http://www.xyz.com/GetBookPrice" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/wsdl/"> <!-- message elements describe the paramters and return values --> <message name="IsbnMessage"> <part name="isbn" type="xsd:string" /> </message> <message name="PriceMessage"> <part name="price" type="xsd:float" /> </message> <!-- portType element describes the abstract interface of a Web service --> <portType name="BookPrice"> <operation name="getBookPrice"> <input name="isbn" message="tns:IsbnMessage"/> <output name="price" message="tns:PriceMessage"/> </operation> </portType> <!-- binding goes here --> <!-- service tells us the Internet address of a Web service --> <service name="BookPriceService"> <port name="BookPrice" binding="tns:BookPrice_Binding"> <soap:address location="http://www.xyz.com/BookPrice" /> </port> </service> </definitions>
Durante el despliegue una herramienta de generación de stubs JAX-RPC convierte el puerto WSDL en un interface y un stub correspondientes, y crea una factoría para acceder a ese stub, basado en la definición del servicio. Los interfaces del puerto y del servicio se parecerían a estos ejemplos:
public Interface BookPriceService extends javax.xml.rpc.Service{ public BookPrice getBookPrice( ) throws RemoteException; } public Interface BookPrice extends java.rmi.Remote { public float getBookPrice(String isbn) throws RemoteException; }
Este es un ejemplo bastante simple; el servicio sólo tiene un puerto. En la práctica un servicio podría tener muchos puertos, cada uno de ellos con su corresponiente interface remoto y su stub.
Una vez que se han generado el interface remoto y el stub y se han unido al JNDI ENC, se pueden utilizar en tiempo de ejecución para invocar las operaciones del Servicio Web. En el siguiente slistado un bean de sessión sin estado, BookCatalog, utiliza JAX-RPC para buscar el precio de venta de un libro desde un Servicio Web .NET.
public class BookCatalog implements javax.ejb.SessionBean { ... public float getWholeSalePrice(String isbn) { try { InitialContext jndiContext = new InitialContext ( ); BookPriceService service = jndiContext.lookup("java:comp/env/service/BookPriceService"); BookPrice bookPrice_port = service.getBookPrice(); float price = bookPrice_port.getBookPrice( isbn ); return price; catch(RemoteException re){ // handle remote exception }catch(ServiceException se){ // handle service exception }catch(NamingException ne){ // handle naming exception } } ... }
Cuando se llama al método getBookPrice(), el stub JAX-RPC envia un mensaje SOAP al Servicio Web, en este caso un programa .NET. El mensaje SOAP generado por el stub se podría parecer a esto:
<?xml version='1.0' ?> <env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xyz='http://www.xyz.com/BookPrice" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> <Body> <xyz:getBookPrice> <isbn xsi:type="string">1565928695 </xyz:getBookPrice> </Body> </env:Envelope>
Entonces el Servicio Web .NET procesa el mensaje SOAP y enviar una respuesta al stub. El stub extrae el resultado del mensaje SOAP y se lo devuelve al cliente, como se puede ver en la siguiente figura:

Los métodos en stubs JAX-RPC pueden tener argumentos que sean tipos primitivos (int, long, etc.), envolturas de tipos primitivos (java.lang.Integer, java.lang.Long, etc.), arrays, uno pocos tipos Java estándar (String y Date), y tipos de objetos personalizados. Los tipos de objetos personalziadaso se deben adherir a las reglas definidas en la especificación JAX-RPC. Estas reglas no son tan restrictivas: prohiben los tipos referencia java.rmi.Remote y requieren que los objetos sean seriables a XML de acuerto a las convenciones prescritas por JAX-RPC.
Proxy DinámicoJAX-RPC también soporta los proxies dinámicos. Un proxy dinámico se usa de la misma forma que el stub generado, excepto en que su interface remoto y la implementación del stub se generan dinámicamente en tiempo de ejecución. Si estas familiarizado con las arquitectura proxy de la API Java Reflection, entonces el JAX-RPC dinámico tendrá sentido para ti. El siguiente ejemplo es un bean de sesión sin estado que JAX-RPC crea como un stub dinámico:
public class BookCatalog implements javax.ejb.SessionBean { ... public float getWholeSalePrice(String isbn) { try { InitialContext jndiContext = new InitialContext ( ); javax.xml.rpc.Service service = jndiContext.lookup("java:comp/env/service/DynamicService"); BookPrice bookPrice_port = service.getPort(BookPrice.class); float price = bookPrice_port.getBookPrice( isbn ); return price; catch(RemoteException re){ // handle remote exception }catch(ServiceException se){ // handle service exception }catch(NamingException ne){ // handle naming exception } } ... }
En tiempo de ejecución, el método getPort() mapea automáticamente el interface BookPrice a su correspondiente definición de puerto en el documento WSDL, luego genera un stub que implementa ese interface.
El Interface DIIJAX-RCP también soporta un API todavía más dinámico, llamado DII (Dynamic Invocation Interface). DII permite al desarrollador ensamblar dinámicamente llamadas a métodos SOAP en tiempo de ejecución. Si has usado CORBA Dynamic Invocation Interface, JAX-RPC DII te será familiar. JAX-RPC DII se parece a Java Reflection en que te permite obtener una referencia a un objeto que representa una sola operación de un Servicio Web, en la forma de un método, y llamar a ese método sin tener que acceder a la factoría del servicio o utilizar un stub y un interface remoto. Por ejemplo el siguiente listado muestra el mismo bean enterprise accediendo a la operación getBookPrice() del puerto BookPrice.
public class BookCatalog implements javax.ejb.SessionBean { ... public float getWholeSalePrice(String isbn) { try { InitialContext jndiContext = new InitialContext ( ); javax.xml.rpc.Service service = jndiContext.lookup("java:comp/env/service/DynamicService"); QName port = new QName("http://www.xyz.com/GetBookPrice ","BookPrice"); QName operation = new QName("http://www.xyz.com/GetBookPrice", "getBookPrice"); Call callObject = service.createCall(port, operation); Object [] args = new Object[1]; args[0] = isbn; Float price = (Float) callObject.invoke( args ); return price.floatValue(); }catch(JAXRPCException se){ // handle JAX-RPC exception }catch(NamingException ne){ // handle naming exception } } } ... }
Realmente podemos configurar -- en tiempo de ejecución -- los parámetros, el estilo de llamada, la codificación, etc. Todo lo que podamos configurar estáticamente con WSDL podemos hacerlo dinámicamente con DII.
Aunque DII ciertamente es útil para sistema auto-organizados, la mayoría de los desarrolladores de EJB usarán stubs generados para acceder a Servicios Web. En las mayorías de lso casos, ellos conocerán por adelantado los servicios wev y las operaciones específicas a las que van a aceder. Cuando los stubs generados son tan simples, ¿por qué complicarse con DII?
Una de las áreas oscuras asociadas con el uso de proxies dinámixo o DII es cómo el servicio localiza los ficheros WSDL. EJB 2.1 no corrige este problema, por eso es por lo que cada vendedor necesita proporciona extensiones para declarar dónde están los ficheros WSDL usado para generar los proxies dinámicos o los objetos Call de DII.
JAX-RPC y el Interface de Punto Final
Como JAX-RPC simplemente es otro sabor de Java RMI, parece natuar usarlo para acceder a beans enterprise, lo que realmente ya podemos hacer usando Java RMI-IIOP y otros sabores propietarios de Java RMI. Usar JAX-RPC para hablar con EJBs imlica que los EJBs actúan como Servicios Web, y ellos pueden hacerlo -- al menos, los beans de sesión sin estado pueden hacerlo. EJB 2.1 nos permite usar JAX-RPC sólo con beans de sesión sin estado, porque SOAP es un protocolo de mensajería sin estado - no tiene noción de la identidad del objeto, por eso no puede utilizarse con beans con estado o beans de entidad.
EJB 2.1 define un nuevo interface Servicio Web endpoint (punto final) para beans de sesión sin estado. La gente del mundo de los Servicios Web usa el término "punto final" para algo que envía y recibe mensajes SOAP. En EJB un punto final es un bean de sesión sin estado al que se puede acceder mediante SOAP, y que sigue las reglas de mapeo de Java-a-WSDL y de Java-a-SOAP especificadas por la especificación SOAP. Usar JAX-RPC como la base para EJB de punto final tiene sentido, porque la especificación JAX-RPC define reglas detalladas para alineación entre mensajes SOAP y llamadas a métodos Java así como generar documentos WSDL desde interfaces remotos Java (y viceversa).
Al contrario que los interfaces remoto o local familiares para los desarrolladores de EJB, el interface de punto final no extiende un objeto del tipo EJB (es decir, EJBObject o EJBLocalObject). En su lugar extiende directamente el interface javax.ejb.Remote. Por ejemplo, el Servicio Web BookPrice usado anteriormente, se podría implementar faclmente como un EJB de punto final. El siguiente código muestra el interface de punto final para BookPrice, y un listado parcial del bean de sesión sin estado que es la implementación del Servicio Web:
public interface BookPrice extends javax.rmi.Remote { public String getBookPrice(String isbn) throws javax.rmi.RemoteException; } public class BookPriceWS implements BookPrice, javax.ejb.SessionBean { public float getBookPrice(String isbn){ Connection con = null; Statement stmt = null; ResultSet rs; try { DataSource ds = jdniEnc.lookup("java:comp/env/jdbc/DataSource"); con = ds.getConneciton(); stmt = con.createStatement(); rs = stmt.executeQuery("SELECT wholesale FROM CATALOG WHERE isbn = \'" +isbn+"\'"); if(rs.next()){ float price = rs.getFloat("wholesale"); return price; }else{ return 0;// zero means its not stocked. } } catch (SQLException se) { // handle SQLException } } ... }
Una bonita ventaja que tiene el punto final sobre los interfaces local y remoro es que no llevan basura extra en la forma de métodos sin utilizar de EJBObject o EJBLocalObject por eso las clases bean enterprise puedn implementar el interface de punto final sin ningún problema. (Nota: No necesitamos declarar la RemoteException en los métodos de la clase bean porque las implemetnación pueden implementar un subconjunto de excepciones que declara un interface).
Además, el punto final no incluye un interface home. SOAP no soporta específicamente el paso-por-referencia, por eso no podemos perdire a un interface de servicio wev (un interface home) que nos pase una referencia a otro (un interface remoto). Además, no podemos crear o eliminar un Servicio Web. Por estas razones no es necesario un interface home.
Cuando desplegamos un Servicio Web como un bean de sesión sin estado, primero definimos el interface de punto final, luego lo usamos para genear stubs JAX-RPC del cliente, podemos empaquetarlos con un JAR de un cliente J2EE, sin ninguna modificación y, usarlos para acceder al bean de sesión sin estado, usando SOAP como protocolo. Esta táctica funciona mejor cuando usamos la misma marca de servidor EJB para desplegar el cliente y los beans. Si generamos undocumento WSDL desde un interface de punto final, otras herramientas SOAP pueden usar el documento para acceder a nuestro bean de sesión sin estado. WSD y SOAP son la lengua franca de los Servicios Web, por eso publicar un WSDL para un Servicio Web EJB permite la interperabilidad con otras plataformas.

Hay algunas limitaciones a un interface de punto final que son únicas a los Servicios Web. Por una cosa, no está permitido el paso-po-referencia. Una consecuencia es que un argumento de un método no podría ser un tipo referencia remota que implemente el interface javax.rmi.Remote. Esta restricción existe porque el paso por referencia en la programación distribuida es inherentemente complicado. Requiere especificaciones para alinear referencias y la realización de esas referencias en construcciones programáticas (stubs), que lanzan los problemas de implementación.
Además de ls limitación al paso-por-referencia, las reglas de los interfaces de punto final requieren que los métodos sólo acepten como argumentos, tipos primitivos, arrays u objetos serializables. Una implicación interesante es que el paso-por-valor (paso-por-copia) está soportado para cualquier tipo personalizado. Por ejemplo, si definimos nuestra propia clase Address serializable, este tipo podría ser un parámetro de un método de punto final. También existe soporte para los tipos estándar de Java como String, Date, Calendar, BigInteger, y BigDecimal.
Se pueden utilizar diferentes estilos de mensajería y protocolos con SOAP y son documentados por WSDL. Estos incluyen RPC/codificado, documento/literal, RPC/literal, e incluso documento/codificado. Se requiere que los vendedores de JAX-RPC soporten RPC/codificado y documento/literal; los otros estilos no son obligatorios.
Cuando usamos documento/literal, el parámetro al métodos del punto funal es un documento XML, representado por un tipo especial llamado SOAPElement. Este tipo forma parte del API SAAJ (SOAP API with Attachments for Java), un API que modela la estructura del mensaje SOAP. El parámetro SOAPElement de un método JAX-RPC documento/literal representa el documento XML que está contenido por el elemento Body de SOAP -- pero esto empieza a salirse del ámbito de este tutorial. En la mayoría de los casos los desarrolladores de EJB usarán el estilo de mensajería RPC/codificado, pero si lo necesitamos está disponible el estilo documento/literal.