一 Web Service 簡介

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

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

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

3.2 XFire目錄介紹
XFire的官方下載地址: http://XFire.codehaus.org/
從官方網(wǎng)站下載到本地機器后解壓,目錄結(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
|____幾個授權(quán)和說明TXT文檔

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

@author 蒲剛敏
*
* 說明:一個簡單的web service服務器端程序
*/
public class HelloService {
/**

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

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

(4)在WebRoot/WEB-INF/classes目錄下創(chuàng)建META-INF/XFire目錄,然后在META-INF/XFire目錄下創(chuàng)建services.xml文件。XFireConfigurableServlet會自動查找classes下的META-INF\xfire\services.xml配置文件,在這個XML配置文件中,把要提供服務類或接口進行綁定,設置其名稱、命名空間、需要綁定的類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>元素記錄實現(xiàn)接口的Java類名。這是一個可選元素,如果前一個元素<serviceClass>

填入的是接口,那么此處就要填入相應的實現(xiàn)類名。
   <implementationClass> </implementationClass>
   
-->
<!– 身份驗證
<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>
這個文檔定義了你要發(fā)布的Web服務,定義了一個名為HelloService的服務,服務類為com.xiaomin.xfire.HelloService。具體解釋如下:
對Web服務的定義包含在元素內(nèi),元素下還有若干子元素。 第一個子元素是,你可以提供任何有效的xml名字,這個名字會被客戶端程序和服務器上的其他組件使用。例如,當服務器起來以后,你可以在瀏覽器上使用這個名稱來查看WSDL。 下一個子元素是,任何有效地xml名稱都可以,將作為你服務器的唯一標識變量使用。 元素包含Java類名用來指明方法的簽名。
元素記錄實現(xiàn)接口的Java類名。這是一個可選元素,如果前一個元素填入的是接口,那么此處就要填入相應的實現(xiàn)類名。

(5)建立WEB.XML文件的配置映射,當訪問services時,調(diào)用XFireConfigurableServlet來處理,在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)起動TOMCAT進行服務器端的測試
輸入網(wǎng)址:http://localhost:8080/testService/services/

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

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

點擊服務名后面的[wsdl],會顯示該服務類的詳細wsdl描述。

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

public interface HelloServiceInf {
public String sayHello(String name);
}
(2)創(chuàng)建測試類(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)建服務模型
  Service srvcModel = new ObjectServiceFactory().create(HelloServiceInf.class);
  
//獲取XFire的代理對象
  XFireProxyFactory factory = new XFireProxyFactory(XFireFactory.newInstance().getXFire());
  
  
try {
  
//通過proxyFactory,使用服務模型和服務終點URL(用于獲得WSDL),我們獲得了服務的本地代理。這個代理就是實際的客戶端。
  HelloServiceInf hell = (HelloServiceInf) factory.create(srvcModel, HELLO_URL);
  System.out.println(hell.sayHello(
"sky"));
  } 
catch (MalformedURLException e) {
   e.printStackTrace();
  }
}
}
在eclipse運行,如在控制臺輸出:sky,你好!  則表示客戶端成功!

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

package com.xiaomin.xfire;

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

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

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

          } 
}

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

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

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

import org.codehaus.xfire.MessageContext;
import org.codehaus.xfire.handler.AbstractHandler;
import org.jdom.Element;
/**
 * 
 * 
@author 蒲剛敏
 *
 * 說明:客戶端發(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)造驗證信息
         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)在客戶端獲取了服務的本地代理之后加入Header,需加入的代碼:
 
XFireProxy proxy = (XFireProxy)Proxy.getInvocationHandler(hell);
  Client client 
= proxy.getClient();
  client.addOutHandler(
new ClientAuthenticationHandler("abcd""1234"));
這樣我們就完成了編碼,下面啟動tomcat,運行客戶端代碼,本文為abcd@1234位授權(quán)用戶,使用abcd@1234,可以正常訪問WS,如果用錯誤帳號,則會有以下異常
Exception in thread "main" org.codehaus.xfire.XFireRuntimeException: Could not invoke service.. Nested exception is org.codehaus.xfire.fault.XFireFault: 非法的用戶名和密碼
org.codehaus.xfire.fault.XFireFault: 非法的用戶名和密碼
    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)