Download Apartado 3.4: JAX-RPC
Document related concepts
no text concepts found
Transcript
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[]