Web Service 迁移方案:JAX-RPC 至 JAX-WS 和 JAXB
本主题说明将 Java™ API for XML-based RPC (JAX-RPC) Web Service 迁移到 Java API for XML-Based Web Services (JAX-WS) 和 Java XML 绑定体系结构 (JAXB) Web Service 的方案。
可通过开发环境(如随 WebSphere® Application Server 提供的组装工具),向用户隐藏大部分工具更改。另外,根据在 XML 中指定的数据的不同,用于 JAX-RPC 服务的某些方法在 JAX-WS 服务中,只需稍加更改或无需更改,即可使用。另外,也存在一些的确要求在 JAX-WS Service 中作出更改的情况。
因为 Java EE 环境强调兼容性,所以大部分为更新的 JAX-WS 和 JAXB 规范提供支持的应用程序服务器会继续支持先前的 JAX-RPC 规范。这样做的结果是现有 Web Service 可能保持基于 JAX-RPC,而新的 Web Service 是使用 JAX-WS 和 JAXB 编程模型来开发。
然而,会随着时间的推移对应用程序进行修改和重写,可能存在一些情况,其中的最佳策略是将基于 JAX-RPC 的 Web Service 迁移到基于 JAX-WS 和 JAXB 编程模型的 Web Service。这可能是由于供应商选择提供仅在新的编程模型中可用的服务质量增强。例如,SOAP 1.2 和 SOAP 消息传输优化机制 (MTOM) 支持只适用于 JAX-WS 2.x 和 JAXB 2.x 编程模型,而不适用于 JAX-RPC。
JAX-RPC 与 JAX-WS 和 JAXB 编程模型的比较
如果您正在查找其他描述 JAX-RPC 与 JAX-WS 和 JAXB 编程模型之间的更改的信息,请参阅 IBM developerWorks 上发布的若干 Web Service 提示与技巧主题。
开发模型
下图显示了 JAX-RPC 与 JAX-WS 和 JAXB 的高级组合开发模型:
在高级别,这些模型非常相似。唯一差别是 JAX-RPC 模型会同时产生可移植工件和不可移植工件;JAX-WS 和 JAXB 模型不会产生不可移植工件。
- Web 服务描述语言 (WSDL) 文件:一个符合 W3C 组织所发布 WSDL 建议的文档。
- (可选)配置文件:
- WSDL 至 Java 生成工具:这些规范描述必需的映射,而不是工具的名称。通常,用于 JAX-WS 和 JAXB 的工具是 wsimport,而用于 JAX-RPC 的工具是 wscompile 和 wsdeploy。
- 可移植工件:这些是由 WSDL 至 Java 生成工具生成的文件,这些文件在 JAX-RPC 或 JAX-WS 和 JAXB(以适用的为准)规范中都具有明确定义的映射。这些工件可按原状在 JAX-RPC 或 JAX-WS 和 JAXB 的不同实现之间使用。
- 不可移植工件:这些是由 WSDL 至 Java 生成工具生成的文件,这些文件在 JAX-RPC 或 JAX-WS 和 JAXB(以适用的为准)规范中不具有明确定义的映射。通常,在不同的 JAX-RPC 或 JAX-WS/JAXB 实现之间,这些工件需要加以修改。
- 实现代码:这是为满足实现的特定需求,而需要的代码。
- 部署信息:为在特定环境中运行应用程序,而需要的其他信息。
- Web 归档 (WAR) 文件:在 SampleService 映像的上下文中,WAR 文件是一个归档文件,它包含将 web Service 部署到特定环境时所需的所有项。这有时称为经过加工的 (cooked) WAR 文件。
开发和运行时环境
专用开发环境会通过自动执行许多任务来简化 Web Service 迁移。对于基于 WebSphere 的应用程序(其中包括 Web Service)的开发,您可以使用随 WebSphere Application Server 提供的组装工具。
- IBM WebSphere Application Server V6.1 - 包含对 JAX-RPC 规范的支持。
- IBM WebSphere Application Server V6.1 Feature Pack for Web Services - 包含对 JAX-WS 和 JAXB 规范的支持。
示例
以下示例显示您在将 JAX-RPC Web Service 迁移到 JAX-WS 和 JAXB Web Service 时可能需要作出的一些代码更改。尤其是,从客户机端和服务器端更改您必须编写的实现代码是重点。如果不会引入新功能,那么在从 JAX-RPC 移至 JAX-WS 和 JAXB 时几乎不需要更改。
第一个示例是根据 WSDL 文件创建的基于 JAX-RPC 的 Web Service 样本(自顶向下式开发);会使用同一 WSDL 文件来生成基于 JAX-WS 和 JAXB 的服务。此 WSDL 文件使用下图所述的这些操作来描述 SampleService Web Service:
- 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>
Address 是由以下片段进行定义:<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>
下图也显示操作 ckAvailability(xsd:int),此操作会产生 invalidDateFault 异常。
复审工具所创建的服务代码。以下信息包含检查针对 JAX-RPC 运行时创建的内容,及同时针对 JAX-WS 和 JAXB 运行时创建的内容。
对于 JAX-RPC,工具会接受 WSDL 文件作为输入,并且除了其他文件以外,还会生成 SampleService.java 和 SampleServiceImpl.java 接口。SampleService.java 接口会定义接口,并且生成的代码可以在以下代码块中进行复审。SampleServiceSoapBindingImpl.java 接口会提供实现的框架,并且您通常会修改此框架以添加您自己的逻辑。
/**
* SampleService.java
*
* This file was auto-generated from WSDL
* by the IBM web services WSDL2Java emitter.
* 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;
/**
* This class was generated by the JAX-WS SI.
* JAX-WS RI IBM 2.0_03-06/12/2007 07:44 PM(Raja)-fcs
* Generated source version: 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);
}
比较代码示例
@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);
但是,如果您忽略注释,那么代码行如下:public Integer calcShippingCost(
Integer shippingWt,
Integer shippingZone);
这些代码行几乎与针对 JAX-RPC 产生的代码行完全相同。唯一的差别是 JAX-RPC 代码会产生 java.rmi.RemoteException 错误,如下所示:public java.lang.Integer calcShippingCost(java.lang.Integer shippingWt,
java.lang.Integer shippingZone) throws java.rmi.RemoteException;
按照此逻辑,其中的三种方法本质上具有相同的签名:public Integer calcShippingCost(Integer shippingWt, Integer shippingZone)
public String getAccountNumber(String accountName)
public Person findSalesRep(String saleRepName)
这意味着从 JAX-RPC 迁移到 JAX-WS 不会直接影响这些方法,并且原始实现代码(正在基于 JAX-RPC 的环境中成功运行)可按原状用于这些方法。然而,其中的两种方法的确具有不同的签名:
public java.util.Calendar calculateShippingDate(
java.util.Calendar requestedDate)
public java.lang.String[] ckAvailability(int[] itemNumbers)
throws java.rmi.RemoteException,
simple.InvalidDateFault
JAX-WS 和 JAXB:public XMLGregorianCalendar calculateShippingDate(
XMLGregorianCalendar requestedDate)
public List<String> ckAvailability(List<Integer> itemNumbers)
throws InvalidDateFault_Exception
- SampleServiceSoapBindingImpl.java
- SampleServiceImpl.java
- 从 XML 名称至 Java 名称的映射存在差别
对于 calculateShippingDate 方法,输入参数和返回参数都已从类型 java.util.Calendar 更改为类型 XMLGregorianCalendar。这是因为 WSDL 已将这些参数指定成类型 xsd:dateTime,所以 JAX-RPC 会将此数据类型映射到 java.util.Calendar,而 JAX-WS 和 JAXB 会将此数据类型映射到 XMLGregorianCalendar。
- 从 XML 至 Java 的不同数组映射
对于 ckAvailability 方法,导致此更改的原因是 XML 数组的数据映射。
JAX-RPC 会映射以下 WSDL 元素:
会将先前元素映射到以下 Java 源代码:<element maxOccurs="unbounded" name="itemNumbers" type="xsd:int"/> <element maxOccurs="unbounded" name="itemAvailability" type="xsd:string"/>
JAX-WS 和 JAXB 会映射以下 WSDL 元素:int[] itemNumbers java.lang.String[] itemAvailability
List<Integer> itemNumbers List<String> ckAvailability
- 不同的异常映射对于 ckAvailability 方法,JAX-RPC 代码生成了以下错误:
JAX-WS 代码会生成以下错误:simple.InvalidDateFault
除了名称之外,这些异常的构造函数是不同的。因此,产生该错误的实际 JAX-RPC 代码可能显示为:InvalidDateFault_Exception
对于 JAX-WS,代码会使用类似于以下示例的命令:throw new InvalidDateFault("this is an InvalidDateFault");
在使用异常的情况下,使用异常的代码需要进行更改。throw new InvalidDateFault_Exception( "this is an InvalidDateFault_Exception", new InvalidDateFault());
迁移代码
在说明代码之间的差别之后,请复审需要进行更改的方法的原始代码,并复审如何更改此代码以便它可以在 JAX-WS 和 JAXB 环境中工作。
public java.util.Calendar calculateShippingDate(
java.util.Calendar requestedDate) throws java.rmi.RemoteException {
// Set the date to the date that was sent to us and add 7 days.
requestedDate.add(java.util.Calendar.DAY_OF_MONTH, 7);
// . . .
return requestedDate;
}
您可以编写新代码以直接使用新类型,如以下示例中所示:public XMLGregorianCalendar calculateShippingDate(
XMLGregorianCalendar requestedDate) {
try {
// Create a data type factory.
DatatypeFactory df = DatatypeFactory.newInstance();
// Set the date to the date that was sent to us and add 7 days.
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;
}
先前代码会接受 int[] 作为输入,并返回 String[]。对于 JAX-WS 和 JAXB 版本,这些分别是 List<Integer> 和 List<String> 元素。对这些数组进行处理以及废弃 Exception 代码时,JAX-RPC 代码类似如下: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;
}
存在以下使用列表取代数组的 JAX-WS 和 JAXB 等价代码: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;
}
从 XML 至 Java 的异常映射差别会强制 JAX-WS 代码使用 InvalidDateFault_Exception 取代 InvalidDateFault。throw new InvalidDateFault_Exception( "test InvalidDateFault_Exception", new InvalidDateFault());
此方法的最终 JAX-WS 和 JAXB 实现可能类似于以下代码: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;
}
可选择多种方式来迁移代码。借助实践和有效的开发环境,从 JAX-RPC 迁移到 JAX-WS 会很简单。