3.4 JAX-RPC Índice Introducción a SOAP JAX-RPC SOAP (1) SOAP es un protocolo basado en XML para el intercambio de mensajes en un entorno distribuido Los mensajes SOAP se envían encapsulados en otros protocolos de nivel de aplicación (e.g. HTTP o SMTP) Originalmente acrónimo de “Simple Object Access Protocol” Hoy ya no se considera acrónimo Estandarizado por W3C HTTP es el usado habitualmente (buena elección para atravesar firewalls) Con SOAP, un servicio se compone de puertos, y cada puerto ofrece un conjunto de operaciones Cada operación tiene: nombre, parámetros (entrada, salida, entrada/salida), valor de retorno y posibles “faults” (excepciones) SOAP (2) Las operaciones pueden ser Síncronas (RPC): el cliente envía la petición y se queda bloqueado hasta que llegue la respuesta La alternativa síncrona es la más usada hoy en día Asíncronas: el cliente envía la petición, continúa con su ejecución, y más adelante puede comprobar si llegó la respuesta SOAP estandariza la manera de enviar un mensaje para invocar una operación del servicio y cómo enviar la respuesta Misma estructura de mensaje para petición y respuesta Envolope Header (opcional) Elemento raíz de un mensaje SOAP Puede contener un elemento Header y otro Body Permite pasar información “extra” a protocolos de nivel superior (e.g. tokens de seguridad, identificadores de transacción, etc.) Body (obligatorio) Contiene los datos del mensaje SOAP (y 3) [Ejemplo (fuente Wikipedia): servicio con operación getProductDetails] <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <getProductDetails xmlns="http://warehouse.example.com/ws"> <productID>827635</productID> </getProductDetails> </soap:Body> </soap:Envelope> Cliente Servicio <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <getProductDetailsResponse xmlns="http://warehouse.example.com/ws"> <getProductDetailsResult> <productName>Toptimate 3-Piece Set</productName> <productID>827635</productID> <description>3-Piece luggage set. Black Polyester.</description> <price currency="NIS">96.50</price> <inStock>true</inStock> </getProductDetailsResult> </getProductDetailsResponse> </soap:Body> </soap:Envelope> WSDL y UDDI WSDL (Web Services Description Language) Permite especificar en XML la interfaz de un servicio (puertos, operaciones, tipos de datos, etc.) Estandarizado por el W3C UDDI (Universal Description, Discovery and Integration of Web Services) Interfaz SOAP de un servicio (registro UDDI) que proporciona operaciones registrar y buscar servicios Web Cada servicio Web se registra dando: su nombre, una descripción (e.g. la URL de su WSDL, una descripción textual, etc.), etc. Permite localizar servicios a partir de su nombre, descripción, etc. Estandarizado por OASIS (http://www.oasis-open.org) Registro UDDI SOAP Client e SOAP SOAP Servicio APIs de programación (1) De bajo nivel Permiten explícitamente construir, enviar y recibir mensajes SOAP De alto nivel Disponen de un compilador de WSDL Para el cliente, por cada puerto, el compilador genera un interfaz, una clase (stub/proxy) que lo implementa y tipos de datos asociados El interfaz contiene una operación por cada operación del puerto El stub/proxy implementa cada operación Construye la petición SOAP (convierte los parámetros a XML) La envía Espera por la respuesta SOAP (si es una operación síncrona) Convierte el valor de retorno o “fault” (XML) a un objeto APIs de programación (2) De alto nivel (cont) Para el servicio, por cada puerto, el compilador genera una clase skeleton y tipos de datos asociados El skeleton Recibe las peticiones SOAP Convierte los parámetros de XML a objetos Delega en una clase implementada por el desarrollador que proporciona una operación por cada operación del puerto Convierte el valor de retorno o excepción a XML Envía la respuesta SOAP También proporcionan un compilador de interfaces a WSDL Permite que el desarrollador del servicio especifique el interfaz del servicio en un lenguaje convencional (e.g. Java, C#, etc.) Genera el WSDL automáticamente a partir del interfaz APIs de programación (y 3) De alto nivel (cont) Resto aplicación cliente getProductDetails <<interface>> ProductManager ProductManagerImpl getProductDetails Clase Proxy Cliente Skeleton Servicio Índice Introducción a SOAP JAX-RPC ¿Qué es JAX-RPC? (1) JAX-RPC es un API estándar en Java para implementar e invocar operaciones de servicios Web SOAP mediante el paradigma de RPC Forma parte del API de Java EE Como cualquier otro API de Java EE, está formada por un conjunto de interfaces Paquete javax.xml.rpc Existen múltiples implementaciones El código escrito por el desarrollador no depende de la implementación concreta de JAX-RPC que se utilice Nosotros utilizaremos Apache Axis Recientemente se ha estandarizado otro API, JAX-WS, que sustituye a JAX-RPC En lo que respecta al soporte para RPC, conceptualmente es equivalente a JAX-RPC Usa anotaciones para facilitar el desarrollo Proporciona también soporte para invocaciones asíncronas y REST Actualmente la mayor parte de las aplicaciones Java usan JAX-RPC Apache Axis todavía no proporciona una implementación de JAXWS ¿Qué es JAX-RPC? (2) Especifica un mapping de WSDL a Java Permite que las implementaciones de JAX-RPC proporcionen un compilador de WSDL a Java, que genere stubs (proxies) y skeletons para invocar e implementar servicios Web Especifica un mapping de Java a WSDL Permite que las implementaciones de JAX-RPC proporcionen un compilador de Java a WSDL, que genere el documento WSDL correspondiente a un interfaz Java La definición del interfaz Java está sujeta a ciertas restricciones El documento WSDL permite que un cliente (escrito sobre cualquier plataforma) pueda invocar el servicio Web ¿Qué es JAX-RPC? (y 3) Proceso de desarrollo Interfaz Java Documento WSDL Compilador Java Compilador WSDL2Java Compilador Java2WSDL Stubs, skeletons y tipos Java Documento WSDL ¿Qué es Apache Axis? Una implementación de JAX-RPC que permite Invocar servicios SOAP Implementar servicios SOAP en un servidor de aplicaciones Java EE Sólo requiere que el servidor proporcione el API de Servlets (e.g. Tomcat) Incluye Un conjunto de librerías Un compilador de Java a WSDL Un compilador de WSDL a Java Ejemplo StockQuote Servicio Web que ofrece una interfaz con una operación que a partir de un conjunto de identificadores de valores bursátiles devuelve sus cotizaciones Cada cotización incluye Su identificador Su valor El número de segundos de antigüedad que tiene el valor (el valor real actual sería ligeramente distinto) SOAP/HTTP Cliente StockQuoteProvider El ejemplo permitirá aprender cómo en JAX-RPC Se define el interfaz de un servicio Web Se invoca un servicio Web Se implementa un servicio Web Definición de la interfaz del servicio Web (1) Se ha definido en el paquete es.udc.ws.jaxrpctutorial.servicedef El paquete incluye StockQuoteProvider: la interfaz propiamente dicha TradePrice: la clase que modela una cotización Define la operación getLastTradePrices La operación getLastTradePrices devuelve los TradePrice correspondientes a un conjunto de identificadores de valores bursátiles que recibe como parámetro IncorrectTickerSymbolException La operación getLastTradePrices levanta esta excepción si alguno de los identificadores no existe Definición de la interfaz del servicio Web (2) Método de trabajo Compilar las clases del paquete es.udc.ws.jaxrpctutorial.servicedef Utilizar el compilador de Java a WSDL sobre StockQuoteProvider Genera StockQuoteProvider.wsdl Usar el compilador de WSDL a Java sobre StockQuoteProvider.wsdl Se le indicará que genere el código Java en el paquete es.udc.ws.jaxrpctutorial.wsdl El código generado incluirá Stub, skeleton y clases auxiliares, y Otra vez StockQuoteProvider, TradePrice e IncorrectTickerSymbolException (incluyen atributos y operaciones adicionales que necesitan el stub y el skeleton) Tanto el cliente como el servicio Web usarán estos tipos (paquete es.udc.ws.jaxrpctutorial.wsdl) Los tipos definidos en es.udc.ws.jaxrpctutorial.servicedef sólo se definen para poder usar el compilador de Java a WSDL y obtener el fichero WSDL (y a partir de éste, usar el compilador de WSDL a Java) Definición de la interfaz del servicio Web (y 3) Alternativamente se podría intentar usar sólo un paquete Ejemplo Compilar las clases del paquete es.udc.ws.jaxrpctutorial.servicedef Utilizar el compilador de Java a WSDL sobre StockQuoteProvider Usar el compilador de WSDL a Java sobre StockQuoteProvider.wsdl Genera StockQuoteProvider.wsdl Indicando que genere el código Java en el paquete es.udc.ws.jaxrpctutorial.servicedef Problema El compilador de WSDL a Java “machaca” los tipos StockQuoteProider, TradePrice y IncorrectTickerSymbolException con los generados por él En principio esto no debería suponer un problema, pero no funciona bien en Axis 1.3+ cuando se repite el proceso una segunda vez (compilador de Java a WSDL + compilador de WSDL a Java) es.udc.ws.jaxrpctutorial.servicedef.StockQuoteProvider package es.udc.ws.jaxrpctutorial.servicedef; import java.rmi.Remote; import java.rmi.RemoteException; public interface StockQuoteProvider extends Remote { TradePrice[] getLastTradePrices(String[] tickerSymbols) throws RemoteException, IncorrectTickerSymbolException; } es.udc.ws.jaxrpctutorial.servicedef.TradePrice (1) package es.udc.ws.jaxrpctutorial.servicedef; import java.io.Serializable; public class TradePrice implements Serializable { private String tickerSymbol; private double price; private int elapsedSeconds; public String getTickerSymbol() { return tickerSymbol; } public void setTickerSymbol(String tickerSymbol) { this.tickerSymbol = tickerSymbol; } public double getPrice() { return price; } es.udc.ws.jaxrpctutorial.servicedef.TradePrice (y 2) public void setPrice(double price) { this.price = price; } public int getElapsedSeconds() { return elapsedSeconds; } public void setElapsedSeconds(int elapsedSeconds) { this.elapsedSeconds = elapsedSeconds; } } es.udc.ws.jaxrpctutorial.servicedef.IncorrectTickerSymbolException package es.udc.ws.jaxrpctutorial.servicedef; public class IncorrectTickerSymbolException extends Exception { private String incorrectTickerSymbol; public IncorrectTickerSymbolException(String incorrectTickerSymbol) { this.incorrectTickerSymbol = incorrectTickerSymbol; } public String getIncorrectTickerSymbol() { return incorrectTickerSymbol; } } Visión global de WSDL Dejaremos momentáneamente la explicación de los detalles de la definición del interfaz remoto StockQuoteProvider y sus tipos asociados Una vez compilados los anteriores ficheros Java, se puede obtener el documento WSDL Un documento WSDL consta de varias partes Definición Definición Definición Definición Definición de de de de de tipos de datos mensajes tipos de puertos bindings servicios Vamos a echar un vistazo al fichero generado Objetivo: comprender el formato general de un documento WSDL StockQuoteProvider.wsdl (1) <?xml version="1.0" encoding="UTF-8"?> <wsdl:definitions targetNamespace="http://ws.udc.es/jaxrpctutorial" xmlns:apachesoap="http://xml.apache.org/xml-soap" xmlns:impl="http://ws.udc.es/jaxrpctutorial" xmlns:intf="http://ws.udc.es/jaxrpctutorial" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <wsdl:types> <schema targetNamespace="http://ws.udc.es/jaxrpctutorial" xmlns="http://www.w3.org/2001/XMLSchema"> <import namespace="http://schemas.xmlsoap.org/soap/encoding/"/> <complexType name="ArrayOf_xsd_string"> <complexContent> <restriction base="soapenc:Array"> <attribute ref="soapenc:arrayType" wsdl:arrayType="xsd:string[]"/> </restriction> </complexContent> </complexType> StockQuoteProvider.wsdl (2) <complexType name="TradePrice"> <sequence> <element name="elapsedSeconds" type="xsd:int"/> <element name="price" type="xsd:double"/> <element name="tickerSymbol" nillable="true" type="xsd:string"/> </sequence> </complexType> <complexType name="ArrayOfTradePrice"> <complexContent> <restriction base="soapenc:Array"> <attribute ref="soapenc:arrayType" wsdl:arrayType="impl:TradePrice[]"/> </restriction> </complexContent> </complexType> <complexType name="IncorrectTickerSymbolException"> <sequence> <element name="incorrectTickerSymbol" nillable="true" type="xsd:string"/> </sequence> </complexType> </schema> </wsdl:types> Definición de tipos de datos - Comentarios Es posible usar varios sistemas de tipos El uso de un esquema XML es el más habitual Cuando el protocolo que se usa es SOAP, también se pueden usar tipos SOAP En el ejemplo se definen El tipo complejo ArrayOf_xsd_string, que corresponde al tipo Java String[] Los tipos complejos que representan vectores, se definen como una especialización por restricción de soapenc:Array Los tipos complejos TradePrice, ArrayOfTradePrice e IncorrectTickerSymbolException que corresponden a los tipos Java con el mismo nombre El atributo nillable con valor true especifica que el correspondiente elemento puede tomar el valor nil (null en Java) StockQuoteProvider.wsdl (3) <wsdl:message name="getLastTradePricesResponse"> <wsdl:part name="getLastTradePricesReturn" type="impl:ArrayOfTradePrice"/> </wsdl:message> <wsdl:message name="getLastTradePricesRequest"> <wsdl:part name="in0" type="impl:ArrayOf_xsd_string"/> </wsdl:message> <wsdl:message name="IncorrectTickerSymbolException"> <wsdl:part name="fault" type="impl:IncorrectTickerSymbolException"/> </wsdl:message> <wsdl:portType name="StockQuoteProvider"> <wsdl:operation name="getLastTradePrices" parameterOrder="in0"> <wsdl:input message="impl:getLastTradePricesRequest" name="getLastTradePricesRequest"/> <wsdl:output message="impl:getLastTradePricesResponse" name="getLastTradePricesResponse"/> <wsdl:fault message="impl:IncorrectTickerSymbolException" name="IncorrectTickerSymbolException"/> </wsdl:operation> </wsdl:portType> Definición de mensajes y puertos – Comentarios (1) Definición de mensajes Especifica los mensajes que se pueden intercambiar clientes y servidores Cada mensaje consta de “partes”, donde cada parte especifica un parámetro del mensaje, un valor de retorno o una excepción (fault) Definición de tipos de puertos Un tipo de puerto especifica un conjunto de operaciones Cada operación especifica el orden de los parámetros, el mensaje de entrada (input), el de salida (output) y los posibles mensajes fault que puede devolver la operación Un mensaje fault sólo puede contener una parte Definición de mensajes y puertos – Comentarios (y 2) Definición de tipos de puertos (cont) Tipos de parámetros In: parámetro que sólo aparece en un mensaje de entrada Out: parámetro que sólo aparece en un mensaje de salida Inout: parámetro que aparece en un mensaje de entrada y salida Valor de retorno Parte que no es parámetro ni excepción StockQuoteProvider.wsdl (4) <wsdl:binding name="StockQuoteProviderSoapBinding" type="impl:StockQuoteProvider"> <wsdlsoap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/> <wsdl:operation name="getLastTradePrices"> <wsdlsoap:operation soapAction=""/> <wsdl:input name="getLastTradePricesRequest"> <wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://ws.udc.es/jaxrpctutorial" use="encoded"/> </wsdl:input> <wsdl:output name="getLastTradePricesResponse"> <wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://ws.udc.es/jaxrpctutorial" use="encoded"/> </wsdl:output> <wsdl:fault name="IncorrectTickerSymbolException"> <wsdlsoap:fault encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" name="IncorrectTickerSymbolException" namespace="http://ws.udc.es/jaxrpctutorial" use="encoded"/> </wsdl:fault> </wsdl:operation> </wsdl:binding> StockQuoteProvider.wsdl (y 5) <wsdl:service name="StockQuoteProviderService"> <wsdl:port binding="impl:StockQuoteProviderSoapBinding" name="StockQuoteProvider"> <wsdlsoap:address location="http://localhost:8080/ws-jaxrpctutorialservice/services/StockQuoteProvider"/> </wsdl:port> </wsdl:service> </wsdl:definitions> Definición de bindings y servicios – Comentarios Definición de bindings Definición de servicios Un binding especifica un protocolo y formato de datos para un tipo de puerto (e.g. SOAP sobre HTTP) Un servicio especifica un conjunto de “puertos” (endpoints) Cada puerto está asociado a un binding particular y especifica su dirección de contacto En JAX-RPC Se usa el término “service endpoint” para referirse al puerto de un servicio Web Se usa el término “interfaz del service endpoint” para referirse al interfaz del puerto Usaremos indistintamente los términos “service endpoint” y “puerto” Mapping de Java a WSDL (1) Tipos válidos JAX-RPC: tipos que se pueden emplear en la definición de interfaces remotos Tipos primitivos y sus contrapartidas objetuales Clases estándar Tipos valor JAX-RPC Arrays ([]) de tipos válidos Mapping de Java a WSDL (2) Tipos primitivos y sus contrapartidas objetuales Tipo Java boolean Tipo WSDL xsd:boolean byte xsd:byte short xsd:short int xsd:int long xsd:long float xsd:float double xsd:double En el caso de las contrapartidas objetuales, el correspondiente elemento lleva el atributo nillable a true Mapping de Java a WSDL (3) Clases estándar Tipo Java Tipo WSDL java.lang.String xsd:string java.math.BigInteger xsd:integer java.math.BigDecimal xsd:decimal java.util.Calendar xsd:dateTime java.util.Date xsd:dateTime Arrays ([]) de tipos válidos Se mapean a tipos complejos derivados por restricción de un array SOAP, a excepción de byte[] (xsd:base64Binary) Mapping de Java a WSDL (4) Tipos valor JAX-RPC En general, estas clases deben tener Pueden heredar de otras clases valor Se mapean a tipos WSDL complejos con compositor all o sequence Un constructor público sin argumentos Atributos públicos de tipos válidos o usar las convenciones de nombrado de JavaBeans para sus atributos (métodos getXXX y setXXX) En caso de herencia, el tipo complejo se define por derivación Es buena práctica que implementen java.io.Serializable (interfaz marker) Ejemplo: TradePrice Mapping de Java a WSDL (5) Definición de interfaces remotos (“interfaces de service endpoints”) Extienden java.rmi.Remote (interfaz marker) Todas las operaciones deben declarar java.rmi.RemoteException Cada interfaz se mapea a un puerto Dado que en WSDL no existe herencia entre puertos, si un interfaz deriva de otro, el puerto hijo incluye todas las operaciones del padre Una operación no puede recibir como parámetro o devolver como valor de retorno una referencia a un interfaz remoto Actualmente SOAP no ofrece soporte para ello Ejemplo: StockQuoteProvider Mapping de Java a WSDL (6) Excepciones java.rmi.RemoteException se mapea a un fault de SOAP Las excepciones específicas al puerto, es decir, las que extienden directa o indirectamente de java.lang.Exception (pero no de java.lang.RuntimeException) se mapean a un wsdl:fault Definen un método getXXX para recuperar el valor de cada propiedad Disponen de un constructor que recibe las propiedades como parámetros Ejemplo: IncorrectTickerSymbolException La herencia de excepciones se mapea a herencia de tipos complejos Mapping de Java a WSDL (7) Otros tipos Si se desean usar otros tipos para los que JAX-RPC no tiene soporte directo (e.g. implementaciones de java.util.Collection), JAX-RPC permite implementar clases serializadoras y deserializadoras Serializador Deserializador Convierte el valor de un tipo Java a XML Convierte el valor de un tipo XML a Java Algunas implementaciones de JAX-RPC proporcionan serializadores/deserializadores para clases estándar usuales Ejemplo: Axis proporciona clases serializadoras/deserializadoras para algunas de las implementaciones de java.util.Collection Mapping de Java a WSDL (y 8) Interoperabilidad El uso de clases serializadoras/deserializadoras puede causar problemas de interoperabilidad Actualmente no hay un formato estándar en SOAP para transmitir listas, mapas, etc. Para máxima interoperabilidad es mejor restringirse a los tipos directamente soportados Con el uso de arrays ([]) y tipos valor JAX-RPC se pueden representar estructuras complejas, que se mapean de forma estándar a tipos WSDL Esta es la técnica usada en todos los ejemplos Mapping de WSDL a Java (1) Las reglas del mapping de Java a WSDL a la inversa xsd:dateTime se mapea a java.util.Calendar (y no a java.util.Date) Los structs XML (con compositor all o sequence) se mapean a una clase Java con métodos getXXX/setXXX para cada campo del struct Además, necesitamos saber ¿Cómo se traducen las enumeraciones? ¿Cómo se traducen los parámetros out e inout? También necesitamos conocer algunas clases generadas que son específicas al cliente o al servidor Las estudiamos como parte del modelo de implementación de clientes y servidores Mapping de WSDL a Java (2) Enumeraciones // WSDL <simpleType name="EyeColor"> <restriction base="xsd:string"> <enumeration value="green"/> <enumeration value="blue"/> </restriction> </simpleType> Mapping de WSDL a Java (3) Enumeraciones (cont) // Java public class EyeColor { public public public public static static static static final final final final String _green ="green"; String _blue = "blue"; EyeColor green = new EyeColor(_green); EyeColor blue = new EyeColor(_blue); protected EyeColor(String value) { ... } public String getValue() { ... } public static EyeColor fromValue(String value) { ... } public boolean equals(Object obj) { ... } public int hashCode() { ... } // Otros métodos ... } Mapping de WSDL a Java (4) Parámetros out e inout Uso de clases Holder Son necesarias porque Java no tiene soporte directo (palabras reservadas en el lenguaje) para parámetros in/inout Son clases que contienen un valor, accesible mediante el atributo público value Ejemplo Supongamos un endpoint con la operación Nombre: add Parámetros: increment (in, double), amount (inout, double) Sin tipo de retorno La operación se mapearía a Java como void add(double increment, javax.xml.rpc.holders.DoubleHolder amount) throws java.rmi.RemoteException; Uso DoubleHolder amount = new DoubleHolder(20); puerto.add(10, amount); System.out.println(amount.value); // 30 Mapping de WSDL a Java (y 5) Parámetros out e inout (cont) Existen clases Holder para los tipos WSDL predefinidos en el paquete javax.xml.rpc.holders Ejemplo: DoubleHolder Para los tipos definidos por el programador, el compilador de WSDL genera clases Holder con el formato final public class <XXX>Holder implements javax.xml.rpc.holders.Holder { public <XXX> value; public <XXX>Holder() { ... } public <XXX>Holder(<XXX> value) { ... } } Modelos de implementación de clientes Modelo basado en Java SE Es el que usaremos Modelo basado en Java EE El cliente corre dentro de un servidor de aplicaciones Java EE con soporte para JAX-RPC Si el servidor de aplicaciones Java EE no tiene soporte nativo para JAX-RPC (e.g. Tomcat), hay que usar el modelo basado en Java SE es.udc.ws.jaxrpctutorial.client.Client (1) package es.udc.ws.jaxrpctutorial.client; import javax.xml.rpc.Stub; import import import import import es.udc.ws.jaxrpctutorial.wsdl.TradePrice; es.udc.ws.jaxrpctutorial.wsdl.StockQuoteProvider; es.udc.ws.jaxrpctutorial.wsdl.StockQuoteProviderService; es.udc.ws.jaxrpctutorial.wsdl.StockQuoteProviderServiceLocator; es.udc.ws.jaxrpctutorial.wsdl.IncorrectTickerSymbolException; class Client { public static void main (String args[]) { es.udc.ws.jaxrpctutorial.client.Client (2) try { /* Check arguments. */ if (args.length < 1) { System.err.println("Usage: " + Client.class.getName() + " stockQuoteProviderURL" + " [tickerSymbol1 tickerSymbol2 ...]"); System.exit(-1); } /* Get argument values. */ String stockQuoteProviderURL = args[0]; String[] tickerSymbols = new String[args.length-1]; for (int i=0; i<tickerSymbols.length; i++) { tickerSymbols[i] = args[i+1]; } /* Construct an instance of the port proxy. */ StockQuoteProviderService stockQuoteProviderService = new StockQuoteProviderServiceLocator(); StockQuoteProvider stockQuoteProvider = stockQuoteProviderService.getStockQuoteProvider(); ((Stub)stockQuoteProvider)._setProperty( Stub.ENDPOINT_ADDRESS_PROPERTY, stockQuoteProviderURL); es.udc.ws.jaxrpctutorial.client.Client (y 3) /* Gest last trade prices. */ TradePrice[] tradePrices = stockQuoteProvider.getLastTradePrices(tickerSymbols); /* Print last trade prices. */ for (int i=0; i<tradePrices.length; i++) { System.out.println("Ticker symbol = " + tradePrices[i].getTickerSymbol() + " | " + "Price = " + tradePrices[i].getPrice() + " | " + "Elapsed seconds = " + tradePrices[i].getElapsedSeconds()); } } catch (IncorrectTickerSymbolException e) { System.err.println("Incorrect ticker symbol: " + e.getIncorrectTickerSymbol()); } catch (Exception e) { e.printStackTrace(); } } } Comentarios (1) StockQuoteProviderService Interfaz del servicio Generada por el compilador de WSDL a Java Su nombre coincide con el nombre del servicio declarado en el fichero WSDL Proporciona métodos get<PortType> Devuelven una instancia del stub/proxy del puerto En JAX-RPC sólo está estandarizado un método get sin parámetros por cada puerto En el caso de Axis, el proxy del puerto utiliza por defecto la URL declarada en el fichero WSDL El proxy implementa el interfaz del puerto (StockQuoteProvider en el ejemplo, generado por el compilador de WSDL a Java) y el interfaz javax.xml.rpc.Stub El ejemplo utiliza el método _setProperty del interfaz javax.xml.rpc.Stub para que se utilice la URL del puerto que se pasa como primer argumento de la aplicación Comentarios (2) StockQuoteProviderServiceLocator Clase concreta generada por el compilador de WSDL a Java Es específica de Axis Implementa el interfaz StockQuoteProviderService Invocación JAXRPCTutorialClient.sh http://.../ws-jaxrpctutorial-service/services/StockQuoteProvider IBM SUN MIC Comentarios (3) Clientes Java EE Un cliente (e.g. una aplicación Web) que corra dentro de un servidor de aplicaciones Java EE con soporte para JAX-RPC puede obtener una referencia al servicio de una manera estándar y obtener una instancia de un proxy de un puerto de una manera más sencilla Se usa JNDI (Java Naming and Directory Interface) API incluida en Java SE (javax.naming) Entre otras cosas, es un API que permite acceder a información de configuración y recursos externos Ejemplo: Context initialContext = new InitialContext(); StockQuoteProviderService stockQuoteProviderService = (StockQuoteProviderService) initialContext.lookup( "java:comp/env/service/StockQuoteProviderService"); StockQuoteProvider stockQuoteProvider = stockQuoteProviderService.getStockQuoteProvider(); Comentarios (y 4) Clientes Java EE (cont) Tiene que declarar en sus ficheros de configuración (web.xml o ejb-jar.xml) las referencias a los servicios Web que usa <service-ref> <service-ref-name>service/StockQuoteProviderService </service-ref-name> <service-interface>es.udc.ws.jaxrpctutorial.wsdl. StockQuoteProviderService</service-interface> </service-ref> Las referencias se pueden localizar por JNDI en el contexto java:comp/env Se recomienda declarar las referencias a servicios Web debajo del subcontexto service Requiere configuración específica en el servidor de aplicaciones (e.g. especificar las URLs de contacto de los puertos) Modelo de implementación de servicios Modelo basado en un servidor de aplicaciones Java EE con soporte para el API de Servlets Una aplicación Web puede incluir uno o varios servicios Web SOAP Es el modelo que estudiaremos SOAP/HTTP war Cliente Aplicaciones Web (.war) Servidor de aplicaciones Modelo basado en un servidor de aplicaciones Java EE con soporte para el API de EJB El servicio Web SOAP se implementa como un Stateless Sesion Bean, cuya interfaz remota es la del servicio Web SOAP/HTTP EJB Cliente Componentes EJB (.jar) Servidor de aplicaciones Modelo basado en servidor de aplicaciones con soporte para Servlets (1) Requisitos de la clase de implementación Implementa el interfaz remoto Ofrece un constructor público sin argumentos En Axis, por defecto El nombre de la clase de implementación es XXXSoapBindingImpl, siendo XXX el nombre del interfaz remoto Ejemplo: StockQuoteProviderSoapBindingImpl El compilador de WSDL a Java genera la clase automáticamente (si no existía) en el paquete en el que se le especificó que generase el código es.udc.ws.jaxrpctutorial.wsdl en el ejemplo Modelo basado en servidor de aplicaciones con soporte para Servlets (2) Modelo de ejecución La implementación de JAX-RPC tiene que incluir un servlet que Recibe las peticiones SOAP sobre HTTP que envían los clientes Invoca la operación correspondiente sobre el servicio Web NOTA A LA FIGURA: en Axis 1.3+ el compilador de WSDL a Java no genera una clase Skeleton (el servlet utiliza clases genéricas que realizan esa misma función) Devuelve una respuesta SOAP sobre HTTP con el resultado de la operación SOAP/HTTP Cliente Servlet Skeleton Implementación del servicio Servidor de aplicaciones Modelo basado en servidor de aplicaciones con soporte para Servlets (3) Modelo de ejecución (cont) El servlet crea un pool (conjunto) de instancias de la clase de implementación Las instancias no pueden mantener estado específico para el cliente Pero pueden tener estado global, es decir, atributos típicamente inicializados en la implementación del método init del interfaz ServiceLifecycle (siguiente transparencia) y que optimizan la ejecución de las operaciones del servicio (e.g. cachés de sólo lectura, una conexión a una BD, etc.) Cuidado con el estado global en entornos cluster (e.g. no usar atributos modificables que afecten a la funcionalidad del servicio) Cada vez que llega una petición, el servlet delega en una instancia que no se esté utilizando en ese momento Dada que el modelo de ejecución de servlets es multi-thread, se pueden atender múltiples peticiones SOAP concurrentemente (cada petición utiliza su propia instancia) Modelo basado en servidor de aplicaciones con soporte para Servlets (y 4) Ciclo de vida La clase de implementación puede implementar opcionalmente el interfaz (del paquete javax.xml.rpc.server) public interface ServiceLifecycle { void init(Object context) throws javax.xml.rpc.ServiceException; void destroy(); } Cada vez que el servlet crea una instancia de la clase de implementación, tiene que invocar a init En el caso de un servidor de aplicaciones con soporte para el API de Servlets, el contexto pasado es de tipo javax.xml.rpc.server.ServletEndpointContext, y proporciona métodos para acceder a aspectos tales como la sesión, el ServletContext, etc. Cada vez que el servlet decide destruir una instancia de la clase de implementación, tiene que invocar a destroy es.udc.ws.jaxrpctutorial.wsdl.StockQuoteProviderSoapBindingImpl (1) package es.udc.ws.jaxrpctutorial.wsdl; import javax.xml.rpc.server.ServiceLifecycle; // ... public class StockQuoteProviderSoapBindingImpl implements StockQuoteProvider, ServiceLifecycle { private Map<String, TradePrice> tradePrices; public void init(Object context) throws ServiceException { TradePrice ibmTradePrice = new TradePrice(); ibmTradePrice.setTickerSymbol("IBM"); ibmTradePrice.setPrice(10.5); ibmTradePrice.setElapsedSeconds(60*20); // ... tradePrices = new HashMap<String, TradePrice>(); tradePrices.put(ibmTradePrice.getTickerSymbol(), ibmTradePrice); // ... } public void destroy() {} es.udc.ws.jaxrpctutorial.wsdl.StockQuoteProviderSoapBindingImpl (y 2) public TradePrice[] getLastTradePrices(String[] tickerSymbols) throws IncorrectTickerSymbolException { List requestedTradePrices = new ArrayList(); for (int i=0; i<tickerSymbols.length; i++) { TradePrice tradePrice = tradePrices.get(tickerSymbols[i]); if (tradePrice == null) { throw new IncorrectTickerSymbolException( tickerSymbols[i]); } requestedTradePrices.add(tradePrice); } return (TradePrice[]) requestedTradePrices.toArray( new TradePrice[0]); } } jar tvf StockQuote.war WEB-INF/lib/<< librerías Apache Axis >> WEB-INF/classes/es/udc/ws/jaxrpctutorial/wsdl/ StockQuoteProvider.class WEB-INF/classes/es/udc/ws/jaxrpctutorial/wsdl/ IncorrectTickerSymbolException.class WEB-INF/classes/es/udc/ws/jaxrpctutorial/wsdl/ TradePrice.class WEB-INF/classes/es/udc/ws/jaxrpctutorial/wsdl/ StockQuoteProviderService.class WEB-INF/classes/es/udc/ws/jaxrpctutorial/wsdl/ StockQuoteProviderServiceLocator.class WEB-INF/classes/es/udc/ws/jaxrpctutorial/wsdl/ StockQuoteProviderSoapBindingStub.class WEB-INF/classes/es/udc/ws/jaxrpctutorial/wsdl /StockQuoteProviderSoapBindingImpl.class WEB-INF/web.xml Comentarios WEB-INF/lib Incluye las librerías (ficheros .jar) necesarias de Axis WEB-INF/classes Contiene las clases requeridas por el servicio (paquete es.udc.ws.jaxrpctutorial.wsdl) Excepto StockQuoteProviderSoapBindingImpl, todas las clases fueron generadas por el compilador de WSDL a Java a partir de la definición del servicio (es.udc.ws.jaxrpctutorial.servicedef) Por sencillez, el fichero WAR incluye StockQuoteProviderSoapBindingStub (el stub) No sería necesario, dado que el stub sólo es de utilidad para un cliente Java web.xml (1) <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4"> <distributable/> <display-name>JAXRPCTutorial Web Service</display-name> <servlet> <display-name>AxisServlet</display-name> <servlet-name>AxisServlet</servlet-name> <servlet-class> org.apache.axis.transport.http.AxisServlet </servlet-class> </servlet> web.xml (y 2) <servlet-mapping> <servlet-name>AxisServlet</servlet-name> <url-pattern>/services/*</url-pattern> </servlet-mapping> </web-app> Comentarios (1) Se declara el servlet AxisServlet Forma parte de las librerías de Axis (WEB-INF/lib) AxisServlet El servidor de aplicaciones Web le pasará todas las peticiones (tag servlet-mapping) dirigidas a las URLs http://.../NombreAplicacionWeb/services/* <servlet-mapping> <servlet-name>AxisServlet</servlet-name> <url-pattern>/services/*</url-pattern> </servlet-mapping> En este caso, asumiendo que instalemos la aplicación Web con el nombre ws-jaxrpctutorial-service, el cliente usará la URL http://.../ws-jaxrpctutorialservice/services/StockQuoteProvider para acceder al puerto StockQuoteProvider (es la URL que aparece en el documento WSDL) Comentarios (y 2) AxisServlet (cont) El servlet invocará la operación correspondiente sobre el service endpoint al que va dirigida la petición, y finalmente enviará una respuesta SOAP con el resultado de la operación TCPMonitor (1) Axis incluye una herramienta que permite monitorizar las peticiones y respuestas SOAP que envían clientes y servidores Actúa como un “túnel” Recibe las peticiones del cliente, las muestra en pantalla y las redirige al service endpoint Recibe las respuestas del service endpoint, las muestra en pantalla y se las envía al cliente TCPMonitor (2) JAXRPCTutorialClient.sh http://localhost:8000/ws-jaxrpctutorial-service/services/StockQuoteProvider IBM SUN MIC 4: Respuesta SOAP 1: Petición SOAP 2: Petición SOAP 3: Respuesta SOAP TCPMonitor.sh 8000 localhost 8080 (escucha por el puerto 8000 y redirige a localhost:8080) <Tomcat_Home>/bin/startup.sh (por defecto escucha por el puerto 8080) TCPMonitor (y 3) Comentarios El stub del cliente envía una petición SOAP para invocar la operación getLastTradePrices sobre el endpoint StockQuoteProvider La petición llega a TCPMonitor Determina que el cliente desea invocar la operación getLastTradePrices sobre StockQuoteProvider Convierte el valor del parámetro de la operación a String[] Invoca la operación Convierte el valor de retorno (TradePrice[]) de la operación a XML Envía una respuesta SOAP La respuesta llega a TCPMonitor La reenvía al servicio AxisServlet recibe la petición SOAP Convierte el valor del parámetro (String[]) de la operación a XML La reenvía al cliente El stub recibe la respuesta SOAP Convierte el valor de retorno a TradePrice[]