一 Web Service 簡(jiǎn)介

1.1什么是Web Service?
從表面上看,Web service 就是一個(gè)應(yīng)用程序,它向外界暴露出一個(gè)能夠通過(guò)Web進(jìn)行調(diào)用的API。這就是說(shuō),你能夠用編程的方法通過(guò)Web來(lái)調(diào)用這個(gè)應(yīng)用程序。我們把調(diào)用這個(gè)Web service 的應(yīng)用程序叫做客戶(hù)。
另一種更精確的解釋?zhuān)篧eb services是建立可互操作的分布式應(yīng)用程序的新平臺(tái)。Web service平臺(tái)是一套標(biāo)準(zhǔn),它定義了應(yīng)用程序如何在Web上實(shí)現(xiàn)互操作性。你可以用任何你喜歡的語(yǔ)言,在任何你喜歡的平臺(tái)上寫(xiě)Web service,只要我們可以通過(guò)Web service標(biāo)準(zhǔn)對(duì)這些服務(wù)進(jìn)行查詢(xún)和訪問(wèn)。Web service平臺(tái)需要一套協(xié)議來(lái)實(shí)現(xiàn)分布式應(yīng)用程序的創(chuàng)建。任何平臺(tái)都有它的數(shù)據(jù)表示方法和類(lèi)型系統(tǒng)。要實(shí)現(xiàn)互操作性,Web service平臺(tái)必須提供一套標(biāo)準(zhǔn)的類(lèi)型系統(tǒng),用于溝通不同平臺(tái)、編程語(yǔ)言和組件模型中的不同類(lèi)型系統(tǒng)。在傳統(tǒng)的分布式系統(tǒng)中,基于界面(interface)的平臺(tái)提供了一些方法來(lái)描述界面、方法和參數(shù)(譯注:如COM和COBAR中的IDL語(yǔ)言)。同樣的,Web service平臺(tái)也必須提供一種標(biāo)準(zhǔn)來(lái)描述Web service,讓客戶(hù)可以得到足夠的信息來(lái)調(diào)用這個(gè)Web service。最后,我們還必須有一種方法來(lái)對(duì)這個(gè)Web service進(jìn)行遠(yuǎn)程調(diào)用。這種方法實(shí)際是一種遠(yuǎn)程過(guò)程調(diào)用協(xié)議(RPC)。為了達(dá)到互操作性,這種RPC協(xié)議還必須與平臺(tái)和編程語(yǔ)言無(wú)關(guān)。
1.2 Web Service用到的技術(shù)
為了實(shí)現(xiàn)平臺(tái)無(wú)關(guān),實(shí)現(xiàn)獨(dú)立的訪問(wèn)Web服務(wù), 業(yè)界制定了一系列技術(shù)標(biāo)準(zhǔn),下面是一些重要的技術(shù):
A.XML
可擴(kuò)展的標(biāo)記語(yǔ)言(XML)是Web service平臺(tái)中表示數(shù)據(jù)的基本格式。它的內(nèi)容與表示的分離十分理想,除了易于建立和易于分析外,XML主要的優(yōu)點(diǎn)在于它既是平臺(tái)無(wú)關(guān)的,又是廠商無(wú)關(guān)的。無(wú)關(guān)性是比技術(shù)優(yōu)越性更重要的:軟件廠商是不會(huì)選擇一個(gè)由競(jìng)爭(zhēng)對(duì)手所發(fā)明的技術(shù)的。
B.SOAP (Simple Object Access Propotol 的簡(jiǎn)稱(chēng))
Web service建好以后,你或者其他人就會(huì)去調(diào)用它,簡(jiǎn)單對(duì)象訪問(wèn)協(xié)議(SOAP)提供了標(biāo)準(zhǔn)的RPC(遠(yuǎn)程過(guò)程調(diào)用協(xié)議)方法來(lái)調(diào)用Web service,SOAP規(guī)范定義了SOAP消息的格式,以及怎樣通過(guò)HTTP協(xié)議來(lái)使用SOAP,SOAP也是基于XML,XML是SOAP的數(shù)據(jù)編碼方式。
C.WSDL
你會(huì)怎樣向別人介紹你的Web service有什么功能,以及每個(gè)函數(shù)調(diào)用時(shí)的參數(shù)呢?你可能會(huì)自己寫(xiě)一套文檔,你甚至可能會(huì)口頭上告訴需要使用你的Web service的人。這些非正式的方法至少都有一個(gè)嚴(yán)重的問(wèn)題:當(dāng)程序員坐到電腦前,想要使用你的Web service的時(shí)候,他們的工具(如Visual Studio)無(wú)法給他們提供任何幫助,因?yàn)檫@些工具根本就不了解你的Web service。解決方法是:用機(jī)器能閱讀的方式提供一個(gè)正式的描述文檔。Web service描述語(yǔ)言(WSDL)就是這樣一個(gè)基于XML的語(yǔ)言,用于描述Web service及其函數(shù)、參數(shù)和返回值。因?yàn)槭腔赬ML的,所以WSDL既是機(jī)器可閱讀的,又是人可閱讀的,這將是一個(gè)很大的好處。一些最新的開(kāi)發(fā)工具既能根據(jù)你的Web service生成WSDL文檔,又能導(dǎo)入WSDL文檔,生成調(diào)用相應(yīng)Web service的代碼。

