鷹翔宇空

          學(xué)習(xí)和生活

          BlogJava 首頁 新隨筆 聯(lián)系 聚合 管理
            110 Posts :: 141 Stories :: 315 Comments :: 1 Trackbacks
          原文引自:http://www-128.ibm.com/developerworks/cn/webservices/ws-deepaxis/

          作者:劉東

          本文主要介紹使用service方式實現(xiàn)Web服務(wù)、復(fù)雜類型參數(shù)或者返回值以及面向消息/文檔的服務(wù)類型,同時還會簡單提及Web服務(wù)的會話管理以及安全問題等等。

          前段時間我的一篇文章《應(yīng)用AXIS開始Web服務(wù)之旅》介紹了如何通過AXIS這個項目來實現(xiàn)Web服務(wù)的功能。該文章主要介紹AXIS的結(jié)構(gòu)、如何使用jws文件的方式開發(fā)一個簡單的Web服務(wù),并用了比較大的篇幅來介紹Web服務(wù)的客戶端編程,應(yīng)該說是使用AXIS開發(fā)Web服務(wù)的入門篇,本文假設(shè)你已經(jīng)看過《應(yīng)用AXIS開始Web服務(wù)之旅》并對AXIS有一定的基礎(chǔ),在這個基礎(chǔ)上我們將要介紹的內(nèi)容有幾個方面包括使用service方式實現(xiàn)Web服務(wù)、復(fù)雜類型參數(shù)或者返回值以及面向消息/文檔的服務(wù)類型,同時還會簡單提及Web服務(wù)的會話管理以及安全問題等等。

          在開始我們的文章之前,我們還需要搭建一個環(huán)境,我們需要一個支持Web服務(wù)的web應(yīng)用程序并假設(shè)名字為axis,如何建立請參照《應(yīng)用AXIS開始Web服務(wù)之旅》文章中的介紹。

          使用定制發(fā)布編寫Web服務(wù)

          使用jws文件的方式編寫Web服務(wù)具有方便、快捷的優(yōu)點(diǎn),它可以很快的將你已有的類發(fā)布成Web服務(wù)。但是更多的時候這并不是一個好的主意,因為這種做法引發(fā)的問題是我們必須要將已有類的源碼發(fā)布出來,因為更多的時候我們并不想這樣做;另外雖然你可以先用工具開發(fā)并調(diào)試完畢一個java文件后再改名為jws,但是這多少有些便扭,而且并不是類中的所有方法你都想發(fā)布成可通過Web服務(wù)來訪問的,這時候你就必須將這些方法的修飾符改為不是public的,這就跟你原有的類不同步,以后的修改將會更加的麻煩。

          在這里我把定制發(fā)布方式稱為service方式,就好像JSP的出現(xiàn)不會使Servlet失寵的道理一樣,有了jws,service方式還是有它的用武之地,而且是大放異彩。發(fā)布一個service方式的Web服務(wù)需要兩部分內(nèi)容:類文件以及Web服務(wù)發(fā)布描述文件。下面我們使用一個簡單的例子來講述這個過程。

          首先我們需要一個service類,這個類跟普通的類沒有任何區(qū)別,下面是我們實現(xiàn)一個城市便民服務(wù)的類,我們需要將CityService類的兩個方法getZip和getTel發(fā)布成Web服務(wù),編譯該文件并把class文件拷貝到<webapp>/WEB-INF/classes對應(yīng)目錄下。

          Package lius.axis.demo;
          /** 
            * 該類實現(xiàn)了城市服務(wù),用于發(fā)布成Web服務(wù) 
            * @author Liudong 
            */
            
            public class CityService {
            /**     
              * 獲取指定城市的郵編     
              * @param city     
              * @return     
              */
              public String getZip(String city) { 
           return "510630";
              }
              
              /**     
                * 獲取指定城市的長途區(qū)號     
                * @param city     
                * @return     
                */ 
              
              public String getTel(String city) {
           return "020"; 
              }
          }
          

          程序已經(jīng)完成,下面是發(fā)布這個Web服務(wù)。打開<webapp>/WEB-INF/server-config.wsdd如果這個文件不存在則創(chuàng)建一個新的文件,內(nèi)容如下:

          <?xml version="1.0" encoding="UTF-8"?>
          <deployment xmlns="http://xml.apache.org/axis/wsdd/" 
          	xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"> 
           <globalConfiguration>  
            <parameter name="adminPassword" value="admin"/>  
            <parameter name="attachments.implementation" 
            	value="org.apache.axis.attachments.AttachmentsImpl"/>  
            <parameter name="sendXsiTypes" value="true"/>  
            <parameter name="sendMultiRefs" value="true"/>  
            <parameter name="sendXMLDeclaration" value="true"/>  
            <parameter name="axis.sendMinimizedElements" value="true"/>  
            <requestFlow>   
             <handler type="java:org.apache.axis.handlers.JWSHandler">    
              <parameter name="scope" value="session"/>   
             </handler>   
             <handler type="java:org.apache.axis.handlers.JWSHandler">    
              <parameter name="scope" value="request"/>    
              <parameter name="extension" value=".jwr"/>   
             </handler>  
            </requestFlow> 
           </globalConfiguration> 
           <handler name="LocalResponder" type="java:org.apache.axis.transport.local.LocalResponder"/> 
           <handler name="URLMapper" type="java:org.apache.axis.handlers.http.URLMapper"/> 
           <handler name="Authenticate" type="java:org.apache.axis.handlers.SimpleAuthenticationHandler"/>   
          
           <service name="city" provider="java:RPC">  
            <!-- 服務(wù)類名 -->   
            <parameter name="className" value="lius.axis.demo.CityService"/>  
            <!-- 允許訪問所有方法 -->  
            <parameter name="allowedMethods" value="*"/> 
           </service>  
           <transport name="http">  
            <requestFlow>   
             <handler type="URLMapper"/>   
             <handler type="java:org.apache.axis.handlers.http.HTTPAuthHandler"/>  
            </requestFlow> 
           </transport> 
           <transport name="local">  
            <responseFlow>   
             <handler type="LocalResponder"/>  
            </responseFlow> 
           </transport>
          </deployment>
          

          其中粗斜體的部分是我們服務(wù)的配置信息,啟動Tomcat并打開瀏覽求訪問地址: http://localhost:8080/axis/services/city?wsdl ,下面是瀏覽器顯示我們Web服務(wù)的WDSL數(shù)據(jù)。



          當(dāng)然了,這個過程比起jws方式來說是稍微麻煩一點(diǎn),你可以把它想象成你發(fā)布一個servlet一樣,創(chuàng)建servlet類然后在web.xml中加入配置信息。





          回頁首


          處理復(fù)雜類型參數(shù)和返回值

          之前我們做的演示程序都很簡單,方法的參數(shù)和返回值都是簡單類型的數(shù)據(jù),但是在實際應(yīng)用過程中往往沒有這么簡單。在使用面向?qū)ο蟮木幊陶Z言時,我們會希望數(shù)據(jù)類型可以是某個對象,比如我們提供一個接口用來發(fā)送短信息,那么我們希望接口的參數(shù)是一個消息對象,這個消息對象封裝了一條信息的所有內(nèi)容包括發(fā)送者、接收者、發(fā)送時間、優(yōu)先級、信息內(nèi)容等等,如果我們把每個內(nèi)容都做成一個參數(shù),那這個接口的參數(shù)可能會非常的多。因此封裝成對象是很有必要的。

          在使用Axis來編寫Web服務(wù)時對復(fù)雜類型數(shù)據(jù)的處理同樣也是非常簡單。Axis要求復(fù)雜類型對象的編寫必須符合JavaBean的規(guī)范,簡單的說就是對象的屬性是通過getter/setter方法來訪問的。來看看下面這個簡單的例子所輸出的WSDL信息有何特殊的地方。為了簡便,我們還是使用jws來編寫,需要編寫三個文件:sms.jws,Message.java,Response.java。

          //文件名:sms.jws
          import lius.axis.demo.*;
          
          public class sms{  
          
          /**  
            * 短信息發(fā)送Web服務(wù)接口  
            */
            
            public Response send(Message msg) throws Exception{
           System.out.println("CONTENT:"+msg.getContent());
           Response res = new Response();
           res.setMessage(msg);
           res.setCode(0);
           res.setErrorText("");
           return res;    
            } 
          }
          

          //Message.javapackage lius.axis.demo;
          
          /** 
            * 欲發(fā)送的信息 
            * @author Liudong 
            */
            
          public class Message {
           private String from;
           private String to;
           private String content;
           private int priority;
           
           public String getContent() {
            return content;
           }
           
           public void setContent(String content) {
            this.content = content;
           } 
           
           public String getFrom() { 
            return from;
           }
           
           public void setFrom(String from) { 
            this.from = from;
           }    
           
           public int getPriority() {
            return priority;
           }
           
           public void setPriority(int priority) {
            this.priority = priority;
           } 
           
           public String getTo() {
            return to;
           } 
           
           public void setTo(String to) {
            this.to = to;
           }
          }
          

          //Response.javapackage lius.axis.demo;
          
          /** 
            * 信息發(fā)送回應(yīng),在這里我們做了一個對Message 類的引用 
            * @author Liudong 
            */
            
          public class Response {
           private int code; 
           
           //發(fā)送結(jié)果代碼 
           private String errorText;
           private Message message;
           
           //發(fā)送的原始信息  
           public int getCode() {
            return code;
           } 
           
           public void setCode(int code) {
            this.code = code;
           } 
           
           public String getErrorText() { 
            return errorText;
           } 
           
           public void setErrorText(String errorText) {   
            this.errorText = errorText;
           } 
           
           public Message getMessage() {
            return message;
           } 
          
           
           public void setMessage(Message message) { 
            this.message = message;
           }
          }
          

          編譯Message.java和Response.java并將編譯后的類文件拷貝到axis/WEB-INF/classes對應(yīng)包的目錄下,sms.jws拷貝到axis目錄,訪問http://localhost:8080/axis/sms.jws?wsdl即可看到WSDL信息,這些信息與之前不同的在于下面列出的內(nèi)容(注意粗斜體部分內(nèi)容):

          <wsdl:types>
          <schema targetNamespace="http://demo.axis.lius" xmlns="http://www.w3.org/2001/XMLSchema">
            <import namespace="http://schemas.xmlsoap.org/soap/encoding/" />
            <complexType name="Message">
              <sequence>
                <element name="content" nillable="true" type="xsd:string" />
                <element name="from" nillable="true" type="xsd:string" />
                <element name="priority" type="xsd:int" />
                <element name="to" nillable="true" type="xsd:string" />
              </sequence>
            </complexType>
            <complexType name="Response">
              <sequence>
                <element name="code" type="xsd:int" />
                <element name="errorText" nillable="true" type="xsd:string" />
                <element name="message" nillable="true" type="tns1:Message" />
              </sequence>
            </complexType>
          </schema>
          </wsdl:types>
          

          這里定義了兩個類型Message和Response,就是我們接口的參數(shù)類型以及返回值的類型。現(xiàn)在再使用WSDL2Java工具來生成客戶端Helper類看看Axis幫我們做了什么?它會自動幫我們生成兩個類Message和Response,包名與類名都跟我們剛才定義的一致,你可以打開看看跟我們剛才編寫的類差別多大?這兩個類添加了很多方法例如getTypeDesc、getSerializer、getDeserializer等等。現(xiàn)在你就可以隨便寫個小程序測試一下了,我們就不在累贅了。Service方式Web服務(wù)的處理跟jws是類似的,不同在于service方式需要在server-config.wsdd添加類型的映射配置,下面給出一個配置的示例,讀者可以根據(jù)實際情況進(jìn)行修改。

          <service name="SmsService" provider="java:RPC">
           <parameter name="className" value="lius.axis.demo.SmsService"/>
           <parameter name="allowedMethods" value="send"/>
           <operation name="send" returnType="ns:Response">
            <parameter name="msg" type="ns:Message"/>
           </operation>
           
          <!-- 這里定義了方法的參數(shù)以及返回值 -->
          
           <typeMapping deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory"   
           encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
           qname="ns:Message"
           serializer="org.apache.axis.encoding.ser.BeanSerializerFactory"
           type="java:lius.axis.demo.Message" xmlns:ns="SmsService"/>
           
           <typeMapping
           deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory"    
           	encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
           qname="ns:Response"    
           serializer="org.apache.axis.encoding.ser.BeanSerializerFactory"    
           type="java:lius.axis.demo.Response" xmlns:ns="SmsService"/>
           </service>
          
          

          其他編程語言也都可以借助語言本身所附帶的工具來生成這些復(fù)雜類型,如果你嫌麻煩的話可以使用XML來描述這些復(fù)雜類型,這樣就簡單很多。





          回頁首


          面向消息/文檔的Web服務(wù)類型

          我們前面介紹的服務(wù)方式是基于RPC(遠(yuǎn)程過程調(diào)用)方式,這也是Web服務(wù)最常用的方式。面向消息/文檔的的類型跟RPC不同的是它提供了一個更底層的抽象,要求更多的編程工作。客戶端可以傳入任何的XML文檔,得到的響應(yīng)不一定是SOAPEnvelope,可以返回任何它所需要的東西,甚至不返回。雖然這對開發(fā)者來說非常的靈活,但是這種通訊類型在實際的應(yīng)用中并不常見。面向消息/文檔的Web服務(wù)主要適合于下面幾種情況,比如批量處理,基于表單的數(shù)據(jù)導(dǎo)入,有需要返回非XML數(shù)據(jù)時,Web服務(wù)器實現(xiàn)中要求直接訪問傳輸層等等。

          對于RPC類型的服務(wù)需要在全局配置文件server-config.wsdd中設(shè)置一行<service ... provider="java:RPC">,其中RPC就是服務(wù)的方式,而對于面向消息/文檔的服務(wù)類型那java:RPC就要替換成為Message,并且面向消息/文檔的服務(wù)類型必須通過WSDD配置來發(fā)表。對于完成面向消息服務(wù)的類,其方法必須具有以下的格式:

          public Element[] methodName(Element [] elems)
          

          其中methodName為你自定義的方法名。在Axis的目錄下可以找到MessageService.java,這就是一個完成了該類型的服務(wù)類,該服務(wù)的在WSDD中的配置如下:

          <deployment name="test" xmlns="http://xml.apache.org/axis/wsdd/"      
          xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"
          xmlns:xsi="http://www.w3.org/2000/10/XMLSchema-instance">
           <service name="MessageService" style="message">
            <parameter name="className" value="samples.message.MessageService"/>
            <parameter name="allowedMethods" value="methodName"/> 
           </service>
          </deployment>
          

          不管是什么內(nèi)容的Web服務(wù),對客戶端來說都是一樣的,使用WSDL2Java來生成的客戶端Helper類的MessageService接口如下:

          /** 
            * MessageService.java 
            * 
            * This file was auto-generated from WSDL 
            * by the Apache Axis WSDL2Java emitter. 
            */
            
          package liudong.axis.services.MessageService;
          
          public interface MessageService extends java.rmi.Remote {
           public java.lang.Object echoElements(java.lang.Object part) throws java.rmi.RemoteException;
          }
          





          回頁首


          我從哪里可以獲得…

          Axis文檔中有一句話很有意思,對于絕大多數(shù)類似于"我從哪里可以獲得…"的問題,答案都在MessageContext類中。通過MessageContext類你可以獲取下面幾個內(nèi)容,一個AxisEngine實例的引用;請求以及回應(yīng)的信息;驗證信息以及對于Servlet規(guī)范中的實例引用等等。例如當(dāng)我們需要客戶端的IP地址時我們可以通過下面代碼片段獲取:

          /**      
            * 獲取客戶端請求     
            * @return     
            */
            
          private HttpServletRequest getRequest() throws Exception{
           MessageContext context = MessageContext.getCurrentContext();
           HttpServletRequest req = (HttpServletRequest)context.getProperty(HTTPConstants.MC_HTTP_SERVLETREQUEST);        
           return req.getRemoteHost();
          }
          

          在類HTTPConstants中,所有以MC_開頭的常量就是你所可以獲取到的信息,例如上面通過MC_HTTP_SERVLETREQUEST獲取對應(yīng)Servlet規(guī)范中的HTTP請求。更詳細(xì)的信息可以通過查詢API文檔獲取。





          回頁首


          Web服務(wù)會話管理

          在Web服務(wù)中我們可以借助HTTP以及HTTP Cookie來處理會話信息。前面我們介紹了大多數(shù)對Axis的管理都是通過MessageContext實例來完成的。下面的例子首先驗證用戶的登錄帳號與口令如果正確則在會話中保存用戶的登錄信息,并提供接口供客戶端獲取密碼。

          import org.apache.axis.MessageContext;
          import org.apache.axis.session.Session;
          
          public class login{ 
           public boolean login(String user, String pass){ 
            MessageContext mc = MessageContext.getCurrentContext();
            Session session = mc.getSession();
            session.set("user",user);
            
            //保存用戶名與口令
            session.set("pass",pass);
            return true;
           }
           
           public String getPassword(String user){
            MessageContext mc = MessageContext.getCurrentContext();
            Session session = mc.getSession();
            if(user.equals(session.get("user"))) 
             return (String)session.get("pass");
            return null;
           }
          }
          

          對于服務(wù)器端來講只需要通過MessageContext實例獲取Session對象即可進(jìn)行會話級的數(shù)據(jù)保存或者讀取,而對于通過Axis的WSDL2Java工具生成的客戶端來講還需要做一個特殊的設(shè)置,請看下面客戶端代碼片段。

          public static void main(String[] args) throws Exception {
           LoginServiceLocator lsl = new LoginServiceLocator();
                  lsl.setMaintainSession(true);
                  Login login = lsl.getlogin();
                  if(login.login("ld","haha")) 
            System.out.println("PWD:"+login.getPassword("ld"));
                  else  
            System.out.println("Login failed.");
          }
          

          代碼中的粗體部分就是讓Axis幫助我們自動處理服務(wù)器返回的Cookie信息以保證會話正常工作。





          回頁首


          保護(hù)Web服務(wù)

          網(wǎng)絡(luò)的安全問題永遠(yuǎn)是需要最先考慮的問題,可是怎么能讓我們的Web服務(wù)更加安全呢?為此Axis建議可以根據(jù)實際的需要采取以下的幾種方法。

          1. 使用HTTPS傳輸方式 該方式需要在Web服務(wù)器上進(jìn)行配置同時需要客戶端的支持。該措施有效的防止數(shù)據(jù)在網(wǎng)絡(luò)傳輸過程中被窺視。
          2. 重命名Axis已有的一些名字,例如AdminService、AxisServlet,刪除Axis目錄下一些無用的程序,例如happyaxis.jsp以及一些無用的jar包等。
          3. 通過設(shè)置axis.enableListQuery的值為false來停止AxisServlet列出所有服務(wù)的功能。
          4. 禁止自動生成WSDL的功能
          5. 使用過濾器來增加一些驗證功能,例如客戶端的地址等。

          最常用的不外乎上面幾個,至于更詳細(xì)的資料可以參考Axis解壓目錄下的docs/reference.html文件的詳細(xì)介紹。





          回頁首


          參考資料


          posted on 2006-05-10 23:16 TrampEagle 閱讀(426) 評論(0)  編輯  收藏 所屬分類: web
          主站蜘蛛池模板: 大关县| 枝江市| 辽宁省| 伊春市| 襄汾县| 宁阳县| 新营市| 崇信县| 边坝县| 清流县| 化州市| 祁门县| 明溪县| 汉沽区| 博客| 兴文县| 周口市| 林口县| 墨玉县| 封丘县| 新平| 郑州市| 玛多县| 兴安盟| 长寿区| 赞皇县| 龙海市| 台江县| 镇江市| 岱山县| 彭山县| 剑河县| 高雄市| 杂多县| 湾仔区| 安多县| 翁源县| 宜州市| 岳阳市| 诸暨市| 黄浦区|