3.4 JAX-WS Índice Introducción a SOAP JAX-WS 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 Envelope 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 desde interfaces o clases de implementación del servicio 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 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-WS ¿Qué es JAX-WS? (1) JAX-WS es un API estándar en Java para implementar e invocar Servicios Web. Forma parte del API de Java SE. Usa anotaciones para facilitar el desarrollo. Permite invocar e implementar operaciones de servicios Web SOAP mediante el paradigma de RPC. Permite también operaciones asíncronas. En lugar de quedarse bloqueado esperando por respuesta,s e proporciona una operación de callback. Permite también cierto soporte para REST (aunque no es el objetivo principal). Como cualquier otro API de Java, está formada por un conjunto de interfaces Existen múltiples implementaciones (Metro, CXF, Axis 2,…) El código escrito por el desarrollador no depende de la implementación concreta de JAX-WS que se utilice Nosotros utilizaremos Metro. ¿Qué es JAX-WS? (2) Especifica un mapping de WSDL a Java Especifica un mapping de Java a WSDL Permite que las implementaciones de JAX-WS proporcionen un compilador de WSDL a Java, que genere stubs (proxies) para invocar servicios Web. Permite que las implementaciones de JAX-WS proporcionen un compilador de Java a WSDL, que genere el documento WSDL correspondiente a un interfaz o clase de implementación Java La definición del interfaz o clase de implementación Java está sujeta a ciertas restricciones El documento WSDL permite que un cliente (escrito sobre cualquier plataforma) pueda invocar el servicio Web Los mappings están basados en JAXB. ¿Qué es JAX-WS? (y 3) Proceso de desarrollo Interfaz Java Documento WSDL Compilador Java Compilador WSDL a Java Compilador WSDL Documento WSDL Stubs, skeletons y tipos Java ¿Qué es Metro? Una toolkit para construir Servicios Web que incluye la implementación de referencia de JAX-WS. A diferencia de otras implementaciones de referencia, es apta para producción) Permite implementar servicios SOAP en un servidor de aplicaciones Java EE Incluye http://metro.dev.java.net/ Un conjunto de librerías Un compilador de Java a WSDL Un compilador de WSDL a Java Otras implementaciones de JAX-WS: Axis 2 (http://ws.apache.org/axis2/) CXF (http://cxf.apache.org/) 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-WS Se define y se implementa el interfaz de un servicio Web Se invoca un servicio Web desde un cliente Definición de la interfaz del servicio Web (1) Puede definirse explícitamente o puede dejarse que JAX-WS la obtenga automáticamente desde una clase anotada de implementación del servicio. En el ejemplo hemos seguido la segunda aproximación. Definida en es.udc.ws.jaxwstutorial.service El paquete incluye StockQuoteProviderImpl: la implementación del servicio. Define la operación getLastTradePrices TradePrice: la clase que modela una cotización La operación getLastTradePrices devuelve los TradePrice correspondientes a un conjunto de identificadores de valores bursátiles. IncorrectTickerSymbolException La operación getLastTradePrices lanza 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.jaxwstutorial.service Utilizar el compilador de Java a WSDL sobre StockQuoteProviderImpl Genera StockQuoteProviderService.wsdl Usar el compilador de WSDL a Java sobre StockQuoteProviderService.wsdl El código generado incluirá Stub, y clases auxiliares, y Otra vez StockQuoteProvider, TradePrice e IncorrectTickerSymbolException (incluyen atributos y operaciones adicionales que necesitan el stub y el skeleton) El cliente del servicio Web usará estos tipos (paquete es.udc.ws.jaxwstutorial.wsdl) 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 Debe tener un constructor público. Debe estar anotada con javax.jws.WebService name: nombre del tipo de puerto en wsdl. serviceName: nombre del servicio en wsdl. targetNamespace. Espacio de nombres en wsdl. endpointInterface. Si la interfaz se define separadamente, se indica en esta propiedad. Las operaciones del servicio deben ser públicas, usar tipos compatibles JAXB y declarar javax.jws.WebMethod. name: nombre de la operación en wsdl exclude: valor booleano. Si true, la operación no se incluye en el wsdl. Las excepciones lanzadas deben usar la anotación javax.xml.ws.WebFault name: nombre en wsdl. targetNamespace. Espacio de nombres en wsdl. Modelo basado en servidor de aplicaciones con soporte para Servlets (2) Modelo de ejecución La implementación de JAX-WS 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 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 Por defecto, las instancias no pueden mantener estado específico para el cliente Pero pueden tener estado global, es decir, atributos típicamente inicializados al crear el servicio 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 Las anotaciones javax.annotation.PostConstruct y import javax.annotation.PreDestroy permiten marcar métodos que se ejecutarán respectivamente tras la inicialización y antes de la destrucción de una instancia. es.udc.ws.jaxwstutorial.service.StockQuoteProviderImpl (1) package es.udc.ws.jaxwstutorial.service; import import import import import import import javax.jws.WebMethod; javax.jws.WebService; javax.annotation.PostConstruct; java.util.ArrayList; java.util.HashMap; java.util.List; java.util.Map; @WebService( name="StockQuoteProvider", serviceName="StockQuoteProviderService", targetNamespace="http://ws.adoo.udc.es/" ) public class StockQuoteProviderImpl { private Map<String, TradePrice> tradePrices; es.udc.ws.jaxwstutorial.service.StockQuoteProviderImpl (2) @PostConstruct private void init() { TradePrice ibmTradePrice = new TradePrice(); ibmTradePrice.setTickerSymbol("IBM"); ibmTradePrice.setPrice(10.5); ibmTradePrice.setElapsedSeconds(60*20); TradePrice sunTradePrice = new TradePrice(); sunTradePrice.setTickerSymbol("SUN"); sunTradePrice.setPrice(9.1); sunTradePrice.setElapsedSeconds(60*10); TradePrice micTradePrice = new TradePrice(); micTradePrice.setTickerSymbol("MIC"); micTradePrice.setPrice(20.3); micTradePrice.setElapsedSeconds(60*5); tradePrices = new HashMap<String, TradePrice>(); tradePrices.put(ibmTradePrice.getTickerSymbol(),ibmTradePrice); tradePrices.put(sunTradePrice.getTickerSymbol(),sunTradePrice); tradePrices.put(micTradePrice.getTickerSymbol(),micTradePrice); } es.udc.ws.jaxwstutorial.service.StockQuoteProviderImpl (3) public StockQuoteProviderImpl() { } @WebMethod( operationName="getLastTradePrices" ) public List<TradePrice> getLastTradePrices(List<String> tickerSymbols) throws IncorrectTickerSymbolException { List requestedTradePrices = new ArrayList(); System.out.println("Requested " + tickerSymbols.size() + " ticker simbol(s)"); for (int i=0; i<tickerSymbols.size(); i++) { TradePrice tradePrice= tradePrices.get(tickerSymbols.get(i)); es.udc.ws.jaxwstutorial.service.StockQuoteProviderImpl (y 4) if (tradePrice == null) { IncorrectTickerSymbolExceptionInfo info = new IncorrectTickerSymbolExceptionInfo(); info.setIncorrectTickerSymbol(tickerSymbols.get(i)); throw new IncorrectTickerSymbolException(info); } requestedTradePrices.add(tradePrice); } System.out.println("Successfully resolved " + requestedTradePrices.size() + " ticker simbol(s)"); return requestedTradePrices; } } es.udc.ws.jaxwstutorial.servicedef.TradePrice (1) package es.udc.ws.jaxwstutorial.service; public class TradePrice { 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.jaxwstutorial.service.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.jaxwstutorial.service.IncorrectTickerSymbolException package es.udc.ws.jaxwstutorial.service; import javax.xml.ws.WebFault; @WebFault(name = "IncorrectTickerSymbolException", targetNamespace = "http://ws.adoo.udc.es/") public class IncorrectTickerSymbolException extends Exception { private IncorrectTickerSymbolExceptionInfo exceptionInfo; public IncorrectTickerSymbolException(IncorrectTickerSymbolExceptionInfo exceptionInfo) { this.exceptionInfo = exceptionInfo; } public IncorrectTickerSymbolExceptionInfo getFaultInfo() { return exceptionInfo; } } es.udc.ws.jaxwstutorial.service.IncorrectTickerSymbolExceptionInfo package es.udc.ws.jaxwstutorial.service; public class IncorrectTickerSymbolExceptionInfo { protected String incorrectTickerSymbol; public String getIncorrectTickerSymbol() { return incorrectTickerSymbol; } public void setIncorrectTickerSymbol(String value) { this.incorrectTickerSymbol = value; } } Comentarios Por la forma en la que JAX-WS define el mapping de Exceptions java a WSDL y viceversa, es conveniente que las Excepciones utilizadas cumplan las siguientes reglas: La información encapsulada en la Excepción debe ir en un objeto separado IncorrectTickerSymbolExceptionInfo Este objeto debe ser obtenido a través del método getFaultInfo. De esta forma, las excepciones lanzadas por los stubs del cliente serán iguales que las usadas en el servicio. En algunas toolkits esto no es necesario. Visión global de WSDL Dejaremos momentáneamente la explicación de los detalles de la definición del interfaz remoto StockQuoteProviderService 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 StockQuoteProviderService.wsdl (1) <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <definitions targetNamespace="http://ws.adoo.udc.es/" name="StockQuoteProviderService" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://ws.adoo.udc.es/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"> <types> <xsd:schema> <xsd:import namespace="http://ws.adoo.udc.es/" schemaLocation="StockQuoteProviderService_schema1.xsd"/> </xsd:schema> </types> StockQuote…schema1.xsd (1) <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <xs:schema version="1.0" targetNamespace="http://ws.adoo.udc.es/" xmlns:tns="http://ws.adoo.udc.es/" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="IncorrectTickerSymbolException" nillable="true" type="tns:incorrectTickerSymbolExceptionInfo"/> <xs:element name="getLastTradePrices" type="tns:getLastTradePrices"/> <xs:element name="getLastTradePricesResponse" type="tns:getLastTradePricesResponse"/> <xs:complexType name="getLastTradePrices"> <xs:sequence> <xs:element name="arg0" type="xs:string" minOccurs="0" maxOccurs="unbounded"/> </xs:sequence> </xs:complexType> <xs:complexType name="getLastTradePricesResponse"> <xs:sequence> <xs:element name="return" type="tns:tradePrice" minOccurs="0" maxOccurs="unbounded"/> </xs:sequence> </xs:complexType> StockQuote…schema1.xsd (y 2) <xs:complexType name="tradePrice"> <xs:sequence> <xs:element name="elapsedSeconds" type="xs:int"/> <xs:element name="price" type="xs:double"/> <xs:element name="tickerSymbol" type="xs:string" minOccurs="0"/> </xs:sequence> </xs:complexType> <xs:complexType name="incorrectTickerSymbolExceptionInfo"> <xs:sequence> <xs:element name="incorrectTickerSymbol" type="xs:string" minOccurs="0"/> </xs:sequence> </xs:complexType> </xs:schema> 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 También se pueden usar tipos SOAP. Metro genera las definiciones de tipos en un fichero aparte (no es necesario hacerlo así). En el ejemplo se definen El tipo complejo getLastTradePrices, que corresponde al tipo Java de entrada a la operación List<String> Se define como una secuencia de elementos xsd:string Los tipos complejos TradePrice e IncorrectTickerSymbolException que corresponden a los tipos Java con el mismo nombre. getLastTradePricesResponse representa el tipo de la respuesta de la operación List<TradePrice>. nillable=true especifica que el correspondiente elemento puede tomar el valor nil (null en Java) StockQuoteProviderService.wsdl (2) <message name="getLastTradePrices"> <part name="parameters" element="tns:getLastTradePrices"/> </message> <message name="getLastTradePricesResponse"> <part name="parameters" element="tns:getLastTradePricesResponse"/> </message> <message name="IncorrectTickerSymbolException"> <part name="fault" element="tns:IncorrectTickerSymbolException"/> </message> <portType name="StockQuoteProvider"> <operation name="getLastTradePrices"> <input message="tns:getLastTradePrices"/> <output message="tns:getLastTradePricesResponse"/> <fault message="tns:IncorrectTickerSymbolException" name="IncorrectTickerSymbolException"/> </operation> </portType> Definición de mensajes y puertos – Comentarios 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 StockQuoteProviderService.wsdl (y 3) <binding name="StockQuoteProviderPortBinding" type="tns:StockQuoteProvider"> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/> <operation name="getLastTradePrices"> <soap:operation soapAction=""/> <input> <soap:body use="literal"/> </input> <output> <soap:body use="literal"/> </output> <fault name="IncorrectTickerSymbolException"> <soap:fault name="IncorrectTickerSymbolException" use="literal"/> </fault> </operation> </binding> <service name="StockQuoteProviderService"> <port name="StockQuoteProviderPort" binding="tns:StockQuoteProviderPortBinding"> <soap:address location="REPLACE_WITH_ACTUAL_URL"/> </port> </service> </definitions> Definición de bindings y servicios – Comentarios Definición de bindings Un binding especifica un protocolo y formato de datos para un tipo de puerto (e.g. SOAP sobre HTTP) Definición de servicios Un servicio especifica un conjunto de “puertos” (endpoints) Cada puerto está asociado a un binding particular y especifica su dirección de contacto REPLACE_WITH_ACTUAL_URL. Ahí debe indicarse la URL por defecto de acceso al servicio. También puede indicarse al generar los stubs. En JAX-WS Se usa el término “service endpoint” para referirse al puerto de un servicio Web Usaremos indistintamente los términos “service endpoint” y “puerto” Mapping de Java a WSDL (1) No todos los tipos se pueden emplear en la definición de las operaciones de los Servicios Web. Tipos primitivos y sus contrapartidas objetuales Clases estándar Clases que sigan las convenciones de “bean”. Colecciones de tipos válidos Se utilizan los mappings de JAXB (Java Architecture for XML Binding). Define unos mappings por defecto. Pueden adaptarse mediante anotaciones. 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 Colecciones (e.g. Listas) de tipos válidos Se mapean a tipos complejos que son secuencias del tipo base. Mapping de Java a WSDL (4) Objetos tipo “bean”: En general, estas clases deben tener atributos públicos de tipos válidos o usar las convenciones de nombrado de JavaBeans para sus atributos (métodos getXXX y setXXX) Pueden heredar de otras clases valor Se mapean a tipos WSDL complejos con compositor all o sequence En caso de herencia, el tipo complejo se define por derivación Ejemplo: TradePrice Mapping de Java a WSDL (5) Definición de operaciones remotas Utilizan las anotaciones @WebService y @WebMethod tal y como ya hemos comentado. 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: StockQuoteProviderImpl Mapping de Java a WSDL (y 6) Excepciones Las excepciones se mapean a un wsdl:fault El objeto que contiene la información de la excepción se mapea a un tipo complejo. Dicho objeto ser accede mediante la operación getFaultInfo. Ejemplo: IncorrectTickerSymbolException Mapping de WSDL a Java (1) Las reglas del mapping de Java a WSDL a la inversa xsd:dateTime se mapea a javax.xml.datatype.XMLGregorianCalendar (y no a java.util.Calendar) 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 como se mapean algunas construcciones no existentes en JAVA (nuevamente, se usan las normas de JAXB): ¿Cómo se traducen los parámetros out e inout? Parámetros de salida y de entrada/salida disponibles en otros lenguajes de programación. Mapping de WSDL a Java (y 2) 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, Holder<Double> amount) Uso Holder<Double> amount = new Holder(20); puerto.add(10, amount); System.out.println(amount.value); // 30 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-WS. es.udc.ws.jaxwstutorial.client.StockQuoteProviderClient (1) package es.udc.ws.jaxwstutorial.client; import import import import es.udc.ws.jaxwstutorial.wsdl.IncorrectTickerSymbolException; es.udc.ws.jaxwstutorial.wsdl.StockQuoteProvider; es.udc.ws.jaxwstutorial.wsdl.StockQuoteProviderService; es.udc.ws.jaxwstutorial.wsdl.TradePrice; import import import import import javax.xml.ws.BindingProvider; javax.xml.ws.WebServiceRef; java.util.ArrayList; java.util.List; java.text.MessageFormat; public class StockQuoteProviderClient { public static StockQuoteProviderService stockQuoteProviderService = new StockQuoteProviderService(); public static void main (String args[]) { es.udc.ws.jaxwstutorial.client.StockQuoteProviderClient (2) try { if (args.length < 2) { System.err.println(MessageFormat.format("Usage: {0} stockQuoteProviderURL [tickerSymbol1 tickerSymbol2 ...]", StockQuoteProviderClient.class.getName())); System.exit(-1); } String stockQuoteProviderURL = args[0]; List<String> tickerSymbols = new ArrayList<String>(); for (int i=1; i < args.length; i++) { tickerSymbols.add(args[i]); } StockQuoteProvider stockQuoteProvider = stockQuoteProviderService.getStockQuoteProviderPort(); ((BindingProvider)stockQuoteProvider).getRequestContext().put( BindingProvider.ENDPOINT_ADDRESS_PROPERTY, stockQuoteProviderURL); es.udc.ws.jaxwstutorial.client.StockQuoteProviderClient (y 3) List<TradePrice> tradePrices = stockQuoteProvider.getLastTradePrices(tickerSymbols); // Show results … for (int i=0; i<tradePrices.size(); i++) { System.out.println( MessageFormat.format ( … …, tradePrices.get(i).getTickerSymbol(), …)); } catch (IncorrectTickerSymbolException e) { System.out.println("Unable to get ticker symbol“ + e.getFaultInfo().getIncorrectTickerSymbol())); } } } 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 El proxy implementa el interfaz del puerto (StockQuoteProvider en el ejemplo, generado por el compilador de WSDL a Java). El ejemplo recibe la URL de invocación del servicio por línea de comandos y la fija modificando la propiedad ENDPOINT_ADDRESS_PROPERTY del contexto de la petición. Hay otras propiedades (e.g. autenticación). Comentarios (2) Clientes Java EE Un cliente (e.g. una aplicación Web) que corra dentro de un servidor de aplicaciones Java EE puede obtener una referencia al servicio de una manera estándar. Anotación javax.xml.ws.WebServiceRef @WebServiceRef(wsdlLocation="http://localhost:9090/wsjaxwstutorial-service/services/StockQuotesService?wsdl"") private static StockQuoteProvider service; Usando 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: @WebServiceRef(name=”service/StockQuoteProviderService”) Private static StockQuoteProvider service; Comentarios (y 3) 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.jaxwstutorial.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) ws-jaxwstutorialservice.war Incluye WEB-INF/lib Incluye las librerías (ficheros .jar) de Metro WEB-INF/classes Contiene las clases requeridas por el servicio (paquete es.udc.ws.jaxwstutorial.wsdl) WEB-INF/web.xml Fichero de configuración de la aplicación web. WEB-INF/sun-jaxws.xml Configuración de los Servicios Web de Metro. web.xml (1) <?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" 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"> <listener> <listener-class> com.sun.xml.ws.transport.http.servlet.WSServletContextListener </listener-class> </listener> <display-name>JAXWSTutorial Web Service</display-name> <servlet> <servlet-name>WSServlet</servlet-name> <servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet </servlet-class> <load-on-startup>1</load-on-startup> </servlet> web.xml (y 2) <servlet-mapping> <servlet-name>WSServlet</servlet-name> <url-pattern>/services/*</url-pattern> </servlet-mapping> <session-config> <session-timeout>30</session-timeout> </session-config> <welcome-file-list> <welcome-file>index.html</welcome-file> </welcome-file-list> </web-app> Comentarios (1) Se declara una clase Listener específica de Metro (las clases Listener son llamadas cuando una sesión se crea y se destruye). Se declara el servlet WSServlet Forma parte de las librerías de Metro WSServlet El servidor de aplicaciones Web le pasará todas las peticiones (tag servlet-mapping) dirigidas a las URLs http://.../NombreAplicacionWeb/services/* <servlet-mapping> <servlet-name>WSServlet</servlet-name> <url-pattern>/services/*</url-pattern> </servlet-mapping> En este caso, asumiendo que instalemos la aplicación Web con el nombre ws-jaxwstutorial-service, el cliente usará la URL http://.../ws-jaxwstutorialservice/services/StockQuoteProvider para acceder al puerto StockQuoteProvider Comentarios (y 2) WSServlet (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 sun-jaxws.xml <?xml version="1.0" encoding="UTF-8"?> <endpoints xmlns='http://java.sun.com/xml/ns/jax-ws/ri/runtime' version='2.0'> <endpoint name='StockQuoteProvider' implementation= 'es.udc.ws.jaxwstutorial.service.StockQuoteProviderImpl' url-pattern='/services/StockQuotesService'/> </endpoints> Comentarios name. Nombre del puerto. implementation. Clase de implementación del puerto. urlpattern. Tiene que concordar con el indicado en el web.xml.