二 Web service開(kāi)發(fā)框架介紹
        
 2.1 Apache SOAP 與 AXIS,XFire框架介紹及之間的區(qū)別
Apache SOAP
Apache SOAP是SOAP協(xié)議的早期實(shí)現(xiàn)
如果有wsdl文件,但生成不了代碼,或沒(méi)有wsdl文件,這時(shí),只能使用發(fā)送soap包,接收soap包的方式,實(shí)現(xiàn)Web Service接口,可以首選Apache SOAP包
實(shí)際上,采用發(fā)送SOAP包,接收SOAP包實(shí)現(xiàn)的接口效率,遠(yuǎn)遠(yuǎn)大于使用java方法調(diào)用的Web Service。因?yàn)橄到y(tǒng)不需要分析soap包,不需要分析xml,不需要反序列化等操作
因?yàn)樾枰_(kāi)發(fā)人員組裝SOAP包,分析SOAP包,所以對(duì)開(kāi)發(fā)人員來(lái)說(shuō),是最困難的實(shí)現(xiàn)方式
Axis
是Apache WebService項(xiàng)目中的子項(xiàng)目,Axis本質(zhì)上就是一個(gè)SOAP引擎,提供創(chuàng)建服務(wù)器端、客戶(hù)端和網(wǎng)關(guān)SOAP操作的基本框架。Axis是第二代Apache SOAP
Axis2
是新一代WebService框架,Axis 的后續(xù)版本。它設(shè)計(jì)為輕量 SOAP 處理引擎
XFire
是新一代WebService框架,支持一系列Web Service的新標(biāo)準(zhǔn)--JSR181、WSDL2.0 、JAXB2、WS-Security等

三 用XFire進(jìn)行Web Service開(kāi)發(fā)
  3.1 XFire簡(jiǎn)介
       在架構(gòu)上可以把XFire大概分為 Service, Transport 和 Invoker 三個(gè)層面。 Service 層是 XFire 架構(gòu)的靜態(tài)基礎(chǔ),負(fù)責(zé)完成對(duì)服務(wù)的注冊(cè)及其管理。核心的 ServiceRegistry 接口完成對(duì)服務(wù)自身的生命期管理,如注冊(cè)/注銷(xiāo)/獲取等等;而 ServiceFactory 接口則負(fù)責(zé)從具體的 POJO 類(lèi)型,生成實(shí)現(xiàn) Service接口的可被管理的服務(wù)代理。 Transport 層則是 XFire 的外部 IO 處理系統(tǒng)。由 TransportManager 接口對(duì)具體的 Transport 接口實(shí)現(xiàn)進(jìn)行管理,默認(rèn)提供了基于 pipe 的 LocalTransport 和基于 Http 協(xié)議的 SoapHttpTransport。理論上可以任意進(jìn)行擴(kuò)展,例如 XFire 發(fā)布包中還提供了基于 JMS 和 XMPP 的實(shí)現(xiàn)。 Invoker 則是 XFire 的動(dòng)態(tài)調(diào)用層,負(fù)責(zé)在 Transport 層接受到請(qǐng)求后,解析內(nèi)容、調(diào)用合適服務(wù)并最終返回 SOAP 封包給調(diào)用者。運(yùn)行時(shí) Invoker 負(fù)責(zé)維系 Service 和 Transport 之間的聯(lián)系。
