IBM WebSphere Application Server V7 包括了對 Java API for XML-Based Web Services (JAX-WS) 2.1 規范的支持。JAX-WS 2.1 是 Java Specification Request (JSR) 224 的維護版本,通過增加新功能對 JAX-WS 2.0 規范提供的功能進行了擴展。其中最重要的新功能就是 在應用程序編程接口(Application Programming Interface,API)中支持 Web Services Addressing (WS-Addressing)。在本文中,我們將 了解如何使用這個新的 WS-Addressing 支持,以及如何幫助 web 服務開發人員簡化有狀態 web 服務的開發。
簡介
Java™ API for XML-Based Web Services (JAX-WS) 接替了此前的標准 Java API for XML based RPC (JAX-RPC)。JAX -RPC 1.0 和 JAX-RPC 1.1 為支持 Remote Procedure Call (RPC) 風格的 Web 服務調用定義了 API 和約定。然而,從那時起,Web 服務開始 朝更加面向文檔的風格發展。JAX-WS 2.0 提供了完全不同的應用程序編程接口(API)和約定來支持更新風格的 web 服務交互。並且還特別注 重簡化開發人員在編寫 Web 服務應用程序時的體驗。參見參考資料部分,獲得詳細介紹 JAX-WS 和 JAX-RPC 之間差別的文章的鏈接。
JAX-WS 2.1 繼承了從 JAX-WS 2.0 開始出現的簡化趨勢,使開發人員更易於編寫有狀態 Web 服務。這是通過向 JAX-WS API 添加明確 的 Web Services Addressing (WS-Addressing) 支持實現的。在本文中,我們將給出一個示例應用程序,它展示了如何使用這些新的 API 編 寫有狀態 Web 服務,但是首先我們將詳細了解一下這些與 WS-Addressing 有關的新 API。
不過,在開始之前,我們需要指出 JAX-WS 2.1 規范只支持 WS-Addressing 1.0 Core 和 Simple Object Access Protocol (SOAP) Binding 規范。如果需要支持 WS-Addressing 規范的不同版本,那麼 JAX-WS 2.1 規范還為供應商提供了擴展 API 的能力。在 IBM® WebSphere® Application Server V7 中,我們利用了這種功能,以支持較舊的 WS-Addressing Member Submission 規范(參見參考資料 )。這些擴展也將在本文中加以介紹。
端點引用
JAX-WS 2.1 規范引入了端點引用(endpoint reference)的概念。端點引用封 裝了用於成功定位 Web 服務端點所需的所有細節。API 引入了一個新類,EndpointReference,用於表示端點引用。然而,該類並不是由開發 人員直接使用,而是應當使用它的子類。
JAX-WS 2.1 API 包括了 EndpointReference 的一個子類,名為 W3CEndpointReference。它 的作用是根據 WS-Addressing 1.0 Core 規范的要求表示端點引用。在 WebSphere Application Server V7 中,我們提供了另一個子類 SubmissionEndpointReference,用於根據 WS-Addressing Member Submission 規范表示端點引用。這兩個類之間的關系如圖 1 所示。
圖 1. 端點關系
JAX-WS 2.1 規范還定義了多種在應用程序中創建端點引用的方法。清單 1 中的代碼片段使用 W3CEndpointReferenceBuilder 類顯示了其中一種方法 。為清晰起見,我們在下面的示例中忽略了一些細節,但是可以通過本文末尾的應用程序獲得完整的示例。
清單 1. JAX-WS 2.1 示例
import org.w3c.dom.Element;
import javax.xml.ws.wsaddressing.W3CEndpointReference;
import javax.xml.ws.wsaddressing.W3CEndpointReferenceBuilder;
String address = ...
Element referenceParameter = ...
W3CEndpointReferenceBuilder builder =
new W3CEndpointReferenceBuilder();
builder.address(address);
builder.referenceParameter(referenceParameter);
W3CEndpointReference epr = builder.build();
W3CEndpointReferenceBuilder 使開發人員能夠指定端點引用的任意屬性。在清單 1 中,我們僅僅指定了一個地址和一個引用參數,然而 ,還可以指定 Web Services Description Language (WSDL) 服務名和端點(端口)名來代替地址。運行時將使用服務名和端點名的組合來識 別引用所需的端點,並且返回一個填充有正確地址的 W3CEndpointReference。
如果需要使用 SubmissionEndpointReference 替代 W3CEndpointReference,那麼在 WebSphere Application Server V7 中,可以使用 SubmissionEndpointReferenceBuilder 類,如清單 2 中的代碼片段所示。
清單 2. SubmissionEndpointReference
import org.w3c.dom.Element;
import com.ibm.websphere.wsaddressing.jaxws21.SubmissionEndpointReference;
import com.ibm.websphere.wsaddressing.jaxws21.SubmissionEndpointReferenceBuilder;
String address = ...
Element referenceParameter = ...
SubmissionEndpointReferenceBuilder builder =
new SubmissionEndpointReferenceBuilder();
builder.address(address);
builder.referenceParameter(referenceParameter);
SubmissionEndpointReference epr = builder.build();
如果使用的是服務名和端點名的組合,那麼可以使用 W3CEndpointReferenceBuilder 和 SubmissionEndpointReferenceBuilder 類為同一 個 Java Platform, Enterprise Edition (Java EE) 應用程序中部署的任意端點創建端點引用。這表示,如果端點不在同一個應用程序中,那 麼必須明確指定地址。如果端點所需的僅是對其本身的引用,那麼 JAX-WS 2.1 提供了一種更簡單的替代方法來使用構建程序。 WebServiceContext 類被擴展,以支持端點引用的創建。如清單 3 所示。
清單 3. WebServiceContext 類
import org.w3c.dom.Element;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.wsaddressing.W3CEndpointReference;
@Resource
private WebServiceContext context;
Element referenceParameter = ...
W3CEndpointReference w3cEPR =
(W3CEndpointReference) context.getEndpointReference(referenceParameter);
在集群中創建端點引用
如果應用程序被部署到集群中,那麼使用構建程序或 WebServiceContext 創建的任何端點引用都不會支持對某個特定服務器或端點的相關 性(affinity)。不過,簡單的工作負載管理是可行的,因為使用這種端點引用通過代理發出的任何請求都將以輪循(round-robin)方式分布 到集群成員中。
在使用 WebServiceContext 時,惟一可以明確指定的屬性是一個引用參數。地址元素以及所有其他屬性由運行時填充。如果需要一個 SubmissionEndpointReference,那麼在 WebSphere Application Server V7 中,可以使用 JAX-WS 2.1 提供的擴展機制。如清單 4 的代碼片 段所示。
清單 4. SubmissionEndpointReference
import org.w3c.dom.Element;
import javax.xml.ws.WebServiceContext;
import com.ibm.websphere.wsaddressing.jaxws21.SubmissionEndpointReference;
@Resource
private WebServiceContext context;
Element referenceParameter = ...
SubmissionEndpointReference epr =
context.getEndpointReference(SubmissionEndpointReference.class, referenceParameter);
應用程序中創建的端點引用可以通過連接返回到客戶端。客戶端隨後可以使用該端點引用按需調用端點。根據 WS-Addressing 規范,端點 引用中包含的任何引用參數都將被作為頭部(header)自動添加到要發送的 SOAP 信封。JAX-WS 2.1 還提供了一種工具,允許 Web 服務應用 程序檢索這些引用參數,如清單 5 所示。
清單 5. 檢索引用參數
import org.w3c.dom.Element;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.handler.MessageContext;
@Resource
private WebServiceContext context;
List<Element> list =
(List<Element>) context.getMessageContext().get(MessageContext.REFERENCE_PARAMETERS);
該列表隨後將被讀取,以從中找出引用參數。然而,如果遵守 WS-Addressing Member Submission 規范編寫應用程序,那麼就不能使用這 種機制檢索引用參數。相反,必須使用 EndpointReferenceManager 類,這是 WebSphere Application Server V7 提供的專有 WS-Addressing API 的一部分。如清單 6 所示。在這種情況下,必須提供引用參數的名稱才能檢索它。
清單 6. EndpointReferenceManager
import javax.xml.namespace.QName;
import com.ibm.websphere.wsaddressing.EndpointReferenceManager;
QName name = ...
String refParam =
EndpointReferenceManager.getReferenceParameterFromMessageContext(name);
Feature
JAX-WS 2.1 還引入了 feature 概念。一個 feature 就是指一種允許客戶端開發人員通過編程方式控制客戶端具體行為的機制。所有 feature 都來自 WebServiceFeature 類。這允許客戶端開發人員將不同類型的 WebServiceFeature 傳遞給需要它們的客戶端 API。與 WS- Addressing 有關的 feature 如圖 2 所示。
圖 2. WS-Addressing Feature
AddressingFeature 用於控制 WS-Addressing 的使用。它允許開發人員啟用/禁用客戶端對 WS-Addressing 的使用。如果啟用了 WS- Addressing,客戶端將在要發送的 SOAP 消息中包含 WS-Addressing 頭。這些頭將位於 WS-Addressing 1.0 Core 規范名稱空間 http://www.w3.org/2005/08/addressing 中。如果禁用 WS-Addressing,那麼不會發送任何 WS-Addressing 頭。
在 WebSphere Application Server V7 中,我們還提供一個 SubmissionAddressingFeature 類。該類與 AddressingFeature 的工作方式 相同,惟一不同之處是如果它得到啟用,那麼由客戶端發送的任何 WS-Addressing 頭都將位於 WS-Addressing Member Submission 規范名稱 空間 http://schemas.xmlsoap.org/ws/2004/08/addressing 中。在 WebSphere Application Server V7 中,可以使用其他機制啟用客戶端中 的 WS-Addressing 支持,但是這不屬於本文的討論范圍。請參見參考資料部分,獲得介紹這些機制的相關鏈接。
注釋
JAX-WS 2.1 大量使用了注釋(annotation)來簡化服務器端的應用程序開發。@Addressing 注釋在服務器端起到與 AddressingFeature 相 同的作用。它用於對 web 服務端點啟用/禁用 WS-Addressing。如果啟用了 WS-Addressing,那麼運行時將處理傳入請求中包含的 http://www.w3.org/2005/08/addressing 名稱空間中的任何 WS-Addressing 頭。此外,任何響應消息在適當時都會將 WS-Addressing 頭添加 到自身。如果 WS-Addressing 被禁用,那麼傳入請求中的任何 WS-Addressing 頭部都將被忽略。
@Addressing 注釋還支持一個名為 required 的屬性。如果將該屬性設置為 true,那麼任何傳入 web 服務的消息都必須包含 WS- Addressing 頭,否則將向客戶端返回一個錯誤。required 屬性也出現在 AddressingFeature 中,但是根據 JAX-WS 2.1 規范,它將被客戶端 忽略。在 WebSphere Application Server V7 中,我們還提供了 @SubmissionAddressing 注釋。它與 @Addressing 的工作方式相同,不同之 處在於它與 http://schemas.xmlsoap.org/ws/2004/08/addressing 名稱空間中的 WS-Addressing 頭有關。這些類如圖 3 所示。
圖 3. Addressing 類
JAX-WS 2.1 還引入了新的注釋來支持 WS-Addressing 動作和 WSDL 操作的映射。這些新注釋是 @Action 和 @FaultAction。@Action 注釋 允許將一個動作顯式地關聯到一個 WSDL 操作的輸入和輸出。@FaultAction 注釋在 @Action 注釋的內部使用,用於將每個錯誤關聯到一個操 作。這些類如圖 4 所示。要使用 @Action 和 @FaultAction 注釋,需要用到 @Addressing 注釋,否則它們將被忽略。
圖 4. Fault 和 Action 類
應用程序
現在我們已經簡要介紹了 JAX-WS 2.1 中引入的與 WS-Addressing 有關的新概念,接下來我們將把它們組合到一個有用的應用程序中 。在下面給出的 Calculator 應用程序中,客戶端向 web 服務發出一個請求,要求將兩個數相加並返回結果。然而,在客戶端發出請求之前, 它必須首先從 web 服務獲得一個票證(ticket),之後才能夠發出請求來將兩個數字相加。這個票證被作為端點引用內部的一個引用參數發送 給客戶端。客戶端隨後可以使用這個端點引用來發送請求,將兩個數字相加。
還可以下載獲得這個 Calculator 應用程序。該應用程序 以 .ear 文件的形式提供,可以被導入到 IBM Rational® Application Developer V7.5.1 或安裝到 WebSphere Application Server V7 上。要在 Rational Application Developer 上完整編譯,必須將 com.ibm.jaxws.thinclient_7.0.0.jar 添加到創建的 CalculatorClient 和 CalculatorService 項目的類路徑。jar 文件可以在 WebSphere Application Server 安裝的運行時目錄中找到。
讓我們首先看一 下 Calculator 服務的 WSDL。WSDL 如清單 7 所示。我們可以看到其中包含 4 個操作:getTicket、add、getSubmissionTicket 和 addSubmission。getTicket 操作返回一個端點引用,其中包含作為引用參數提供的票證。add 操作允許我們將兩個數字相加。其他兩個操作執 行相同的功能,但是使用的是 WS-Addressing Member Submission 規范而不是 WS-Addressing 1.0 Core 規范。
清單 7. Calculator.wsdl
<?xml version="1.0" encoding="UTF-8"?>
<definitions targetNamespace="http://calculator.jaxws.developerworks"
name="Calculator"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://calculator.jaxws.developerworks"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
<types>
<xsd:schema>
<xsd:import namespace="http://calculator.jaxws.developerworks"
schemaLocation="Calculator_schema1.xsd"/>
</xsd:schema>
</types>
<message name="getTicket">
<part name="parameters" element="tns:getTicket"/>
</message>
<message name="getTicketResponse">
<part name="parameters" element="tns:getTicketResponse"/>
</message>
<message name="add">
<part name="parameters" element="tns:add"/>
</message>
<message name="addResponse">
<part name="parameters" element="tns:addResponse"/>
</message>
<message name="getSubmissionTicket">
<part name="parameters" element="tns:getSubmissionTicket"/>
</message>
<message name="getSubmissionTicketResponse">
<part name="parameters" element="tns:getSubmissionTicketResponse"/>
</message>
<message name="addSubmission">
<part name="parameters" element="tns:addSubmission"/>
</message>
<message name="addSubmissionResponse">
<part name="parameters" element="tns:addSubmissionResponse"/>
</message>
<message name="AddNumbersException">
<part name="fault" element="tns:AddNumbersException"/>
</message>
<portType name="Calculator">
<operation name="getTicket">
<input message="tns:getTicket"/>
<output message="tns:getTicketResponse"/>
</operation>
<operation name="getSubmissionTicket">
<input message="tns:getSubmissionTicket"/>
<output message="tns:getSubmissionTicketResponse"/>
</operation>
<operation name="add">
<input wsaw:Action="http://calculator.jaxws.developerworks/add"
message="tns:add"/>
<output wsaw:Action="http://calculator.jaxws.developerworks/addResponse"
message="tns:addResponse"/>
<fault message="tns:AddNumbersException" name="AddNumbersException"
wsaw:Action="http://calculator.jaxws.developerworks/addFault"/>
</operation>
<operation name="addSubmission">
<input wsaw:Action="http://calculator.jaxws.developerworks/addSubmission"
message="tns:addSubmission"/>
<output
wsaw:Action="http://calculator.jaxws.developerworks/addSubmissionResponse"
message="tns:addSubmissionResponse"/>
<fault message="tns:AddNumbersException" name="AddNumbersException"
wsaw:Action="http://calculator.jaxws.developerworks/addSubmissionFault"/>
</operation>
</portType>
<binding name="CalculatorPortBinding" type="tns:Calculator">
<wsaw:UsingAddressing/>
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
<operation name="getTicket">
<soap:operation soapAction=""/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
<operation name="getSubmissionTicket">
<soap:operation soapAction=""/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
<operation name="add">
<soap:operation soapAction=""/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
<fault name="AddNumbersException">
<soap:fault name="AddNumbersException" use="literal"/>
</fault>
</operation>
<operation name="addSubmission">
<soap:operation soapAction=""/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
<fault name="AddNumbersException">
<soap:fault name="AddNumbersException" use="literal"/>
</fault>
</operation>
</binding>
<service name="Calculator">
<port name="CalculatorPort" binding="tns:CalculatorPortBinding">
<soap:address location="http://localhost:9080/CalculatorService/Calculator"/>
</port>
</service>
</definitions>
生成一個 SEI
當使用清單 7 中的 WSDL 生成 SEI 後,@Action 和 @FaultAction 注釋不會被自動添加。在清單 8 中的 SEI 中,我們手動添加了注釋。
通過使用這個 WSDL 以及應用程序 ear 文件中的 Calculator_schema1.xsd 模式,我們可以生成一個 Service Endpoint Interface (SEI) 。這可以通過使用 WebSphere Application Sever 安裝的 bin 目錄中的 wsimport 工具完成。生成的 SEI 如清單 8 所示。它包含 4 個方法 :getTicket()、add()、getSubmissionTicket() 和 addSubmission()。 getTicket() 方法返回一個 W3CEndpointReference,而 getSubmissionTicket() 方法返回一個 SubmissionEndpointReference。add() 和 addSubmission() 方法返回一個 int,並且它們還使用 @Action 進行了注釋。@Action 注釋包含一個將 AddNumbersException 映射到動作 @FaultAction 注釋。
清單 8. Calculator.java
package developerworks.jaxws.calculator;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.ws.Action;
import javax.xml.ws.FaultAction;
import javax.xml.ws.RequestWrapper;
import javax.xml.ws.ResponseWrapper;
import javax.xml.ws.wsaddressing.W3CEndpointReference;
import com.ibm.websphere.wsaddressing.jaxws21.SubmissionEndpointReference;
@WebService(name = "Calculator", targetNamespace =
"http://calculator.jaxws.developerworks")
@XmlSeeAlso({
ObjectFactory.class
})
public interface Calculator {
@WebMethod
@WebResult(targetNamespace = "")
@RequestWrapper(localName = "getTicket",
targetNamespace = "http://calculator.jaxws.developerworks",
className = "developerworks.jaxws.calculator.GetTicket")
@ResponseWrapper(localName = "getTicketResponse",
targetNamespace = "http://calculator.jaxws.developerworks",
className = "developerworks.jaxws.calculator.GetTicketResponse")
public W3CEndpointReference getTicket();
@WebMethod
@WebResult(targetNamespace = "")
@RequestWrapper(localName = "getSubmissionTicket",
targetNamespace = "http://calculator.jaxws.developerworks",
className = "developerworks.jaxws.calculator.GetSubmissionTicket")
@ResponseWrapper(localName = "getSubmissionTicketResponse",
targetNamespace = "http://calculator.jaxws.developerworks",
className = "developerworks.jaxws.calculator.GetSubmissionTicketResponse")
public SubmissionEndpointReference getSubmissionTicket();
@WebMethod
@WebResult(targetNamespace = "")
@RequestWrapper(localName = "add",
targetNamespace = "http://calculator.jaxws.developerworks",
className = "developerworks.jaxws.calculator.Add")
@ResponseWrapper(localName = "addResponse",
targetNamespace = "http://calculator.jaxws.developerworks",
className = "developerworks.jaxws.calculator.AddResponse")
@Action(input = "http://calculator.jaxws.developerworks/add",
output = "http://calculator.jaxws.developerworks/addResponse",
fault = {@FaultAction(className = AddNumbersException.class,
value = "http://calculator.jaxws.developerworks/addFault")})
public int add(
@WebParam(name = "arg0", targetNamespace = "")
int arg0,
@WebParam(name = "arg1", targetNamespace = "")
int arg1)
throws AddNumbersException_Exception;
@WebMethod
@WebResult(targetNamespace = "")
@RequestWrapper(localName = "addSubmission",
targetNamespace = "http://calculator.jaxws.developerworks",
className = "developerworks.jaxws.calculator.AddSubmission")
@ResponseWrapper(localName = "addSubmissionResponse",
targetNamespace = "http://calculator.jaxws.developerworks",
className = "developerworks.jaxws.calculator.AddSubmissionResponse")
@Action(input = "http://calculator.jaxws.developerworks/addSubmission",
output = "http://calculator.jaxws.developerworks/addSubmissionResponse",
fault = {@FaultAction(className = AddNumbersException.class,
value = "http://calculator.jaxws.developerworks/addSubmissionFault")})
public int addSubmission(
@WebParam(name = "arg0", targetNamespace = "")
int arg0,
@WebParam(name = "arg1", targetNamespace = "")
int arg1)
throws AddNumbersException_Exception;
}
在清單 9 中顯示了我們的 web 服務的實現。它使用 @Addressing 和 @SubmissionAddressing 進行了注釋,表示通過這兩種規范對應用程 序啟用了 WS-Addressing。注意,@Addressing 和 @SubmissionAddressing 必須被添加到實現類而不是 SEI 中。getTicket() 和 getSubmissionTicket() 方法各自創建了一個包含票證的端點引用。在將兩個數字相加之前,add() 和 addSubmission() 方法將檢查該票證。 考慮到我們的這個簡單示例的意圖,我們僅僅硬編碼了一個字符串來充當票證。然而,在一個更加真實的場景中,我們可以想象得到這個票證 實際上表示數據庫中某個記錄的鍵(舉例而言)。
清單 9. CalculatorService.java
package developerworks.jaxws.calculator.impl;
import java.util.List;
import javax.annotation.Resource;
import javax.jws.WebService;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.soap.Addressing;
import javax.xml.ws.wsaddressing.W3CEndpointReference;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import com.ibm.websphere.wsaddressing.EndpointReferenceManager;
import com.ibm.websphere.wsaddressing.ReferenceParameterCreationException;
import com.ibm.websphere.wsaddressing.jaxws21.SubmissionAddressing;
import com.ibm.websphere.wsaddressing.jaxws21.SubmissionEndpointReference;
import developerworks.jaxws.calculator.AddNumbersException;
import developerworks.jaxws.calculator.AddNumbersException_Exception;
import developerworks.jaxws.calculator.Calculator;
@Addressing
@SubmissionAddressing
@WebService(endpointInterface = "developerworks.jaxws.calculator.Calculator",
serviceName = "Calculator",
portName = "CalculatorPort",
targetNamespace = "http://calculator.jaxws.developerworks")
public class CalculatorService implements Calculator {
@Resource
private WebServiceContext context;
public W3CEndpointReference getTicket() {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
Element element = null;
try {
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.newDocument();
element = document.createElementNS(
"http://calculator.jaxws.developerworks",
"TicketId");
element.appendChild( document.createTextNode("123456789") );
} catch (ParserConfigurationException pce) {
throw new WebServiceException("Unable to create ticket.", pce);
}
return (W3CEndpointReference) context.getEndpointReference(element);
}
public SubmissionEndpointReference getSubmissionTicket() {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
Element element = null;
try {
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.newDocument();
element = document.createElementNS(
"http://calculator.jaxws.developerworks",
"TicketId");
element.appendChild( document.createTextNode("123456789") );
} catch (ParserConfigurationException pce) {
throw new WebServiceException("Unable to create ticket.", pce);
}
return (SubmissionEndpointReference) context.getEndpointReference(
SubmissionEndpointReference.class, element);
}
public int add(int value1, int value2) throws AddNumbersException_Exception {
List<?> list = (List<?>) context.getMessageContext().get(
MessageContext.REFERENCE_PARAMETERS);
if (list.isEmpty())
throw new AddNumbersException_Exception("No ticket found.",
new AddNumbersException());
Element element = (Element) list.get(0);
if (!"123456789".equals(element.getTextContent()))
throw new AddNumbersException_Exception("Invalid ticket: "
+ element.getTextContent(), new AddNumbersException());
return value1 + value2;
}
public int addSubmission(int value1, int value2)
throws AddNumbersException_Exception {
String refParam;
try {
refParam = EndpointReferenceManager
.getReferenceParameterFromMessageContext(new QName(
"http://calculator.jaxws.developerworks",
"TicketId"));
} catch (ReferenceParameterCreationException e) {
throw new AddNumbersException_Exception("No ticket found.",
new AddNumbersException());
}
if (!"123456789".equals(refParam)) {
throw new AddNumbersException_Exception("Invalid ticket: "
+ refParam, new AddNumbersException());
}
return value1 + value2;
}
}
對 WS-Addressing Member Submission 規范的支持
在使用 wsimport 工具生成客戶端項目時,將自動把 WS-Addressing Core 1.0 名稱空間中的端點引用映射到 W3CEndpointReference 類。 然而,要將 WS-Addressing Member Submission 名稱空間的端點引用映射到 SubmissionEndpointReference 類,您需要將所提供的綁定文件 SubmissionEndpointReference.xjb 指定為工具的 -b 參數。綁定文件可以在 WebSphere Application Server 安裝的 util 目錄中找到。
清單 10 展示的客戶端演示了對 Calculator 服務的應用。它被編寫為同時使用 WS-Addressing 1.0 規范和 WS-Addressing Member Submission 規范。為此,它使用 get ticket 方法調用服務來獲取端點引用,後者包含作為引用參數提供的票證。隨後使用端點引用向對應的 add 方法發送請求,要求將兩個數字相加。使用端點引用將使所包含的引用參數被作為 SOAP 頭自動包含到請求中。web 服務將首先檢查這一 點,然後再執行數字相加並發送結果。
清單 10. CalculatorClient.java
package developerworks.jaxws.calculator.client;
import javax.xml.ws.soap.AddressingFeature;
import javax.xml.ws.wsaddressing.W3CEndpointReference;
import com.ibm.websphere.wsaddressing.jaxws21.SubmissionAddressingFeature;
import com.ibm.websphere.wsaddressing.jaxws21.SubmissionEndpointReference;
import developerworks.jaxws.calculator.Calculator;
import developerworks.jaxws.calculator.Calculator_Service;
public class CalculatorClient {
/**
* @param args
*/
public static void main(String[] args) {
try {
int value0 = Integer.parseInt(args[0]);
int value1 = Integer.parseInt(args[1]);
Calculator_Service service = new Calculator_Service();
Calculator port1 = service.getCalculatorPort();
// Retrieve W3CEndpointRefence ticket
W3CEndpointReference epr = port1.getTicket();
Calculator port2 = epr.getPort(Calculator.class,
new AddressingFeature());
// Add numbers using W3CEndpointRefence ticket
int answer = port2.add(value0, value1);
System.out
.println("The answer using a W3CEndpointRefence ticket is: "
+ answer);
// Retrieve SubmissionEndpointReference ticket
SubmissionEndpointReference submissionEpr = port1
.getSubmissionTicket();
port2 = submissionEpr.getPort(Calculator.class,
new SubmissionAddressingFeature());
// Add numbers using a SubmissionEndpointReference
answer = port2.addSubmission(value0, value1);
System.out
.println("The answer using a SubmissionEndpointReference ticket is: "
+ answer);
} catch (Exception e) {
e.printStackTrace();
}
}
}
在清單 11 中,我們看到在使用 getTicket() 方法請求票證時消息被發送。注意,使用了 WS-Addressing 的默認動作模式來確定要使用的 動作,因為清單 8 中的 getTicket() 方法中沒有使用 @Action 注釋。默認動作模式是由 WS-Addressing 規范定義的一種方式,用於根據 WSDL 操作的屬性確定操作。
清單 11. GetTicket Request
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header xmlns:wsa="http://www.w3.org/2005/08/addressing">
<wsa:To>http://localhost:9080/CalculatorService/Calculator</wsa:To>
<wsa:MessageID>urn:uuid:14C1B8561B19D0B1261258998512414</wsa:MessageID>
<wsa:Action>
http://calculator.jaxws.developerworks/Calculator/getTicketRequest
</wsa:Action>
</soapenv:Header>
<soapenv:Body>
<ns4:getTicket
xmlns:ns2="http://schemas.xmlsoap.org/ws/2004/08/addressing"
xmlns:ns3="http://www.w3.org/2005/08/addressing"
xmlns:ns4="http://calculator.jaxws.developerworks"/>
</soapenv:Body>
</soapenv:Envelope>
在清單 12 中,我們看到了響應消息。注意,TicketId 包含在返回的 EndpointReference 的 ReferenceParameters 中,而且默認的動作 模式仍在使用中。還需注意,WS-Addressing 1.0 Core 規范名稱空間被用於 EndpointReference,以及使用該 EndpointReference 的後續請 求和響應的 WS-Addressing 頭。
清單 12. GetTicket Response
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/>
<soapenv:Header xmlns:wsa="http://www.w3.org/2005/08/addressing">
<wsa:Action>
http://calculator.jaxws.developerworks/Calculator/getTicketResponse
</wsa:Action>
<wsa:RelatesTo>urn:uuid:14C1B8561B19D0B1261258998512414</wsa:RelatesTo>
</soapenv:Header>
<soapenv:Body>
<ns4:getTicketResponse
xmlns:ns2="http://schemas.xmlsoap.org/ws/2004/08/addressing"
xmlns:ns3="http://www.w3.org/2005/08/addressing"
xmlns:ns4="http://calculator.jaxws.developerworks">
<return>
<ns3:Address>
http://L32H82K:9080/CalculatorService/Calculator
</ns3:Address>
<ns3:ReferenceParameters>
<ns4:TicketId
xmlns="http://calculator.jaxws.developerworks"
xmlns:wsa="http://www.w3.org/2005/08/addressing">
123456789
</ns4:TicketId>
</ns3:ReferenceParameters>
<ns3:Metadata>
<wsam:ServiceName
xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata"
xmlns:axis2ns5="http://calculator.jaxws.developerworks"
xmlns:wsa="http://www.w3.org/2005/08/addressing"
EndpointName="CalculatorPort">
axis2ns5:Calculator
</wsam:ServiceName>
</ns3:Metadata>
</return>
</ns4:getTicketResponse>
</soapenv:Body>
在清單 13 中,我們看到在調用 add() 方法時消息被發送。注意 TicketId 包含在消息頭中。還要注意該動作與清單 8 中 add 方法中的 @Action 注釋的輸入動作匹配。
清單 13. Add Request
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header xmlns:wsa="http://www.w3.org/2005/08/addressing">
<ns4:TicketId xmlns:ns4="http://calculator.jaxws.developerworks"
xmlns="http://calculator.jaxws.developerworks"
xmlns:ns2="http://schemas.xmlsoap.org/ws/2004/08/addressing"
xmlns:ns3="http://www.w3.org/2005/08/addressing"
ns3:IsReferenceParameter="true">123456789</ns4:TicketId>
<wsa:To>http://L32H82K:9080/CalculatorService/Calculator</wsa:To>
<wsa:MessageID>urn:uuid:14C1B8561B19D0B1261258998516588</wsa:MessageID>
<wsa:Action>http://calculator.jaxws.developerworks/add</wsa:Action>
</soapenv:Header>
<soapenv:Body>
<ns4:add xmlns:ns2="http://schemas.xmlsoap.org/ws/2004/08/addressing"
xmlns:ns3="http://www.w3.org/2005/08/addressing"
xmlns:ns4="http://calculator.jaxws.developerworks">
<arg0>1</arg0>
<arg1>2</arg1>
</ns4:add>
</soapenv:Body>
</soapenv:Envelope>
在清單 14 中顯示了最終的響應消息,其中包含預期的結果和清單 8 中設置的輸出動作。
清單 14. Add Response
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header xmlns:wsa="http://www.w3.org/2005/08/addressing">
<wsa:Action>http://calculator.jaxws.developerworks/addResponse</wsa:Action>
<wsa:RelatesTo>urn:uuid:14C1B8561B19D0B1261258998516588</wsa:RelatesTo>
</soapenv:Header>
<soapenv:Body>
<ns4:addResponse xmlns:ns2="http://schemas.xmlsoap.org/ws/2004/08/addressing"
xmlns:ns3="http://www.w3.org/2005/08/addressing"
xmlns:ns4="http://calculator.jaxws.developerworks">
<return>3</return>
</ns4:addResponse>
</soapenv:Body>
</soapenv:Envelope>
在清單 15 中顯示了在使用 WS-Addressing Member Submission 規范 EndpointReference 時發送的相應消息。
清單 15. WS-Addressing Member Submission 規范
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header xmlns:wsa="http://www.w3.org/2005/08/addressing">
<wsa:To>http://localhost:9080/CalculatorService/Calculator</wsa:To>
<wsa:MessageID>urn:uuid:14C1B8561B19D0B1261258998516636</wsa:MessageID>
<wsa:Action>
http://calculator.jaxws.developerworks/Calculator/getSubmissionTicketRequest
</wsa:Action>
</soapenv:Header>
<soapenv:Body>
<ns4:getSubmissionTicket
xmlns:ns2="http://schemas.xmlsoap.org/ws/2004/08/addressing"
xmlns:ns3="http://www.w3.org/2005/08/addressing"
xmlns:ns4="http://calculator.jaxws.developerworks"/>
</soapenv:Body>
</soapenv:Envelope>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header xmlns:wsa="http://www.w3.org/2005/08/addressing">
<wsa:Action>
http://calculator.jaxws.developerworks/Calculator/getSubmissionTicketResponse
</wsa:Action>
<wsa:RelatesTo>urn:uuid:14C1B8561B19D0B1261258998516636</wsa:RelatesTo>
</soapenv:Header>
<soapenv:Body>
<ns4:getSubmissionTicketResponse
xmlns:ns2="http://schemas.xmlsoap.org/ws/2004/08/addressing"
xmlns:ns3="http://www.w3.org/2005/08/addressing"
xmlns:ns4="http://calculator.jaxws.developerworks">
<return>
<ns2:Address>
http://L32H82K:9080/CalculatorService/Calculator
</ns2:Address>
<ns2:ReferenceParameters>
<ns4:TicketId xmlns="http://calculator.jaxws.developerworks"
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">
123456789
</ns4:TicketId>
</ns2:ReferenceParameters>
<ns2:ServiceName PortName="CalculatorPort">
ns4:Calculator
</ns2:ServiceName>
</return>
</ns4:getSubmissionTicketResponse>
</soapenv:Body>
</soapenv:Envelope>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">
<ns4:TicketId xmlns:ns4="http://calculator.jaxws.developerworks"
xmlns="http://calculator.jaxws.developerworks"
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"
xmlns:ns2="http://schemas.xmlsoap.org/ws/2004/08/addressing"
xmlns:ns3="http://www.w3.org/2005/08/addressing">123456789</ns4:TicketId>
<wsa:To>http://L32H82K:9080/CalculatorService/Calculator</wsa:To>
<wsa:ReplyTo>
<wsa:Address>
http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous
</wsa:Address>
</wsa:ReplyTo>
<wsa:MessageID>urn:uuid:14C1B8561B19D0B1261258998516715</wsa:MessageID>
<wsa:Action>http://calculator.jaxws.developerworks/addSubmission</wsa:Action>
</soapenv:Header>
<soapenv:Body>
<ns4:addSubmission xmlns:ns2="http://schemas.xmlsoap.org/ws/2004/08/addressing"
xmlns:ns3="http://www.w3.org/2005/08/addressing"
xmlns:ns4="http://calculator.jaxws.developerworks">
<arg0>1</arg0>
<arg1>2</arg1>
</ns4:addSubmission>
</soapenv:Body>
</soapenv:Envelope>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">
<wsa:To>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:To>
<wsa:MessageID>urn:uuid:A9C31FB6B32D8A45DD1258998519215</wsa:MessageID>
<wsa:Action>
http://calculator.jaxws.developerworks/addSubmissionResponse
</wsa:Action>
<wsa:RelatesTo>urn:uuid:14C1B8561B19D0B1261258998516715</wsa:RelatesTo>
</soapenv:Header>
<soapenv:Body>
<ns4:addSubmissionResponse
xmlns:ns2="http://schemas.xmlsoap.org/ws/2004/08/addressing"
xmlns:ns3="http://www.w3.org/2005/08/addressing"
xmlns:ns4="http://calculator.jaxws.developerworks">
<return>3</return>
</ns4:addSubmissionResponse>
</soapenv:Body>
</soapenv:Envelope>
結束語
在本文中,我們介紹了 JAX-WS 2.1 規范中引入的與 WS-Addressing 有關的新概念,並展示了如何使用它們編寫一個有狀態 web 服務應用 程序。除了支持 JAX-WS 2.1 所需的 WS-Addressing 1.0 規范外,我們還展示了如何擴展一個標准 API 來支持 WS-Addressing Member Submission 規范。
本文配套源碼