2012年9月26日 星期三

使用 WSDD 來發布服務

使用 WSDD 來發布服務

This document is provided as is. You are welcomed to use it for non-commercial purpose.
Written by: 國立中興大學資管系呂瑞麟

請勿轉貼


在前一個範例中,我們使用了 Axis 的特殊功能--稱之為 instant web service-- 來完成我們的 web service。他的好處在於速度快,只要把 .jws 的檔案放到 Axis 的目錄下就完成了 web service 的佈置,只可惜這個方式我們沒有辦法從 Axis 所提供的 List 功能中看到發布的服務。另外,JWS 還有其他缺點,例如 JWS 無法使用 packages, 而且由於它是在使用的時候才 compile,造成沒辦法即時知道錯誤。因此, 文件上建議如果要長期使用一個服務的話,請使用另一種服務發布的方式, 而這個方式需要使用 Axis 的 Web Service Deployment Descriptor(WSDD)。 使用 WSDD 來發布服務的步驟整理如下(我們使用同樣的範例來說明):
  1. 我將之前的 TimeService.jws 重新命名為 QueryTime.java 以避免可能的 衝突(程式碼如下所示),並將 compile 好的 .class 檔放到 axis\WEB-INF\classes
    import java.util.*;
    
    public class QueryTime {
      // 經過測試,我們也可以把 d.toString() 的值以 XML 的格式回傳,
      // 例如: return "<date>" + d.toString() + "</date>"
      public String getTime() {
        Date d = new Date();
        return "當地時間 " + d.toString();
      }
    }
    
  2. 產生一個註冊用的 WSDD 檔,以我們的範例而言,這個 WSDD 檔的內容 如下,並假設這個檔案的名稱是 deploy-time.wsdd
    01 <deployment xmlns="http://xml.apache.org/axis/wsdd/"
    02     xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
    03
    04   <service name="TimeService" provider="java:RPC"
    04               style="wrapped" use="literal">
    05     <parameter name="className" value="QueryTime" />
    06     <parameter name="allowedMethods" value="*" />
    07   </service>
    08 </deployment>
    
    這個檔案的第 1、2、以及最後一行幾乎都是固定的。第 4 到 7 行代表一個即將 要註冊的 service。由第 4 行可以看出,將要註冊的 service 名稱為 "TimeService", 另外,因為 AXIS 採用 RPC 的方式來傳遞資料,所以 provider 的值為 "java:RPC"。 最後,在第二個 04 行定義了被傳遞的 SOAP 訊息的格式是 wrapped/literal (也就是 document/literal wrapped)的方式,這是目前所知道最容易在不同 web services 平台上交換資料的方式。 這個 service 的參數由第 5 以及 6 行表達,第 5 行說明我們要呼叫的 service 的類別名稱為 "QueryTime",而第 6 行說明這個類別內的 public 方法都可以 呼叫。請注意,service 的類別名稱 "QueryTime" 指的就是之前設計的 QueryTime.java,這兩個一定要一致,否則無法正常運作。 定義完了 WSDD 檔案之後,我們必須利用 Axis 所提供的工具來註冊該服務。為了 安全性的考量,Axis 規定只能在安裝 Axis 的電腦上(也就是提供服務的電腦上) 執行註冊,由於這個程式很長,我們將下列的內容放到一個 adminclient.bat 的批次檔內。
    @echo off
    set TEMP=%CLASSPATH%
    set CLASSPATH=%CLASSPATH%;d:\tomcat\webapps\axis\WEB-INF\lib\axis.jar;d:\tomcat\webapps\axis\WEB-INF\lib\axis-ant.jar;d:\tomcat\webapps\axis\WEB-INF\lib\commons-discovery-0.2.jar;d:\tomcat\webapps\axis\WEB-INF\lib\commons-logging-1.0.4.jar;d:\tomcat\webapps\axis\WEB-INF\lib\jaxrpc.jar;d:\tomcat\webapps\axis\WEB-INF\lib\log4j-1.2.8.jar;d:\tomcat\webapps\axis\WEB-INF\lib\saaj.jar;d:\tomcat\webapps\axis\WEB-INF\lib\wsdl4j-1.5.1.jar
    java org.apache.axis.client.AdminClient -lhttp://localhost:8080/axis/services/AdminService %1
    set CLASSPATH=%TEMP%
    
    然後執行下列指令來完成註冊。
    adminclient.bat deploy-time.wsdd
    
  3. 檢查你所註冊的 service 是否成功。檢查的方式有以下三種:
    • 利用 adminclient.bat list來檢查服務是否註冊成功,執行該指令後 的畫面如下:
      在紅色框框中,就是剛剛註冊的 TimeService。除此之外,我們也可以看出還有其他 服務,例如 AdminService 和 Version。
    • 可以自行開發程式來瀏覽,程式碼如下所示:
      import org.apache.axis.client.AdminClient;
      
      public class ListWSDD {
        public static void main(String[] args) throws Exception {
          String host = "http://localhost:8080";
          String servicepath = "/axis/services";
          String endpoint = host + servicepath;
      
          AdminClient ac = new AdminClient();
          ac.setTargetEndpointAddress(new java.net.URL (endpoint));
          System.out.println(ac.list());
        }
      }
      
      跟之前 adminclient.bat 一樣,我們需要設定適當的 CLASSPATH;為了簡化起見, 請產生如下的兩個檔案,並假設其名稱分別為 compile.batrun.bat
      @echo off
      set TEMP=%CLASSPATH%
      set CLASSPATH=%CLASSPATH%;d:\tomcat\webapps\axis\WEB-INF\lib\axis.jar;d:\tomcat\webapps\axis\WEB-INF\lib\axis-ant.jar;d:\tomcat\webapps\axis\WEB-INF\lib\commons-discovery-0.2.jar;d:\tomcat\webapps\axis\WEB-INF\lib\commons-logging-1.0.4.jar;d:\tomcat\webapps\axis\WEB-INF\lib\jaxrpc.jar;d:\tomcat\webapps\axis\WEB-INF\lib\log4j-1.2.8.jar;d:\tomcat\webapps\axis\WEB-INF\lib\saaj.jar;d:\tomcat\webapps\axis\WEB-INF\lib\wsdl4j-1.5.1.jar 
      javac %*
      set CLASSPATH=%TEMP%
      
      @echo off
      set TEMP=%CLASSPATH%
      set CLASSPATH=%CLASSPATH%;d:\tomcat\webapps\axis\WEB-INF\lib\axis.jar;d:\tomcat\webapps\axis\WEB-INF\lib\axis-ant.jar;d:\tomcat\webapps\axis\WEB-INF\lib\commons-discovery-0.2.jar;d:\tomcat\webapps\axis\WEB-INF\lib\commons-logging-1.0.4.jar;d:\tomcat\webapps\axis\WEB-INF\lib\jaxrpc.jar;d:\tomcat\webapps\axis\WEB-INF\lib\log4j-1.2.8.jar;d:\tomcat\webapps\axis\WEB-INF\lib\saaj.jar;d:\tomcat\webapps\axis\WEB-INF\lib\wsdl4j-1.5.1.jar
      java %*
      set CLASSPATH=%TEMP%
      
      如果你清楚環境變數 CLASSPATH 的設定方式,你也可以直接在控制台進行設定, 如此一來就可以減少設定上的麻煩。完成了以上檔案之後,我們就可以進行編譯以及 執行,編譯以及執行的指令如下:
      compile.bat QueryWSDD.java
      run.bat QueryWSDD
      
      執行後的畫面就像之前執行 adminclient.bat list 的結果一樣。 由於這樣的結果是一份 XML 的文件,我們可以針對結果再進一步處理。
    • AXIS 也提供了瀏覽器的查詢介面,在瀏覽器輸入 http://localhost:8080/axis/servlet/AxisServlet 其畫面如下。或許有人會覺得很奇怪, 這個方式超簡單,為什麼還要之前的兩種方式?答案當然就是看"需求",有時候 自動化的過程是由程式負責的,而不是"人"。



  4. 你也可以把剛剛註冊的服務移除掉。移除的方式很簡單,只要把 下列的 wsdd 檔建立好(假設這個檔案的名稱為 undeploy-time.wsdd), 然後利用剛剛建好的 deploy.bat 來執行 adminclient.bat undeploy-time.wsdd 即可完成移除服務的步驟。undeploy-time.wsdd 的結構很簡單, 除了 <undeployment> 必要的根元素之外,我們只需要定義即將移除的 服務名稱,也就是 TimeService
    <undeployment xmlns="http://xml.apache.org/axis/wsdd/">
      <service name="TimeService"/>
    </undeployment>
    
  5. 請在瀏覽器輸入 http://localhost:8080/axis/services/TimeService?method=getTime,就可以在瀏覽器內看到執行結果,其畫面如下:
  6. 開發客戶端程式:大部分 service 的 client 程式開發都是利用 service 的 WSDL 來開發所需要的 proxy 類別。為了降低程式人員的困難,Axis 提供了一個工具 (WSDL2Java 類別)讓開發人員可以輕易的產生所需的 proxy 類別。請產生如下的 檔案,並將其命名為 wsdl.bat
    @echo off
    set TEMP=%CLASSPATH%
    set CLASSPATH=%CLASSPATH%;d:\tomcat\webapps\axis\WEB-INF\lib\axis.jar;d:\tomcat\webapps\axis\WEB-INF\lib\axis-ant.jar;d:\tomcat\webapps\axis\WEB-INF\lib\commons-discovery-0.2.jar;d:\tomcat\webapps\axis\WEB-INF\lib\commons-logging-1.0.4.jar;d:\tomcat\webapps\axis\WEB-INF\lib\jaxrpc.jar;d:\tomcat\webapps\axis\WEB-INF\lib\log4j-1.2.8.jar;d:\tomcat\webapps\axis\WEB-INF\lib\saaj.jar;d:\tomcat\webapps\axis\WEB-INF\lib\wsdl4j-1.5.1.jar
    java org.apache.axis.wsdl.WSDL2Java %*
    set CLASSPATH=%TEMP%
    
    完成後,請執行以下指令來產生所需的 proxy 類別:
    wsdl.bat http://aa.bb.cc.dd/axis/services/服務名稱?wsdl
    
    在上例中,aa.bb.cc.dd 代表提供服務的主機位置。如果是 IP address,則 WSDL2Java 會在目前的目錄下產生一個 _aa\_bb\_cc\_dd\axis\services\服務名稱 的一系列子目錄,而且所有的 proxy 類別會放在 服務名稱 的目錄內。 如果 aa.bb.cc.dd 是 hostname,則 WSDL2Java 會在目前的目錄下產生一個 aa\bb\cc\dd\axis\services\服務名稱 的一系列子目錄。 以本範例來說,我們輸入的指令如下:
    wsdl.bat http://localhost:8080/axis/services/TimeService?wsdl
    
    來取得 TimeService 的 WSDL,而且經過 WSDL2Java 的處理,它會在 目前路徑下產生了 localhost\axis\services\TimeService 一系列子目錄,並且產生了 QueryTime.java, QueryTimeService.java, QueryTimeServiceLocator.java, TimeServiceSoapBindingStub.java 四個類別。由於這個服務所對應的類別是 QueryTime,所以 QueryTime.java 代表遠端服務的類別(或者物件),這個代表遠端物件的類別會被用來呼叫 operation。 要尋找遠端的服務,client 程式必須經由 QueryTimeServiceLocator 來 找到遠端的服務 QueryTimeService(Axis 採用的名稱規則是:若遠端物件是 QueryTime,則代表該服務的名稱就是 QueryTimeService,而實際上去執行的 Locator 就命名為 QueryTimeServiceLocator;從這樣的命名規則,我們知道 遠端物件的名稱最好不要以 Service 做結尾以避免混淆);而經由 QueryTimeService 的 get 方法(一般來說,get 方法的名稱為 get服務名稱();以本範例來說, 也就是 getTimeService())來找到遠端的物件 QueryTime。 TimeServiceSoapBindingStub 就是 Stub 類別,一般來說, 我們不會直接使用 stub。 有了 proxy 類別之後,客戶端的程式就可以變的非常簡單(呼叫遠端物件的 方式就非常像呼叫 local 端物件的樣子):
    import java.rmi.*;
    import javax.xml.rpc.*;
    import localhost.axis.services.TimeService.*;
    
    public class TimeClient {
      public static void main(String[] args) throws RemoteException, 
                                                    ServiceException {
        // 呼叫服務的方式,一般分為兩個步驟。首先,先初始化一個 Service Locator
        // 物件,再由這個物件的 get 方法,取得代表遠端服務的 stub。
        QueryTimeService service = new QueryTimeServiceLocator();
        QueryTime call = service.getTimeService();
        
        // 由於 stub 在概念上代表遠端服務的物件,所以就可以直接
        // 使用該物件的 get 方法來執行方法 getTime()
        String result = call.getTime();
    
        System.out.println(result);
      }
    }
    
  7. 編譯以及執行該程式的畫面如下,畫面中的警告訊息可以忽略。








Written by: 國立中興大學資管系呂瑞麟



沒有留言:

張貼留言