因此一個(gè)服務(wù)的生成和注冊(cè)往往類(lèi)似如下代碼:
[code]Service endpoint = serviceFactory.create(clazz);// 根據(jù)具體類(lèi)型創(chuàng)建服務(wù)
XFire.getServiceRegistry().register(endpoint);// 向服務(wù)管理注冊(cè)服務(wù) endpoint.setInvoker(new BeanInvoker(bean));// 設(shè)定服務(wù)調(diào)用模式 [/code]
最基本的XFire 接口,實(shí)際上就是 getServiceRegistry()和getTransportManager() 的封裝。
XFire 中另一塊核心的思想,就是其靈活而強(qiáng)大的 binding 機(jī)制,負(fù)責(zé)完成 Java 類(lèi)型與 SOAP 消息的雙向轉(zhuǎn)換。XFire僅僅內(nèi)建就支持 POJO(Aegis), Castor,JAXB 1.1, JAXB 2.0 和 XMLBeans 多種模式,你可以根據(jù)需求選擇從全自動(dòng)POJO生成,到全手工 xsd 文件定義的不同方式。

3.2 XFire目錄介紹
XFire的官方下載地址: http://XFire.codehaus.org/
從官方網(wǎng)站下載到本地機(jī)器后解壓,目錄結(jié)構(gòu)如下:
XFire-distribution-1.1-beta-1
|____api (javadoc文檔)
|____examples (XFire例子)
|____lib (XFire所需的jars包)
|____modules (XFire模塊)
|____XFire-all-1.1-beta-1.jar
|____幾個(gè)授權(quán)和說(shuō)明TXT文檔

