使用Eclipse和Java SE 6創建獨立Web Services應用程序,第2部分: Web服務客戶端應用程序
開始之前
關於本系列
此系列教程演示如何使用 Java SE 6 創建可輕松地從命令行而不是從 Web 應 用程序服務器容器中運行的獨立 Web 服務服務端和客戶端應用程序。在簡單的 Hello World 示例中,您將利用 Eclipse IDE、Java SE 6 和 Apache Ant 輕松 創建完全可以正常工作的 Web 服務服務端和客戶端應用程序。您還將使用 TCP/IP Monitor 檢查服務器與客戶端之間的通信流量,並使用 Eclipse Web Services Explorer 工具測試 Web 服務。
關於本教程
本教程是本系列的第 2 部分,描述如何創建獨立 Web 服務客戶端應用程序, 用於與第 1 部分中開發和部署的獨立 Web 服務進行通信。本教程逐步講解如何 使用 Eclipse IDE、Java SE 6 和 Ant 開發和部署 Web 服務客戶端應用程序。
目標
完成本教程後,您應該知道:
如何通過使用 Eclipse IDE 生成代碼,使用 Java SE 6 編譯代碼,創建 Web 服務的客戶端。
如何使用 Eclipse IDE 中基於 Java 的構建工具 Ant 運行專門的 Java 命令 ,從本系列第 1 部分發布的 WSDL 生成一些代碼。
如何使用 Eclipse IDE 中的 TCP/IP Monitor 觀察、捕捉和驗證服務器與客 戶機之間的 Web 服務 SOAP 傳輸。
如何在 Eclipse IDE 外直接從命令行運行服務器和客戶端應用程序。
先決條件
本教程包括為具備一些 Java 語言和 Ant 構建的實際應用知識的初級和中級 Java 程序員編寫的簡單步驟。初學者到更高級的 Java 開發人員將獲得一些知識 :學會如何構建、部署和運行獨立 Web 服務服務端和分布式客戶端,以提供防火 牆友好的遠程通信和應用程序處理。
系統需求
要按照示例進行操作,需要下載:
Eclipse IDE for Java EE Developers
Java SE 6
您不必下載 Ant,因為其功能與 Eclipse 打包在一起。本教程使用 Ganymede Package for the Eclipse IDE for Java EE Developers。
創建新項目
您也許還記得,在第 1 部分中,一個 Eclipse 項目包含應用程序的源代碼和 其他相關文件。可以使用項目作為源代碼容器,或者在項目中創建文件夾,以便 組織文件。為了創建 Web 服務客戶端,需要創建一個新的項目:
選擇 File > New > Project。
展開 Java 文件夾並單擊 Java Project(見圖 1)。
圖 1. 在 Eclipse 中創建項目
單擊 Next。
根據提示輸入項目名,例如 wsClientExample,如圖 2 所示。
圖 2. 在 Eclipse 中輸入項目詳細信息
如果 Use default JRE 單選按鈕之前已默認選中,則選擇該單選按鈕;否則 選擇 Use a project specific JRE 單選按鈕,確保它是 Java SE 6。
單擊 Finish 將項目與第 1 部分中安裝的 Java JDK 相關聯。
如果提示切換 Java 透視圖,單擊 Yes。
Eclipse 環境在 Package Explorer 中現在應該有兩個項目,如圖 3 所示。 一個項目是在本教程第 1 部分中創建的,另一個項目是剛才創建的。
圖 3. Eclipse 中的 Project Explorer
右鍵單擊 wsClientExample 項目下的 src 文件夾,然後選擇菜單項 New > Package,為客戶端應用程序包輸入一個名稱,例如 com.myfirst.wsClient ,如圖 4 所示。
圖 4. 在 Eclipse 中創建包
生成 Web 服務客戶端代碼
為了創建客戶端代碼,需要運行 wsimport 任務。和在本系列第 1 部分中一 樣,您將從一個名為 build.xml 的 Ant 腳本中運行該任務:
右鍵單擊項目,選擇 New > File。
輸入名稱 build.xml,然後單擊 Finish(見圖 5)。
右鍵單擊該文件,選擇 Open With > Ant Editor,在 Ant Editor 中打開 該文件。從現在起,每當雙擊該文件,都會在 Ant Editor 中打開它。
圖 5. 創建 build.xml 文件
輸入清單 1 中顯示的 Ant 項目。
清單 1. Ant 腳本
<project default="wsimport">
<target name="wsimport">
<exec executable="{java.home}/../bin/wsimport">
<arg line="-keep -s ./src -p com.myfirst.wsClient
-d ./bin http://localhost:8080/wsServerExample? wsdl"/>
</exec>
</target>
</project>
在運行 Ant build.xml 文件之前,必須首先回到第 1 部分 中創建的項目, 並啟動 RunService 服務。為此,展開該項目,右鍵單擊 RunService 文件,選 擇 Run As > Java Application。
確認 Eclipse IDE 控制台窗口顯示消息說該服務已啟動,如圖 6 所示。
圖 6. 服務運行時的控制台
為了運行 Ant build.xml 文件,返回到本項目(wsClientExample),單擊右 鍵並選擇 Run As > Ant Build,執行該 Ant 文件。
確認 Eclipse Console 窗口中顯示一條 BUILD SUCCESSFUL 消息,如圖 7 所 示。
圖 7. Ant Build Success
返回到 Eclipse 項目,右鍵單擊 wsClientExample 並選擇 Refresh,或者選 中項目並按 F5,刷新項目。現在在 com.myfirst.wsClient 包下應該可以看到生 成的運行客戶端的代碼(見圖 8)。
圖 8. 生成的代碼
在此過程中,wsimport 任務從運行 RunService 時發布的 WSDL 生成 JAX-WS 可移植工件。這就是服務必須首先運行的原因。
wsgen 讀取服務端點類,並生成部署和調用 Web 服務所需的所有工件。
wsimport 讀取 WSDL,並生成開發、部署和調用 web 服務所需的所有工件。
您將在下一節創建的客戶端應用程序中使用這些生成的類。
創建客戶端應用程序
現在,您已經生成了 Web 服務客戶端的代碼,接下來需要在 com.myfirst.wsClient 包下創建使用它的應用程序:
右鍵單擊那個包,選擇選項 New > Class,然後配置它,如圖 9 所示。
圖 9. 創建一個類
將類創建為 public,類中有一個 main 方法。
提供了含有一個類的包之後,便可以開始編寫客戶端代碼,如清單 2 所示。
清單 2. 客戶端應用程序
package com.myfirst.wsClient;
import javax.xml.ws.BindingProvider;
public class SayHelloClient {
public static void main(String args[]) {
SayHelloService shs = new SayHelloService();
SayHello sh = (SayHello) shs.getSayHelloPort ();
((BindingProvider)sh)。getRequestContext()。put (BindingProvider.
ENDPOINT_ADDRESS_PROPERTY, "http://localhost:8080/wsServerExample");
System.out.println( ((BindingProvider)sh)。toString() );
System.out.println(sh.getGreeting("Fiona"));
}
}
運行客戶端應用程序
使用 Eclipse
編寫客戶端應用程序之後,試著在 Eclipse 中運行它:
右鍵單擊 SayHelloClient.java,並選擇 Run As > Java Application。 這時應該會顯示 Eclipse IDE 控制台窗口。如果沒有顯示控制台窗口,從菜單欄 選擇 Window > Show View > Console。此時應該可以看到執行 Web 客戶 端的結果,如圖 10 所示。
圖 10. 運行客戶端應用程序
當運行 SayHelloClient 應用程序時,它創建一個新服務 SayHelloService, 它是由通過清單 1 中的 Ant 腳本運行的 wsimport 任務生成的類之一。然後, 它獲得端口 SayHello,這是調用目標服務端點上的操作的一個代理。然後,該客 戶端獲得請求上下文,將端點地址 http://localhost:8080/wsServerExample 添 加到上下文,這個上下文是用於處理請求消息的一個 map。這裡有兩條 print 語 句,第一條以易於閱讀的格式顯示 SayHello,第二條顯示返回的問候語 Hello Fiona(見圖 10)。
完成時,可以通過在 Eclipse console 視圖中終止 Web 服務來停止它。
使用腳本
為了脫離 Eclipse 運行,可以修改 wsClientExample 的 build.xml,使它在 單獨的 shell 窗口中啟動服務器和客戶端應用程序:
雙擊 build.xml 文件,在 Ant 編輯器中編輯它。
修改該文件,如清單 3 所示。
清單 3. 修改後的 build.xml 文件
<project default="runClient">
<!-- =================================
target: wsimport
================================= -->
<target name="wsimport" description="-->
Read the WSDL and generate the required artifacts">
<exec executable="${java.home}/../bin/wsimport">
<arg line="-keep -s ./src -p com.myfirst.wsClient -d ./bin
http://localhost:8080/wsServerExample? wsdl"/>
</exec>
</target>
<!-- =================================
target: runServer
================================= -->
<target name="runServer" description="-->
Runs the Web service server from a terminal">
<echo>
Running the following command from the terminal to run the server:
${java.home}/bin/java -cp "${basedir}/../wsServerExample/bin"
com.myfirst.wsServer.RunService
</echo>
<exec dir="${java.home}/bin/" executable="cmd" spawn="true"
os="Windows XP" description="runs on XP">
<arg line="start cmd /K start cmd /K" />
<arg line='${java.home}/bin/java -cp
"${basedir}/../wsServerExample/bin"
com.myfirst.wsServer.RunService' />
</exec>
<exec executable="xterm" spawn="true" os="Linux"
description="Runs on Linux">
<arg line="-e ${java.home}/bin/java -cp
'${basedir}/../wsServerExample/bin'
com.myfirst.wsServer.RunService"/>
</exec>
</target>
<!-- =================================
target: pause
================================= -->
<target name="pause" depends="runServer" description="-- >
Pauses briefly while the server starts">
<sleep seconds="5"/>
</target>
<!-- =================================
target: runClient
================================= -->
<target name="runClient" depends="pause" description="-- >
Runs a Web service client from a terminal">
<echo>
Running the following command from the terminal to run the client:
${java.home}/bin/java -cp "${basedir}/bin" com.myfirst.wsClient.SayHelloClient
</echo>
<exec dir="${java.home}/bin/" executable="cmd" spawn="true"
os="Windows XP" description="Runs on XP">
<arg line="start cmd /K start cmd /K" />
<arg line='${java.home}/bin/java -cp "${basedir}/bin"
com.myfirst.wsClient.SayHelloClient' />
</exec>
<exec executable="xterm" spawn="true" os="Linux"
description="Runs on Linux">
<arg line="-hold -e ${java.home}/bin/java -cp '${basedir}/bin'
com.myfirst.wsClient.SayHelloClient" />
</exec>
</target>
</project>
注意:若要在 linux 上運行,必須首先設置 JAVA_HOME;在命令行輸入:set JAVA_HOME=<your/java/home>
新的 build.xml 有兩個新的目標:runServer 和 runClient。您可能已經注 意到,第一行中還更新了 default 目標值,使之不運行 wsimport 任務,而是運 行 runClient 目標。而且,注意 runClient 對 pause 有依賴,這意味著雖然默 認值為 runClient,但首先會運行 pause。pause 任務依賴於 runServer。這樣 便允許在客戶端運行之前進行暫停,以便適當地啟動服務器。所以 runServer 將 首先運行。還有一點要注意的是 os 值。這個值表明將執行哪個操作系統(OS) 命令,它由 Java Virtual Machine(JVM)決定。OS 是在 os.name 系統屬性中 設置的。修改後的 build.xml 腳本只包括 Windows 和 Linux,但是必要時可以 增加適合您環境的其他操作系統,並更改 Ant <exec> 任務。
注意加粗的 <echo> 部分沒有像其他行那樣縮進。這是因為所有字符都 會返回,包括空格字符。這意味著在 console 窗口中顯示的消息將不會有前導空 格(圖 11)。當腳本運行時,它將顯示可以從控制台運行的用於運行服務器應用 程序的命令。
為了測試腳本的執行,可以對客戶端應用程序作一些修改,以便可以運行它, 直到退出。修改如下:
雙擊 SayHelloClient.java,編輯該文件,如以下清單所示:
清單 4. 修改後的 SayHelloClient.java 文件
package com.myfirst.wsClient;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import javax.xml.ws.BindingProvider;
import com.myfirst.wsClient.SayHello;
import com.myfirst.wsClient.SayHelloService;
public class SayHelloClient {
public static void main(String[] args) {
SayHelloService shs = new SayHelloService();
SayHello sh = (SayHello) shs.getSayHelloPort();
((BindingProvider) sh )。getRequestContext()。put (
BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
"http://localhost:8080/wsServerExample");
System.out.println(((BindingProvider) sh)。toString ());
String userName = null;
boolean exit = false;
while (!exit) {
System.out.print("\nPlease enter your name
(type 'quit' to exit): ");
BufferedReader br = new BufferedReader(
new InputStreamReader(System.in));
try {
userName = br.readLine();
} catch (IOException e) {
System.out.println("Error reading name.");
System.exit(1);
}
if (!(exit = userName.trim()。equalsIgnoreCase ("quit") ||
userName.trim()。equalsIgnoreCase("exit"))) {
System.out.println(sh.getGreeting (userName));
}
}
System.out.println("\nThank you for running the client.");
}
}
當客戶端應用程序運行時,它將繼續提示輸入,直到輸入 quit 或 exit。為 了運行 Ant 腳本:
右鍵單擊 build.xml,選擇 Run As > Ant Build。
確認 Eclipse Console 窗口出現一條 BUILD SUCCESSFUL 消息,如圖 11 所 示。
圖 11. Ant 構建成功的消息以及返回的消息
執行 Ant 腳本後,應該會打開兩個命令窗口,一個是服務器應用程序的窗口 (見圖 12),一個是客戶端應用程序的窗口(見圖 13)。客戶端窗口不斷提示 輸入姓名,直到退出應用程序。每當輸入一個姓名,修改後的 SayHelloClient 應用程序通過一個 while 循環進行迭代,在該循環中,它將輸入的姓名發送到服 務器,服務器則返回問候語,返回的問候語在窗口中顯示為 Hello <name> 。
圖 12. 服務器的命令窗口
圖 13. 客戶端的命令窗口
在兩個命令窗口中分別輸入 “quit”,退出服務器和客戶端 Java 應用程序 ,然後關閉每個命令窗口應用程序。
用 SOAP Monitor 監視通信
在 Eclipse 中配置 TCP/IP Monitor
至此,您已經創建了一個服務器和一個客戶端,現在可以使用 Eclipse TCP/IP Monitor 監視 SOAP 傳輸。該監視器是一個簡單的服務器,它監視服務器 與客戶端之間的所有請求和響應,如圖 14 所示。
圖 14. 服務器與客戶端之間的 SOAP 傳輸
要查看該活動,需要選擇 Window > Show View > Other > Debug > TCP/IP Monitor,從而打開 TCP/IP Monitor 視圖,如圖 15 所示。
圖 15. 顯示 TCP/IP Monitor
該視圖將出現在 Eclipse IDE 底部的面板中,如圖 16 所示。
圖 16. 查看 TCP/IP Monitor
要配置 TCP/IP Monitor,可選擇 Windows > Preferences,並展開 Run/Debug,然後單擊 TCP/IP Monitor,如圖 17 所示。
圖 17. 添加 TCP/IP Monitor
從該窗口中(圖 17),可以通過 Start 和 Stop 按鈕管理表中列出的多個 TCP/IP 監視服務器。可以添加、編輯、移除、啟動或停止可用的服務器。Status 列表明監視器是已啟動還是已停止。
勾選窗口中的復選框,以便每當有活動時自動顯示 TCP/IP Monitor 視圖,然 後單擊 Add... 按鈕定義一組新的配置選項,如圖 17 所示。輸入以下詳細信息 ,如圖 18 所示:
圖 18. 配置 TCP/IP Monitor
選項描述
Local monitoring port 是本地計算機上一個唯一的端口號,例如 8081。
Host name 是用於運行服務器的計算機的計算機名或 IP 地址,例如 localhost。
Port 是遠程服務器的端口號,例如 8080。
Type 是從浏覽器發送的請求類型,有 HTTP 和 TCP/IP 兩種選項。
Communication Timeout 是與服務器的 TCP/IP 連接可持續的時間長度,單位 為毫秒。
單擊 OK 保存更改。
更新客戶端代碼
接下來,需要對客戶端代碼作一些更改,以便通過 TCP/IP monitor 重定向 Web 服務。這裡需要更改 Web 服務的端點,因為 TCP/IP monitor 偵聽端口 8081。更新後的代碼如清單 5 所示:
清單 5. 客戶端應用程序
package com.myfirst.wsClient;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import javax.xml.ws.BindingProvider;
import com.myfirst.wsClient.SayHello;
import com.myfirst.wsClient.SayHelloService;
public class SayHelloClient {
public static void main(String[] args) {
SayHelloService shs = new SayHelloService();
SayHello sh = (SayHello) shs.getSayHelloPort();
((BindingProvider) sh )。getRequestContext()。put (
BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
"http://localhost:8081/wsServerExample");
System.out.println(((BindingProvider) sh)。toString ());
String userName = null;
boolean exit = false;
while (!exit) {
System.out.print("\nPlease enter your name
(type 'quit' to exit): ");
BufferedReader br = new BufferedReader(
new InputStreamReader(System.in));
try {
userName = br.readLine();
} catch (IOException e) {
System.out.println("Error reading name.");
System.exit(1);
}
if (!(exit = userName.trim()。equalsIgnoreCase ("quit") ||
userName.trim()。equalsIgnoreCase("exit"))) {
System.out.println(sh.getGreeting (userName));
}
}
System.out.println("\nThank you for running the client.");
}
}
運行 Web 服務
再次運行 Ant 腳本:右鍵單擊之前創建的用於運行服務器和客戶端的 build.xml,並選擇 Run As > Ant Build。
此時再次出現兩個命令窗口,一個是服務器窗口,一個是客戶端窗口。和之前 一樣,輸入姓名。
查看 TCP/IP Monitor 視圖,該視圖看上去應該和下面的圖 19 類似:
圖 19. TCP/IP Monitor 的結果
在該視圖中可以看到通過 TCP/IP Monitor 路由的請求和響應對。為了觀察得 更仔細,清單 6 和 7 顯示了完整的頭部:
清單 6. 請求頭部
POST /wsServerExample HTTP/1.1
SOAPAction: ""
Accept: text/xml, multipart/related, text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Content-Type: text/xml; charset=utf-8
User-Agent: Java/1.6.0
Host: localhost:8081
Connection: keep-alive
Content-Length: 226
<?xml version="1.0" encoding="UTF-8"?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<ns2:getGreeting xmlns:ns2="http://wsServer.myfirst.com/">
<arg0>Fiona</arg0>
</ns2:getGreeting>
</S:Body>
</S:Envelope>
清單 7. 響應頭部
HTTP/1.1 200 OK
Content-type: text/xml; charset=utf-8
Transfer-encoding: chunked
fc
<?xml version="1.0" encoding="UTF-8"?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<ns2:getGreetingResponse xmlns:ns2="http://wsServer.myfirst.com/">
<return>Hello Fiona</return>
</ns2:getGreetingResponse>
</S:Body>
</S:Envelope>
0
可以看到,請求頭部信封中加粗的部分是在客戶端應用程序的命令窗口中輸入 的內容,此處為 Fiona。現在看看響應頭部信封,可以看到返回的響應,此處為 Hello Fiona。
可選活動
可以通過單擊圖 19 中用紅色圈住的圖標,確認 Web 服務 SOAP 傳輸是否遵 從 WS-I。這將提示您保存一個日志文件,之後要驗證該文件是否遵從 WS-I。可 以在 XML 編輯器中打開該日志,查看它的內容。
附錄:Web 服務術語和縮略語概述
WS-I - Web 服務互操作性組織(Web services interoperability)
WS-I 是一個開發的行業組織,專門促進 Web 服務跨平台、操作系統和編程語 言的互操作性。
Envelope(信封)
Envelope 是 SOAP 消息的一部分。它定義一個框架,以描述消息中的內容以 及如何處理消息。SOAP 消息就是一個 Envelope,由 0 個或多個頭部和一個主體 組成。Envelope 是 XML 文檔的頂層元素,是控制信息、消息地址和消息本身的 容器。
Headers(頭部)
頭部包含所有的控制信息。它是 Envelope 的子元素。
Body(主體)
主體包含消息的身份信息及其參數。它是 Envelope 的子元素。
結束語
創建、生成和發布 Web 服務服務器非常簡單,只需使用 Eclipse 和 Java SE 6。借助過這些工具可以輕松地開發簡單的 Web Services 服務器端和客戶端應用 程序。