Casos de ejemplo de migración de servicios web: JAX-RPC a JAX-WS y JAXB
Este tema explica casos de ejemplo para la migración de sus servicios web JAX-RPC (API Java™ para RPC basadas en XML) a JAX-WS (API Java para servicios web basados en XML) a JAXB (Arquitectura Java para enlace XML).
Los cambios en las herramientas se pueden ocultar en gran medida al usuario mediante un entorno de desarrollo como las herramientas de ensamblaje disponibles con WebSphere Application Server. Además, según los datos especificados en XML, algunos métodos que se utilizaban para el servicio JAX-RPC se pueden utilizar con muy pocos (o ningún) cambios en un servicio JAX-WS. También hay condiciones que precisan de cambios en el servicio JAX-WS.
Como los entornos Java EE hacen énfasis en la compatibilidad, la mayoría de los servidores de aplicaciones que ofrecen soporte para especificaciones JAX-WS y JAXB más nuevas siguen teniendo soporte para la especificación JAX-RPC anterior. Una consecuencia de esto es que es probable que los servicios web existentes permanezcan basados en JAX-RPC, mientras que los servicios web nuevos se desarrollen utilizando modelos de programación JAX-WS y JAXB.
No obstante, a medida que pasa el tiempo y las aplicaciones se revisan y reescriben, podría haber situaciones en las que la mejor estrategia es migrar un servicio web basado en JAX-RPC a uno basado en los modelos de programación JAX-WS y JAXB. Así, un proveedor podría optar por proporcionar mejoras sobre las cualidades del servicio que sólo estuviesen a disposición de los nuevos modelos de programación. Por ejemplo, el soporte para SOAP 1.2 y MTOM (SOAP Message Transmission Optimization Mechanism) sólo está disponible en los modelos de programación JAX-WS 2.x y JAXB 2.x, y no en JAX-RPC.
Comparación de los modelos de programación JAX-RPC a JAX-WS y JAXB
Si busca información adicional que describa los cambios entre los modelos de programación JAX-RPC, JAX-WS y JAXB, IBM developerWorks ha publicado varios temas de sugerencias y consejos de servicios web.
Modelos de desarrollo
Los modelos de desarrollo combinados de alto nivel
para JAX-RPC, JAX-WS y JAXB se muestran en la imagen siguiente:
A un alto nivel, los modelos son muy parecidos. La única diferencia es que el modelo JAX-RPC produce artefactos portables y no portables; los modelos de programación JAX-WS y JAXB no producen artefactos no portables.
- Archivo Web Services Description Language (WSDL): Un documento que cumple la recomendación WSDL según publicación por la organización W3C.
- (Opcional) Archivos de configuración
- Herramienta de generación WSDL a Java: Las especificaciones describen las correlaciones necesarias, no el nombre de las herramientas. Por lo general, la herramienta para JAX-WS y JAXB es wsimport, y para JAX-RPC las herramientas wscompile y wsdeploy.
- Artefactos portables: son archivos generados por la herramienta de generación WSDL a Java que tiene correlaciones bien definidas en la especificación JAX-RPC, o JAX-WS y JAXB, lo que sea aplicable. Estos artefactos se pueden utilizar sin modificación entre las distintas implementaciones de JAX-RPC, o JAX-WS y JAXB.
- Artefactos no portables: son archivos generados por la herramienta de generación WSDL a Java que no tiene correlaciones bien definidas en la especificación JAX-RPC, o JAX-WS y JAXB, lo que sea aplicable. Estos artefactos suelen precisar de modificación entre las distintas implementaciones de JAX-RPC o JAX-WS/JAXB.
- Código de implementación: es el código que necesita para cumplir requisitos concretos para implementación.
- Información de despliegue: información adicional necesaria para ejecutar la aplicación en un entorno concreto.
- Archivo WAR (Web Archive): en el contexto de la imagen SampleService, un archivo WAR es un archivo de archivado que contiene todos los elementos necesarios para desplegar un servicio web en un entorno concreto. Esto a veces se denomina archivo WAR preparado.
Los entornos de ejecución y desarrollo
Un entorno de desarrollo especializado simplifica la migración de servicios web mediante la automatización de varias de las tareas. Para el desarrollo de aplicaciones basadas en WebSphere, incluidos los servicios web, puede utilizar las herramientas de ensamblaje que se proporcionan con WebSphere Application Server.
- IBM WebSphere Application Server V6.1, que incluye soporte para la especificación JAX-RPC.
- Paquete de características de IBM WebSphere Application Server V6.1 para servicios web, que incluye soporte para las especificaciones JAX-WS y JAXB.
Ejemplos
Los ejemplos de migración siguientes muestran algunos cambios del código que son necesarios para migrar los servicios web JAX-RPC a servicios web JAX-WS y JAXB. En concreto, hay que hacer énfasis en los cambios en el código de implementación que debe desarrollar, tanto para el cliente como para el servidor. Si no se introduce una función nueva, los cambios necesarios para pasar de JAX-RPC a JAX-WS y JAXB deben ser pocos.
El primer ejemplo es un ejemplo de servicio web basado en JAX-RPC que se ha creado desde un archivo WSDL (desarrollo descendente); el mismo archivo WSDL se utiliza para generar el servicio basado en JAX-WS y JAXB. El archivo WSDL describe el servicio web, SampleService, con estas operaciones descritas en la imagen siguiente:
- xsd:int calcShippingCost(xsd:int, xsd:int)
- xsd:string getAccountNumber(xsd:string)
- xsd:dateTime calculateShippingDate(xsd:dateTime)
- xsd:string ckAvailability(xsd:int) creates invalidDateFault
- Person findSalesRep(xsd:string)
<complexType name="Person">
<sequence>
<element name="name" type="xsd:string"/>
<element name="age" type="xsd:int"/>
<element name="location" type="impl:Address/>
</sequence>
</complexType>
La dirección está definida por: <complexType name="Address">
<sequence>
<element name="street" type="xsd:string"/>
<element name="city" type="xsd:string"/>
<element name ="state" type="xsd:string"/>
<element name="zip" type="xsd:int"/>
</sequence>
</complexType>
La imagen también muestra que la operación,
ckAvailability(xsd:int), que produce una excepción invalidDateFault. 
Revise el código de servicio creado por la herramienta. La información siguiente incluye revisar qué se ha creado para el tiempo de ejecución JAX-RPC y también para un tiempo de ejecución JAX-WS y JAXB.
Para JAX-RPC, la herramienta acepta el archivo WSDL como entrada y, entre otros archivos, genera las interfaces SampleService.java y SampleServiceImpl.java. La interfaz SampleService.java define una interfaz y el código generado se puede revisar en el bloque de código siguiente. La interfaz SampleServiceSoapBindingImpl.java proporciona el esqueleto de una implementación y, por lo general, el usuario tendrá que modificarlo para añadir su propia lógica.
/**
* SampleService.java
*
* Este archivo ha sido generado automáticamente desde WSDL
* por el emisor WSDL2Java de servicios web de IBM.
* cf20633.22 v82906122346
*/
package simple;
public interface SampleService extends java.rmi.Remote {
public java.lang.Integer calcShippingCost(java.lang.Integer shippingWt,
java.lang.Integer shippingZone) throws java.rmi.RemoteException;
public java.lang.String getAccountNumber(java.lang.String accountName)
throws java.rmi.RemoteException;
public java.lang.String[] ckAvailability(int[] itemNumbers)
throws java.rmi.RemoteException, simple.InvalidDateFault;
public java.util.Calendar calculateShippingDate(
java.util.Calendar requestedDate)
throws java.rmi.RemoteException;
public simple.Person findSalesRep(java.lang.String saleRepName)
throws java.rmi.RemoteException; }
package simple;
import java.util.List;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.ws.RequestWrapper;
import javax.xml.ws.ResponseWrapper;
/**
* Esta clase ha sido generada por la SI JAX-WS.
* JAX-WS RI IBM 2.0_03-06/12/2007 07:44 PM(Raja)-fcs
* Versión de origen generada: 2.0
*
*/
@WebService(name = "SampleService", targetNamespace = "http://simple") public interface SampleService {
/**
*
* @param shippingWt
* @param shippingZone
* @return
* returns java.lang.Integer
*/
@WebMethod
@WebResult(name = "shippingCost", targetNamespace = "")
@RequestWrapper(localName = "calcShippingCost", targetNamespace = "http://simple",
className = "simple.CalcShippingCost")
@ResponseWrapper(localName = "calcShippingCostResponse", targetNamespace = "http://simple",
className = "simple.CalcShippingCostResponse")
public Integer calcShippingCost(
@WebParam(name = "shippingWt", targetNamespace = "")
Integer shippingWt,
@WebParam(name = "shippingZone", targetNamespace = "")
Integer shippingZone);
/**
*
*
@param accountName
* @return
* returns java.lang.String
*/
@WebMethod
@WebResult(name = "accountNumber", targetNamespace = "")
@RequestWrapper(localName = "getAccountNumber", targetNamespace = "http://simple",
className = "simple.GetAccountNumber")
@ResponseWrapper(localName = "getAccountNumberResponse", targetNamespace = "http://simple",
className = "simple.GetAccountNumberResponse")
public String getAccountNumber(
@WebParam(name = "accountName", targetNamespace = "")
String accountName);
/**
*
* @param requestedDate
* @return
* returns javax.xml.datatype.XMLGregorianCalendar
*/
@WebMethod
@WebResult(name = "actualDate", targetNamespace = "")
@RequestWrapper(localName = "calculateShippingDate", targetNamespace = "http://simple",
className = "simple.CalculateShippingDate")
@ResponseWrapper(localName = "calculateShippingDateResponse", targetNamespace = "http://simple",
className = "simple.CalculateShippingDateResponse")
public XMLGregorianCalendar calculateShippingDate(
@WebParam(name = "requestedDate", targetNamespace = "")
XMLGregorianCalendar requestedDate);
/**
*
* @param itemNumbers
* @return
* returns java.util.List<java.lang.String>
* @throws InvalidDateFault_Exception
*/
@WebMethod
@WebResult(name = "itemAvailability", targetNamespace = "")
@RequestWrapper(localName = "ckAvailability", targetNamespace = "http://simple", className = "simple.CkAvailability")
@ResponseWrapper(localName = "ckAvailabilityResponse", targetNamespace = "http://simple",
className = "simple.CkAvailabilityResponse")
public List<String> ckAvailability(
@WebParam(name = "itemNumbers", targetNamespace = "")
List<Integer> itemNumbers)
throws InvalidDateFault_Exception
;
/**
*
* @param saleRepName
* @return
* returns simple.Person
*/
@WebMethod
@WebResult(name = "salesRepInfo", targetNamespace = "")
@RequestWrapper(localName = "findSalesRep", targetNamespace = "http://simple", className = "simple.FindSalesRep")
@ResponseWrapper(localName = "findSalesRepResponse", targetNamespace = "http://simple",
className = "simple.FindSalesRepResponse")
public Person findSalesRep(
@WebParam(name = "saleRepName", targetNamespace = "")
String saleRepName);
}
Comparación de los ejemplos de código
@WebMethod
@WebResult(name = "shippingCost", targetNamespace = "")
@RequestWrapper(localName = "calcShippingCost", targetNamespace = "http://simple",
className = "simple.CalcShippingCost")
@ResponseWrapper(localName = "calcShippingCostResponse", targetNamespace = "http://simple",
className = "simple.CalcShippingCostResponse")
public Integer calcShippingCost(
@WebParam(name = "shippingWt", targetNamespace = "")
Integer shippingWt,
@WebParam(name = "shippingZone", targetNamespace = "")
Integer shippingZone);
Pero si no tiene en cuenta las anotaciones, las líneas de código siguiente
son: public Integer calcShippingCost(
Integer shippingWt,
Integer shippingZone);
Estas líneas son casi idénticas a las generadas por JAX-RPC. La única diferencia es que el código JAX-RPC puede producir el error java.rmi.RemoteException
de la siguiente manera:public java.lang.Integer calcShippingCost(java.lang.Integer shippingWt,
java.lang.Integer shippingZone) throws java.rmi.RemoteException;
Siguiendo esta lógica, los tres métodos tienen básicamente las mismas signaturas:public Integer calcShippingCost(Integer shippingWt, Integer shippingZone)
public String getAccountNumber(String accountName)
public Person findSalesRep(String saleRepName)
Esto quiere decir que la migración de JAX-RPC a JAX-WS no afecta directamente
a estos métodos y el código de implementación original que se ejecuta correctamente en el entorno
basado en JAX-WS puede probablemente utilizarse sin cambios para estos métodos.No obstante, ´ dos de los métodos tiene signaturas distintas:
public java.util.Calendar calculateShippingDate(
java.util.Calendar requestedDate)
public java.lang.String[] ckAvailability(int[] itemNumbers)
throws java.rmi.RemoteException,
simple.InvalidDateFault
JAX-WS y JAXB: public XMLGregorianCalendar calculateShippingDate(
XMLGregorianCalendar requestedDate)
public List<String> ckAvailability(List<Integer> itemNumbers)
throws InvalidDateFault_Exception
- SampleServiceSoapBindingImpl.java
- SampleServiceImpl.java
- Correlaciones distintas de nombres XML a nombres Java
Para el método calculateShippingDate, ambos parámetros de entrada y el parámetro de retorno han cambiado del tipo java.util.Calendar al tipo XMLGregorianCalendar. Esto es debido a que WSDL ha especificado estos parámetros para que sean del tipo xsd:dateTime; JAX-RPC correlaciona este tipo de datos con java.util.Calendar, mientras que JAX-WS y JAXB lo correlacionan con XMLGregorianCalendar.
- Distintas correlaciones de matrices de XML a Java
Para el método ckAvailability, el cambio es debido a las correlaciones de datos para matrices XML.
JAX-RPC correlaciona los elementos WSDL siguientes:
Los elementos anteriores se correlacionan con el siguiente código fuente en Java:<element maxOccurs="unbounded" name="itemNumbers" type="xsd:int"/> <element maxOccurs="unbounded" name="itemAvailability" type="xsd:string"/>
JAX-WS y JAXB correlacionan los elementos WSDL siguientes:int[] itemNumbers java.lang.String[] itemAvailability
List<Integer> itemNumbers List<String> ckAvailability
- Distintas correlaciones de excepcionesPara el método ckAvailability, el código JAX-RPC ha generado el error siguiente:
El código de JAX-WS genera el error siguiente:simple.InvalidDateFault
Salvo por los nombres, los constructores para estas excepciones son distintos. Por lo tanto,el código real de JAX-RPC que produce el error se debe visualizar como:InvalidDateFault_Exception
Para JAX-WS, el código utiliza un mandato parecido al ejemplo siguiente:throw new InvalidDateFault("this is an InvalidDateFault");
En casos que utilizan excepciones, el código que las utiliza debe cambiar.throw new InvalidDateFault_Exception( "this is an InvalidDateFault_Exception", new InvalidDateFault());
Migración del código
Ahora que se han explicado las diferencias de código, revise el código original en busca de los métodos que hay que cambiar y cómo cambiar el código, de forma que funcione en un entorno JAX-WS y JAXB.
public java.util.Calendar calculateShippingDate(
java.util.Calendar requestedDate) throws java.rmi.RemoteException {
// Establecer la fecha en la fecha en que se nos ha enviado y añadir 7 días.
requestedDate.add(java.util.Calendar.DAY_OF_MONTH, 7);
// . . .
return requestedDate;
}
Puede escribir el código nuevo de forma que utilice directamente los tipos nuevos como
en los ejemplos siguientes:public XMLGregorianCalendar calculateShippingDate(
XMLGregorianCalendar requestedDate) {
try {
// Crear una fábrica de tipo de datos.
DatatypeFactory df = DatatypeFactory.newInstance();
// Establecer la fecha en la fecha en que se nos ha enviado y añadir 7 días.
Duration duration = df.newDuration("P7D");
requestedDate.add(duration);
} catch (DatatypeConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// . . .
return requestedDate;
}
public java.lang.String[] ckAvailability(int[] itemNumbers)
{
String[] myString = new String[itemNumbers.length];
for (int j = 0; j < myString.length; j++) {
. . .
if ( . .. )
throw new simple.InvalidDateFault(“InvalidDateFault”);
. . .
if ( . . .)
myString[j] = “available: “ + jitemNumbers[j] ;
else
myString[j] = “not available: “ + jitemNumbers[j];
}
return myString;
}
El código anterior acepta un int[] como entrada y devuelve String[]. Para la versión de JAX-WS y JAXB, éstos son elementos List<Integer> y List<String > respectivamente. El procesamiento de estas matrices y descartar el código Exception
code, el código JAX-RPC es parecido a lo siguiente:public java.lang.String[] ckAvailability(int[] itemNumbers)
{
String[] myString = new String[itemNumbers.length];
for (int j = 0; j < jitemNumbers.length; j++) {
. . .
if ( . . .)
myString[j] = “available: “ + itemNumbers.get(j);
else
myString[j] = “not available: “ + itemNumbers.get(j);
}
return myString;
}
El código equivalente JAX-WS y JAXB siguiente existe utilizando listas en vez
de matrices:List <String> ckAvailability(List <Integer> itemNumbers)
{
ArrayList<String> retList = new ArrayList<String>();
for (int count = 0; count < itemNumbers.size(); count++) {
. . .
if ( . . .)
retList.add(“available: “ + itemNumbers.get(j));
else
retList.add(“not available: “ + itemNumbers.get(j));
}
return retList;
}
Las diferencias en las correlaciones de excepciones de XML a Java fuerza al código
JAX-WS a utilizar. InvalidDateFault_Exception
en vez de InvalidDateFault. throw new InvalidDateFault_Exception( "test InvalidDateFault_Exception", new InvalidDateFault());
La implementación JAX-WS y JAXB final del método podría ser parecido al código
siguiente:List <String> ckAvailability(List <Integer> itemNumbers)
{
ArrayList<String> retList = new ArrayList<String>();
for (int count = 0; count < itemNumbers.size(); count++) {
if ( . . . ) {
throw new InvalidDateFault_Exception(
"test InvalidDateFault_Exception",
new InvalidDateFault());
}
. . .
if ( . . .)
retList.add(“available: “ + itemNumbers.get(count));
else
retList.add(“not available: “ + itemNumbers.get(count));
}
return retList;
}
Hay varias formas en las que puede elegir migrar el código. Con práctica y un entorno de desarrollo eficaz, la migración de JAX-RPC
a JAX-WS puede ser sencilla.