3.3 用XFire進(jìn)行Web service開(kāi)發(fā)步驟
A. Web service服務(wù)器端開(kāi)發(fā)
(1)首先打開(kāi)Eclipse,創(chuàng)建一個(gè)工程 .
(2)將需要的庫(kù)文件copy到工程的lib目錄下
(3) 創(chuàng)建一個(gè)簡(jiǎn)單的java文件
新建HelloService.java文件,該java文件只聲明了一個(gè)簡(jiǎn)單的sayHello(String name)方法,文件內(nèi)容如下:
package com.xiaomin.xfire;
/**

@author 蒲剛敏
*
* 說(shuō)明:一個(gè)簡(jiǎn)單的web service服務(wù)器端程序
*/
public class HelloService {
/**

*說(shuō)明: 傳入一個(gè)名稱(chēng),向其名稱(chēng)打招呼 
*
@param name 名稱(chēng)
*
@return 返回打招呼的內(nèi)容
*/
public String sayHello(String name){
return name+",你好!";
}

注:檢驗(yàn)Java類(lèi)的方法和構(gòu)造函數(shù)是公共的(public)

(4)在WebRoot/WEB-INF/classes目錄下創(chuàng)建META-INF/XFire目錄,然后在META-INF/XFire目錄下創(chuàng)建services.xml文件。XFireConfigurableServlet會(huì)自動(dòng)查找classes下的META-INF\xfire\services.xml配置文件,在這個(gè)XML配置文件中,把要提供服務(wù)類(lèi)或接口進(jìn)行綁定,設(shè)置其名稱(chēng)、命名空間、需要綁定的類(lèi)service.xml文件的內(nèi)容如下 :
         
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xfire.codehaus.org/config/1.0">
 
<service>
  
<name>HelloService</name>
  
<namespace>
   myWebService
  
</namespace>
  
<serviceClass>com.xiaomin.xfire.HelloService</serviceClass>
  
<!--
    <implementationClass>元素記錄實(shí)現(xiàn)接口的Java類(lèi)名。這是一個(gè)可選元素,如果前一個(gè)元素<serviceClass>

填入的是接口,那么此處就要填入相應(yīng)的實(shí)現(xiàn)類(lèi)名。
   <implementationClass> </implementationClass>
   
-->
<!– 身份驗(yàn)證
<inHandlers
>
     
<handler handlerClass="com.xiaomin.xfire.AuthenticationHandler"></handler>
  
</inHandlers> -->
 
</service>
 
<!-- 
 <service>
  <name>HelloTwoService</name>
  <namespace>
   http://localhost:8080/testService/HelloTwoService
  </namespace>
  <serviceClass>com.xiaomin.xife.HelloTwoService</serviceClass>
 </service>
 
-->
</beans>
這個(gè)文檔定義了你要發(fā)布的Web服務(wù),定義了一個(gè)名為HelloService的服務(wù),服務(wù)類(lèi)為com.xiaomin.xfire.HelloService。具體解釋如下:
對(duì)Web服務(wù)的定義包含在元素內(nèi),元素下還有若干子元素。 第一個(gè)子元素是,你可以提供任何有效的xml名字,這個(gè)名字會(huì)被客戶(hù)端程序和服務(wù)器上的其他組件使用。例如,當(dāng)服務(wù)器起來(lái)以后,你可以在瀏覽器上使用這個(gè)名稱(chēng)來(lái)查看WSDL。 下一個(gè)子元素是,任何有效地xml名稱(chēng)都可以,將作為你服務(wù)器的唯一標(biāo)識(shí)變量使用。 元素包含Java類(lèi)名用來(lái)指明方法的簽名。
元素記錄實(shí)現(xiàn)接口的Java類(lèi)名。這是一個(gè)可選元素,如果前一個(gè)元素填入的是接口,那么此處就要填入相應(yīng)的實(shí)現(xiàn)類(lèi)名。

(5)建立WEB.XML文件的配置映射,當(dāng)訪問(wèn)services時(shí),調(diào)用XFireConfigurableServlet來(lái)處理,在web.xml文件中加入如下內(nèi)容:
<servlet>
  
<servlet-name>XFireServlet</servlet-name>
  
<servlet-class>
   org.codehaus.xfire.transport.http.XFireConfigurableServlet
  
</servlet-class>
  
<load-on-startup>0</load-on-startup>
  
</servlet>
  
<servlet-mapping>
  
<servlet-name>XFireServlet</servlet-name>
  
<url-pattern>/services/*</url-pattern>
  
</servlet-mapping>
(6)起動(dòng)TOMCAT進(jìn)行服務(wù)器端的測(cè)試
輸入網(wǎng)址:http://localhost:8080/testService/services/

瀏覽器會(huì)顯示你所綁定的類(lèi)或接口名稱(chēng),出現(xiàn)如下內(nèi)容則服務(wù)器端成功:

Available Services:
HelloService [wsdl]
Generated by XFire ( http://xfire.codehaus.org )

點(diǎn)擊服務(wù)名后面的[wsdl],會(huì)顯示該服務(wù)類(lèi)的詳細(xì)wsdl描述。

B. Web Service 客戶(hù)端開(kāi)發(fā)
(1)創(chuàng)建個(gè)服務(wù)模型接口類(lèi)(HelloServiceInf.java),內(nèi)容如下:
package com.xiaomin.xfire.client;

public interface HelloServiceInf {
public String sayHello(String name);
}
(2)創(chuàng)建測(cè)試類(lèi)(TestClient.java),內(nèi)容如下:
import org.codehaus.xfire.XFireFactory;
import org.codehaus.xfire.client.XFireProxyFactory;
import org.codehaus.xfire.service.Service;
import org.codehaus.xfire.service.binding.ObjectServiceFactory;
import com.xiaomin.xfire.client.HelloServiceInf;
public class TestClient {
public static void main(String[] args){
  
final String  HELLO_URL = "http://localhost:8080/testService/services/HelloService";
  
//創(chuàng)建服務(wù)模型
  Service srvcModel = new ObjectServiceFactory().create(HelloServiceInf.class);
  
//獲取XFire的代理對(duì)象
  XFireProxyFactory factory = new XFireProxyFactory(XFireFactory.newInstance().getXFire());
  
  
try {
  
//通過(guò)proxyFactory,使用服務(wù)模型和服務(wù)終點(diǎn)URL(用于獲得WSDL),我們獲得了服務(wù)的本地代理。這個(gè)代理就是實(shí)際的客戶(hù)端。
  HelloServiceInf hell = (HelloServiceInf) factory.create(srvcModel, HELLO_URL);
  System.out.println(hell.sayHello(
"sky"));
  } 
catch (MalformedURLException e) {
   e.printStackTrace();
  }
}
}
在eclipse運(yùn)行,如在控制臺(tái)輸出:sky,你好!  則表示客戶(hù)端成功!

C. Web service身份驗(yàn)證
這里采用soap header方式
(1)身份驗(yàn)證目的
安全,服務(wù)只能被授權(quán)用戶(hù)訪問(wèn)
(2)服務(wù)器端設(shè)置授權(quán)用戶(hù),首先我們編寫(xiě)服務(wù)端驗(yàn)證類(lèi)繼承AbstractHandler,代碼如下:

package com.xiaomin.xfire;

import org.codehaus.xfire.MessageContext;
import org.codehaus.xfire.handler.AbstractHandler;
import org.jdom.Element;

/**
 * 
 * 
@author 蒲剛敏
 *
 * 說(shuō)明:服務(wù)器端設(shè)置身份驗(yàn)證
 
*/
public class AuthenticationHandler extends AbstractHandler{
   
    
public void invoke(MessageContext cfx) throws Exception{
      
if(cfx.getInMessage().getHeader() == null)
           {
               
throw new org.codehaus.xfire.fault.XFireFault("請(qǐng)求必須包含驗(yàn)證信息",org.codehaus.xfire.fault.XFireFault.SENDER);
           }
           Element token
=cfx.getInMessage().getHeader().getChild("AuthenticationToken");
           
if (token == null
           { 
            
throw new org.codehaus.xfire.fault.XFireFault("請(qǐng)求必須包含身份驗(yàn)證信息", org.codehaus.xfire.fault.XFireFault.SENDER); 
           } 

              String username 
= token.getChild("Username").getValue(); 
              String password 
= token.getChild("Password").getValue(); 
              
try 
              { 
                  
//進(jìn)行身份驗(yàn)證 ,只有abcd@1234的用戶(hù)為授權(quán)用戶(hù)
                 if(username.equals("abcd"&& password.equals("1234"))
                  
//這語(yǔ)句不顯示
                  System.out.println("身份驗(yàn)證通過(guò)");
                 
else throw new Exception();
              } 
              
catch (Exception e) 
              { 
                  
throw new   org.codehaus.xfire.fault.XFireFault("非法的用戶(hù)名和密碼",   org.codehaus.xfire.fault.XFireFault.SENDER); 
              } 

          } 
}

(3)為要進(jìn)行身份驗(yàn)證的服務(wù)綁定Handler,修改Services.xml文件內(nèi)容:
   
<service>
  
<name>HelloService</name>
  
<namespace>
   myWebService
  
</namespace>
  
  
<serviceClass>com.xiaomin.xfire.HelloService</serviceClass>
  
<!--
    <implementationClass>元素記錄實(shí)現(xiàn)接口的Java類(lèi)名。這是一個(gè)可選元素,如果前一個(gè)元素<serviceClass>

填入的是接口,那么此處就要填入相應(yīng)的實(shí)現(xiàn)類(lèi)名。
   <implementationClass> </implementationClass>
   
-->
  
<!—此處為進(jìn)行綁定的Handler-->
   
<inHandlers>
     
<handler handlerClass="com.xiaomin.xfire.AuthenticationHandler"></handler>
  
</inHandlers>

 
</service>
(4)編寫(xiě)客戶(hù)端發(fā)送授權(quán)信息類(lèi)ClientAuthenticationHandler繼承AbstractHandler
代碼:
 
package com.xiaomin.xfire.client;

import org.codehaus.xfire.MessageContext;
import org.codehaus.xfire.handler.AbstractHandler;
import org.jdom.Element;
/**
 * 
 * 
@author 蒲剛敏
 *
 * 說(shuō)明:客戶(hù)端發(fā)送授權(quán)信息
 
*/
public class ClientAuthenticationHandler extends AbstractHandler{

     
private String username = null

     
private String password = null

     
public ClientAuthenticationHandler(){ 

     } 

     
public ClientAuthenticationHandler(String username,String password){ 

         
this.username = username; 

         
this.password = password; 
     } 

     
public void setUsername(String username) { 

         
this.username = username; 

     } 

     
public void setPassword(String password){ 

         
this.password = password; 

     } 

     
public void invoke(MessageContext context) throws Exception { 

         
//為SOAP Header構(gòu)造驗(yàn)證信息
         Element el = new Element("header"); 
         context.getOutMessage().setHeader(el); 
         Element auth 
= new Element("AuthenticationToken"); 
         Element username_el 
= new Element("Username"); 
         username_el.addContent(username); 
         Element password_el 
= new Element("Password"); 
         password_el.addContent(password); 
         auth.addContent(username_el); 
         auth.addContent(password_el); 
         el.addContent(auth); 
     } 
}
(5)在客戶(hù)端獲取了服務(wù)的本地代理之后加入Header,需加入的代碼:
 
XFireProxy proxy = (XFireProxy)Proxy.getInvocationHandler(hell);
  Client client 
= proxy.getClient();
  client.addOutHandler(
new ClientAuthenticationHandler("abcd""1234"));
這樣我們就完成了編碼,下面啟動(dòng)tomcat,運(yùn)行客戶(hù)端代碼,本文為abcd@1234位授權(quán)用戶(hù),使用abcd@1234,可以正常訪問(wèn)WS,如果用錯(cuò)誤帳號(hào),則會(huì)有以下異常
Exception in thread "main" org.codehaus.xfire.XFireRuntimeException: Could not invoke service.. Nested exception is org.codehaus.xfire.fault.XFireFault: 非法的用戶(hù)名和密碼
org.codehaus.xfire.fault.XFireFault: 非法的用戶(hù)名和密碼
    at org.codehaus.xfire.fault.Soap11FaultSerializer.readMessage(Soap11FaultSerializer.java:31)
    at org.codehaus.xfire.fault.SoapFaultSerializer.readMessage(SoapFaultSerializer.java:28)
    at org.codehaus.xfire.soap.handler.ReadHeadersHandler.checkForFault(ReadHeadersHandler.java:111)
    at org.codehaus.xfire.soap.handler.ReadHeadersHandler.invoke(ReadHeadersHandler.java:67)
    at org.codehaus.xfire.handler.HandlerPipeline.invoke(HandlerPipeline.java:131)
    at org.codehaus.xfire.client.Client.onReceive(Client.java:406)
    at org.codehaus.xfire.transport.http.HttpChannel.sendViaClient(HttpChannel.java:139)
    at org.codehaus.xfire.transport.http.HttpChannel.send(HttpChannel.java:48)
    at org.codehaus.xfire.handler.OutMessageSender.invoke(OutMessageSender.java:26)
    at org.codehaus.xfire.handler.HandlerPipeline.invoke(HandlerPipeline.java:131)
    at org.codehaus.xfire.client.Invocation.invoke(Invocation.java:79)
    at org.codehaus.xfire.client.Invocation.invoke(Invocation.java:114)
    at org.codehaus.xfire.client.Client.invoke(Client.java:336)
    at org.codehaus.xfire.client.XFireProxy.handleRequest(XFireProxy.java:77)
    at org.codehaus.xfire.client.XFireProxy.invoke(XFireProxy.java:57)
    at $Proxy0.getUser(Unknown Source)
    at test.ClientTest.main(ClientTest.java:39)