顯示具有 AXIS 標籤的文章。 顯示所有文章
顯示具有 AXIS 標籤的文章。 顯示所有文章

2012年9月26日 星期三

處理 SOAP Header

處理 SOAP Header

This document is provided as is. You are welcomed to use it for non-commercial purpose.

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

請勿轉貼


目錄

  1. 處理 SOAP Header
    1. 首先建立一個沒有 SOAP Header 的 web service 以及 client
  2. SOAP Header 的處理流程
  3. 開發 .NET client 來處理 SOAP Header
    1. 開發一個回傳 SOAP Header 的 service 以及使用 SOAP Header 的 .NET client
    2. 開發 .NET client 來處理 AXIS service 傳來的 SOAP Header



處理 SOAP Header

我們在之前的說明中,著重在如何開發 Axis 的服務以及客戶端程式。 可是在實務上,我們需要更多的功能:例如,SOAP 封包內如果包含機密 內容而不希望被惡意人士擷取、或者我們希望能夠控制只允許某些特定人士 才能使用服務等等。為了能夠達成以上目標,我們必須能夠善用 SOAP Header。 參考資料:

AXIS 和 .NET 的互通性

AXIS 和 .NET 的互通性

This document is provided as is. You are welcomed to use it for non-commercial purpose.

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

請勿轉貼


目錄

  1. 開發 ASP.NET 的客戶端來存取 AXIS 的服務: simple datatypes
  2. 開發與使用回傳字串陣列的 AXIS 服務
    1. 開發 AXIS 客戶端程式
    2. 開發 .NET 客戶端程式
  3. 開發與使用回傳 JavaBean 的 AXIS 服務
    1. 開發 AXIS 客戶端程式
    2. 開發 .NET 客戶端程式
  4. 開發與使用回傳 JavaBean 陣列的 AXIS 服務
    1. 開發 AXIS 客戶端程式
    2. 開發 .NET 客戶端程式



開發 ASP.NET 的客戶端來存取 AXIS 的服務

開發 Web Services 的最主要的目的之一,就是希望能達到跨平台性, 然而可惜的是:事實並非如此,因為每一個廠商的 implementation 或多或少有一些差異。目前,在 Java 的世界,Axis 是最常用的 web service 平台(根據 Axis 的 FAQ,採用 Axis 技術的商用軟體包含 BEA's Weblogic Platform, Borland, IBM WebSphere, Oracle Web Services Manager, JBoss 等);而在微軟的世界裡, .NET 卻是最常見的 web services 平台;因此,這兩者之間的 interoperability (稱為互通性)就更形重要。在前一節的文章中,我已經介紹了如何開發 Axis 客戶端 程式來存取 .NET 的服務;在本文中,,我會敘述如何開發 ASP.NET 的客戶端程式 來存取 Axis 的服務 我參考的主要資料來源為: 為了達到跨平台性,有幾個地方要特別注意的:
  1. 如之前說過的,傳送的 SOAP 資料格式必須設定成 wrapped/literal。這是因為 .NET 的預設傳送格式是 wrapped/literal,而 AXIS 卻不是。
  2. 目前確定可以交換的資料型態包含 Java 的 String、boolean、byte、short、int、long、float、以及 double。另外,上述資料型態的陣列,JavaBean,以及 JavaBean 的陣列也可以正確的傳送。
  3. 避免交換 multi-dimensional 和 jagged 陣列,以及避免利用 overloading 來 定義 web services。
我們的測試環境是:Windows XP Professional SP2 + IIS 5.1 + .NET Framework 2.0 + .NET Framework SDK 2.0。 開發的步驟包含:
  1. 利用 WSDL 檔案來產生 ASP.NET 的 proxy 檔案(這部份的概念跟 Axis 相同, ASP.NET 的程式也是利用 proxy 類別來跟遠端的服務來連結的,在本範例中, QueryTimeService.vb 就是 proxy 類別)。產生 proxy 類別的方式很簡單, 你只要使用 .NET Framework SDK 2.0 所提供的 wsdl.exe 即可;執行的指令為 wsdl /l:vb /protocol:SOAP http://localhost:8080/axis/services/TimeService?wsdl。你也可以不必使用 HTTP 存取 WSDL 檔,WSDL 檔也可以是一個 local 的檔案。由於我們開發的程式是以 VB 來撰寫,因此才有 /l:vb 的選項。
  2. 利用剛剛產生的原始碼來編譯並產生 .dll 檔(稱為 assembly)。 執行的指令為 vbc /out:bin\QueryTimeService.dll /t:library /r:System.Web.Services.dll /r:System.Xml.dll QueryTimeService.vb;QueryTimeService.vb 是前一個步驟所產生的原始碼,而這個步驟會產生一個 QueryTimeService.dll 的 .dll 檔。而這個 .dll 檔的儲存位置在目前這個路徑底下,名稱為 bin 的子目錄 內(由於執行 ASP.NET 的程式時,ASP 會自動從 bin 子目錄內去尋找所需的 .dll 檔,因此我們建議不要設定其它的輸出路徑)。假設我們執行第一和第二步驟 的路徑是 c:\Inetpub\wwwroot,則 QueryTimeService.vb 位於 c:\Inetpub\wwwroot,而 QueryTimeService.dll 位於 c:\Inetpub\wwwroot\bin
  3. 開發 ASP.NET 的客戶端程式。如果剛剛 QueryTimeService.dll 位於 c:\Inetpub\wwwroot\bin,則下列的 ASP.NET 的程式必須置放於 c:\Inetpub\wwwroot
    <%@Page Language="VB" %>
    <script runat=server>
    Sub Page_Load(o As Object, e As EventArgs)
      ' 這個是 Axis 的 service
      Const URL as String ="http://hostname:8080/axis/services/TimeService"
    
      ' QueryTimeService 的類別已經由 wsdl.exe 和 vbc.exe 產生
      ' 這個 QueryTimeService.dll 必須放在這個 .aspx 檔的同一個
      ' 目錄底下的 bin 目錄;依照我們的範例,就是放在
      ' c:\Inetpub\wwwroot\bin 內
      Dim service as New QueryTimeService()
    
      ' 設定服務的 URL
      service.Url = URL
    
      ' 呼叫服務的 getTime()
      Dim Answer As String = service.getTime()
    
      Label1.Text = Answer
    End Sub
    </script>
    <html>
    <body>
    <h2 align="center">ASP SOAP Client</h2>
    <hr/>
    <font color="gray" size="5">
    <asp:Label id="Label1" runat="server" />
    </font>
    <p/><p/>
    </body>
    </html>
    
  4. 執行這個 ASP.NET 的程式會得到如下的畫面:

開發與使用回傳字串陣列的 AXIS 服務

由之前的討論,Axis 和 .NET 之間可以互傳字串陣列、符合 JavaBean 規格 的物件、以及物件陣列,我們就依序測試。首先測試的是字串陣列:
  1. AXIS service: ArrayService.java 該服務會將服務的時間,以年、月、日、 時、分、秒的字串陣列回傳。請注意,由於 AXIS 的預設狀況 為 "Request" scope,也就是每一次 service 被呼叫的時候,AXIS 都會產生 一個新的 ArrayService 的物件,因此,每一次呼叫這個 service 都會回傳 最新的時間。
    import java.util.*;
    
    public class ArrayService {
      private String[] date;
    
      public ArrayService() {
        Calendar now = Calendar.getInstance();
        date = new String[6];
        date[0] = String.valueOf(now.get(Calendar.YEAR));
        date[1] = String.valueOf(now.get(Calendar.MONTH) + 1);
        date[2] = String.valueOf(now.get(Calendar.DATE));
        date[3] = String.valueOf(now.get(Calendar.HOUR));
        date[4] = String.valueOf(now.get(Calendar.MINUTE));
        date[5] = String.valueOf(now.get(Calendar.SECOND));
      }
    
      public String[] getTimeArray() {
        return date;
      }
    }
    
  2. 註冊服務,該服務的 WSDD 檔 deploy.wsdd 如下所示:
    <deployment xmlns="http://xml.apache.org/axis/wsdd/"
        xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
      <service name="ArrayService" provider="java:RPC" style="wrapped" use="literal">
        <parameter name="className" value="ArrayService" />
        <parameter name="allowedMethods" value="*" />
      </service>
    </deployment>
    

  3. 將 service 佈置好了以後,就可以開始開發客戶端程式。
  4. 開發 AXIS 的客戶端程式
    1. 我們利用 AXIS 所提供的 WSDL2Java 的工具來產生所需要的 proxy 類別,然後開發客戶端程式如下:
    2. ArrayClient.java
      import java.rmi.*;
      import javax.xml.rpc.*;
      import _aa._bb._cc._cc.axis.services.ArrayService.*;
      
      public class ArrayClient {
        public static void main(String[] args) throws RemoteException, 
                                                      ServiceException {
          ArrayServiceService service = new ArrayServiceServiceLocator();
          ArrayService call = service.getArrayService();
          
          String[] result = call.getTimeArray();
      
          System.out.println(result[0]);
          System.out.println(result[1]);
          System.out.println(result[2]);
        }
      }
      
  5. 開發 .NET 的客戶端程式
    1. 將 service 佈置好了以後,就可以開始開發客戶端程式。過程跟之前所說的 一樣,先利用 wsdl.exe 以及 service 的 WSDL 檔來產生 proxy 類別,然後 在依照 proxy 類別產生 .dll 檔。
    2. VBArrayClient.aspx
      <%@Page Language="VB" %>
      <script runat="server">
      Sub Page_Load(o As Object, e As EventArgs)
        ' 這個是 Axis 的 service
        Const URL as String ="http://hostname:8080/axis/services/StringArray"
      
        Dim service as New ArrayServiceService()
      
        ' 設定服務的 URL
        service.Url = URL
      
        ' 呼叫服務的 getTime()
        Dim Answer() As String = service.getTimeArray()
      
        Label1.Text = Answer(0)
        Label2.Text = Answer(1)
        Label3.Text = Answer(2)
        Label4.Text = Answer(3)
        Label5.Text = Answer(4)
        Label6.Text = Answer(5)
      End Sub
      </script>
      <HTML>
      <BODY>
      <h2 align="center">ASP SOAP Client</h2>
      <hr/>
      <font color="gray" size="5">
      年/月/日: <asp:Label id="Label1" runat="server" />/<asp:Label id="Label2" runat="server" />/<asp:Label id="Label3" runat="server" /><br/>
      時/分/秒: <asp:Label id="Label4" runat="server" />/<asp:Label id="Label5" runat="server" />/<asp:Label id="Label6" runat="server" /><br/>
      </font>
      <p/><p/>
      </body>
      </html>
      

開發與使用回傳 JavaBean(或者複雜的資料型態)的 AXIS 服務

就像之前文件的說明,我們不能任意的傳送 Java Objects,目前只建議 傳送 JavaBean。但是,傳送的 JavaBean 被 SOAP 訊息正確的表達之後 (也就是所謂的 serialize 或者 unmarshall), 我們必須注意 .NET 可以正確的解讀(也就是所謂的 deserialize 或者 unmarshall)。 由於 serialize/deserialize 的問題,我一直無法正確的讀取資料, 這一個問題困擾了我半天,終於在輸入正確的 keywords 之後,Goolge 大神 引導我到這一篇解答: Solved: null responses with interop between Axis service and .NET client。 我們以範例來說明,要如何設計一個符合 JavaBean 的物件,讓 .NET 的 client 能夠正確的從 Axis service 讀取。
  1. 定義一個 JavaBean: DateBean.java 這個物件將會被 Axis service 傳送。
    import java.util.*;
    
    public class DateBean {
      private int year, month, day;
    
      public DateBean() {
        Calendar now = Calendar.getInstance();
        year = now.get(Calendar.YEAR);
        month = now.get(Calendar.MONTH);
        day = now.get(Calendar.DATE);
      }
    
      public int getYear() {
        return year;
      }
    
      public int getMonth() {
        return month + 1;
      }
    
      public int getDay() {
        return day;
      }
    
      public void setYear(int y) {
        year = y;
      }
    
      public void setMonth(int m) {
        month = m;
      }
    
      public void setDay(int d) {
        day = d;
      }
    }
    
  2. 定義服務:我們開發一個簡單的 BeanService.java。請特別留意,在 getDate() 這個方法回傳的是一個 DateBean 物件。
    public class BeanService {
      public DateBean getDate() {
        DateBean d = new DateBean();
        return d;
      }
    }
    
  3. 定義 wsdd 檔:這個檔案有幾個需要特別注意的地方:
    1. 我們必須增加一個 beanMapping 宣告,這個宣告就是下列範例中呈現暗紅色 的這一段。宣告中,明確的說明 DateBean 物件是一個以 Java 所寫出來的物件, 而且它的 QName 是 myNS:DateBean。
    2. 由於 AXIS 和 .NET 的轉換過程中,非常容易出現 namespace 對不上的情形, 而因為我們採用的是先開發 JavaBean 才由 AXIS 轉成相對的 WSDL 檔,又因為 AXIS 的預設行為就是產生一個名為 http://DefaultNamespace 的 targetNamespace,所以在為 DateBean 定義 namespace 的時候,我們就採用 http://DefaultNamespace;否則 .NET 客戶端所接受到的資料會是 0。 希望以後能夠找得到一種經由 AXIS 的設定,可以採用我們自訂的 namespace 的方式。
  4. <deployment xmlns="http://xml.apache.org/axis/wsdd/"
        xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
      <service name="BeanService" provider="java:RPC" style="wrapped" use="literal">
        <parameter name="className" value="BeanService" />
        <parameter name="allowedMethods" value="*" />
    
        <beanMapping qname="myNS:DateBean" xmlns:myNS="http://DefaultNamespace"
                     languageSpecificType="java:DateBean" />
    
      </service>
    </deployment>
    
  5. 將 service 佈置好了以後,就可以開始開發客戶端程式。
  6. 開發 AXIS 的客戶端程式
    1. 過程跟之前所說的一樣,先利用 WSDL2Java 以及 service 的 WSDL 檔來產生 proxy 類別,然後把這些類別 import 到客戶端程式即可。由於我們在 WSDD 內定義 DateBean 的 namespace 是 DefaultNamespace,因此 WSDL2Java 會產生一個 DateBean.java 的程式在 DefaultNamespace 的子目錄內,所以程式中也記得 要把 DefaultNamespace import 進來。
    2. BeanClient.java
      import java.rmi.*;
      import javax.xml.rpc.*;
      import _aa._bb._cc._dd.axis.services.BeanService.*;
      import DefaultNamespace.*;
      
      public class BeanClient {
        public static void main(String[] args) throws RemoteException, 
                                                      ServiceException {
          String URL = "http://aa.bb.cc.dd:8080/axis/services/BeanService";
      
          BeanServiceService service = new BeanServiceServiceLocator();
          BeanService call = service.getBeanService();
          
          DateBean result = call.getDate();
      
          System.out.println(result.getYear());
          System.out.println(result.getMonth());
          System.out.println(result.getDay());
        }
      }
      
  7. 開發 .NET 的客戶端程式
    1. 過程跟之前所說的一樣,先利用 wsdl.exe 以及 service 的 WSDL 檔來產生 proxy 類別,然後依照 proxy 類別產生 .dll 檔。跟 Axis 不一樣的地方,.NET 的 wsdl.exe 並不會單獨在 DefaultNamespace 目錄內,產生 DateBean 的 proxy 類別。
    2. VBBeanClient.aspx
      <%@Page Language="VB" %>
      <script runat="server">
      Sub Page_Load(o As Object, e As EventArgs)
        ' 這個是 Axis 的 service
        Const URL as String ="http://hostname:8080/axis/services/BeanService"
      
        Dim service as New BeanServiceService()
      
        ' 設定服務的 URL
        service.Url = URL
      
        ' 呼叫服務的 getDate()
        Dim Answer As DateBean = service.getDate()
      
        Label1.Text = Answer.year
        Label2.Text = Answer.month
        Label3.Text = Answer.day
      End Sub
      </script>
      <HTML>
      <BODY>
      <h3 align="center">ASP.NET 2.0 SOAP Client</h3>
      <h4 align="center">Process JavaBeans</h4>
      <hr/>
      <font color="gray" size="5">
      年/月/日: <asp:Label id="Label1" runat="server" />/<asp:Label id="Label2" runat="server" />/<asp:Label id="Label3" runat="server" /><br/>
      </font>
      <p/>
      </body>
      </html>
      
    3. 執行後的畫面如下:

開發與使用回傳 JavaBean 陣列的 AXIS 服務

  1. 定義 JavaBean:TimeBean.java
    import java.util.*;
    
    public class TimeBean {
      private int hour, minute, second;
    
      public TimeBean() {
        Calendar now = Calendar.getInstance();
        hour = now.get(Calendar.HOUR);
        minute = now.get(Calendar.MINUTE);
        second = now.get(Calendar.SECOND);
      }
    
      public int getHour() {
        return hour;
      }
    
      public int getMinute() {
        return minute;
      }
    
      public int getSecond() {
        return second;
      }
    
      public void setHour(int h) {
        hour = h;
      }
    
      public void setMinute(int m) {
        minute = m;
      }
    
      public void setSecond(int s) {
        second = s;
      }
    }
    
  2. 開發服務:TimeArray.java 該服務會產生兩個 TimeBean 物件,並以 物件陣列的方式回傳給 client 端。
    import java.io.*;
    
    public class TimeArray {
      public TimeBean[] getTimeArray() throws Exception {
        TimeBean[] res = new TimeBean[2];
        res[0] = new TimeBean();
    
        // 只是為了確保兩個 TimeBean 的物件差一秒
        Thread.sleep(1000);
    
        res[1] = new TimeBean();
        return res;
      }
    }
    
  3. 定義 wsdd 檔
    <deployment xmlns="http://xml.apache.org/axis/wsdd/"
        xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
      <service name="BeanArray" provider="java:RPC" style="wrapped" use="literal">
        <parameter name="className" value="TimeArray" />
        <parameter name="allowedMethods" value="*" />
        <beanMapping qname="myNS:TimeBean" xmlns:myNS="http://DefaultNamespace"
                     languageSpecificType="java:TimeBean" />
      </service>
    </deployment>
    
  4. 將 service 佈置好了以後,就可以開始開發客戶端程式。
  5. 開發 AXIS 客戶端程式
    1. 過程跟之前所說的一樣,先利用 WSDL2Java 以及 service 的 WSDL 檔來產生 proxy 類別,然後將這些類別檔 import 到客戶端程式。
    2. BeanArrayClient.java
      import java.rmi.*;
      import javax.xml.rpc.*;
      import _aa._bb._cc._dd.axis.services.BeanArray.*;
      import DefaultNamespace.*;
      
      public class BeanArrayClient {
        public static void main(String[] args) throws RemoteException, 
                                                      ServiceException {
          String URL = "http://aa.bb.cc.dd:8080/axis/services/BeanArray";
      
          TimeArrayService service = new TimeArrayServiceLocator();
          TimeArray call = service.getBeanArray();
          
          TimeBean[] result = call.getTimeArray();
      
          System.out.print(result[0].getHour() + ":");
          System.out.print(result[0].getMinute() + ":");
          System.out.println(result[0].getSecond());
          System.out.print(result[1].getHour() + ":");
          System.out.print(result[1].getMinute() + ":");
          System.out.println(result[1].getSecond());
        }
      }
      
  6. 開發 .NET 客戶端程式
    1. 過程跟之前所說的 一樣,先利用 wsdl.exe 以及 service 的 WSDL 檔來產生 proxy 類別,然後 在依照 proxy 類別產生 .dll 檔。
    2. 開發 .NET 客戶端程式
      <%@Page Language="VB" %>
      <script runat="server">
      Sub Page_Load(o As Object, e As EventArgs)
        ' 這個是 Axis 的 service
        Const URL as String ="http://hostname:8080/axis/services/BeanArray"
      
        Dim service as New TimeArrayService()
      
        ' 設定服務的 URL
        service.Url = URL
      
        ' 呼叫服務的 getDate()
        Dim Answer() As TimeBean = service.getTimeArray()
      
        Label1.Text = Answer(0).hour
        Label2.Text = Answer(0).minute
        Label3.Text = Answer(0).second
      
        Label4.Text = Answer(1).hour
        Label5.Text = Answer(1).minute
        Label6.Text = Answer(1).second
      End Sub
      </script>
      <HTML>
      <BODY>
      <h3 align="center">ASP.NET 2.0 SOAP Client</h3>
      <h4 align="center">Process JavaBean Arrays</h4>
      <hr/>
      <font color="gray" size="5">
      時/分/秒: <asp:Label id="Label1" runat="server" />/<asp:Label id="Label2" runat="server" />/<asp:Label id="Label3" runat="server" /><br/>
      時/分/秒: <asp:Label id="Label4" runat="server" />/<asp:Label id="Label5" runat="server"/>/<asp:Label id="Label6" runat="server" /><br/>
      </font>
      <p/>
      </body>
      </html>
      


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






呼叫遠端 .NET 的服務

呼叫遠端 .NET 的服務

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

請勿轉貼


在之前的範例中,我們以 Axis 開發 service 並依據該 service 開發 client。在這一篇文章中,我們試著以 Axis 來開發遠端 .NET 服務的 client 程式。我們在另一篇文章會探討如何以 .NET 來開發遠端 Axis 服務的 client 程式。 開發 client 程式的方式跟以前的說明類似,至於可能遇見的狀況,我們 以範例說明。目前提供各式各樣的 web services 的入口網頁有 XMethods (似乎已經結束營業了) 、Lightweight Service Registry、以及 WebserviceX.NET
  1. 範例一:Whois Service 這是一個由 .NET 開發的服務,其提供一個 operations: getWhoIS()。getWhoIS() 需要一個參數,就是查詢的 domain name。

開發回傳字串陣列、物件、以及物件陣列的服務

開發回傳字串陣列、物件、以及物件陣列的服務

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


在之前的範例中,我們可以觀察到只要交換資料的資料型態是基本資料型態 (如 short, int, float, double 等)以及字串,client 的程式都非常 簡單。可是如果是陣列,或者物件(尤其是日期的物件),程式的開發有沒有 困難?
  1. 開發一個會回傳字串陣列的服務,該服務提供一 String[] getTimeArray(String[] input) operation,而該 operation 接受一個字串陣列的參數,並將回傳一 字串陣列。
    1. 服務端:StringArray.java
      import java.util.*;
      
      public class StringArray {
        private String[] date;
      
        public StringArray() {
          Calendar now = Calendar.getInstance();
          date = new String[6];
          date[0] = String.valueOf(now.get(Calendar.YEAR));
          date[1] = String.valueOf(now.get(Calendar.MONTH) + 1);
          date[2] = String.valueOf(now.get(Calendar.DATE));
          date[3] = String.valueOf(now.get(Calendar.HOUR));
          date[4] = String.valueOf(now.get(Calendar.MINUTE));
          date[5] = String.valueOf(now.get(Calendar.SECOND));
        }
      
        public String[] getTimeArray(String[] input) {
          for(int i=0; i<date.length; i++) {
            date[i] = date[i] + " " + input[i];
          }
      
          return date;
        }
      }
      
    2. 產生一個註冊用的 WSDD 檔並註冊,跟之前的類似,故省略。
    3. 利用 WSDL2Java 產生必要的 proxy 類別,跟之前的類似,故省略。
    4. 產生客戶端程式:ArrayClient.java
      import java.rmi.*;
      import javax.xml.rpc.*;
      
      import localhost.axis.services.StrArrayService.*;
      
      public class ArrayClient {
        public static void main(String[] args) throws RemoteException, 
                                                      ServiceException {
          StringArrayService service = new StringArrayServiceLocator();
          StringArray call = service.getStrArrayService();
          
          String[] input = {"年","月","日","時","分","秒"};
          String[] result = call.getTimeArray(input);
      
          for(int i=0; i<result.length; i++)
            System.out.print(result[i] + " ");
          System.out.println();
        }
      }
      


  2. 開發一個回傳物件以及物件陣列的服務,該服務提供 Worker getWorker()、Worker[] getArray()、以及 void setWorker(Worker w) 的 operations.
    1. 服務端:WorkerService.java
      import java.util.*;
      
      public class WorkerService {
        private Worker worker;
      
        public WorkerService() {
          worker = new Worker();
        }
      
        public Worker getWorker() {
          return worker;
        }
      
        public void setWorker(Worker w) {
          worker = w;
        }
      
        public Worker[] getArray() {
          Calendar c = Calendar.getInstance();
          c.set(1978, Calendar.MAY, 17);
          Worker w2 = new Worker("Dave Mentis", 60, c);
          Worker[] w = {new Worker(), w2};
      
          return w;
        }
      }
      
    2. 自訂的類別: Worker.java(為了避免麻煩,我們將 Worker.java 依據 JavaBean 的規範設計,細節討論,請參考 AXIS 與 .NET 互通性)。另外,我們在 Worker.java 也增加了一個日期的資料成員,根據經驗,請不要使用 java.util.Date,而應該使用 java.util.Calendar 或者 java.util.GregorianCalendar。請注意,所謂 JavaBean 的設計規範是說:當開發人員設計一個類別時,請務必設計該類別的預設建構元、 包含所有資料成員當作參數的建構元、以及每一個資料成員都有一對 get/set 方法。 以 Worker.java 為例,由於它有三個資料成員,所以我們需要設計出一個預設建構元 public Worker()、包含所有資料成員當作參數的建構元 public Worker(String n, int h, Calendar c)、資料成員 name 有一對 setName()/getName() 方法、資料成員 hours 有一對 setHours()/getHours() 方法、以及資料成員 birthday 有一對 setBirthday()/getBirthday() 方法。
      import java.util.*;
      
      public class Worker {
        private String name;
        private int hours;
        private Calendar birthday;
      
        public Worker() {
          name = "Eric Lu";
          hours = 40;
          birthday = Calendar.getInstance();
          birthday.set(1988, Calendar.JULY, 15);
        }
      
        public Worker(String n, int h, Calendar c) {
          setName(n);
          setHours(h);
          setBirthday(c);
        }
      
        public String getName() {
          return name;
        }
      
        public int getHours() {
          return hours;
        }
      
        public Calendar getBirthday() {
          return birthday;
        }
      
        public void setName(String n) {
          name = n;
        }
      
        public void setHours(int h) {
          hours = h;
        }
      
        public void setBirthday(Calendar d) {
          birthday = d;
        }
      }
      
    3. WSDD 檔:由於在 WorkerService 中,使用了自訂的類別 Worker,因此在 WSDD 的定義上,需要增加 beanMapping 的定義,該定義會使得 WSDL2Java 產生 恰當的 Worker 檔在 client 端。另外,由於 WSDL 檔內的 targetNamespace 是預設的 http://DefaultNamespace,最好在定義 beanMapping 的時候, 也設定適當的命名空間。
      <deployment xmlns="http://xml.apache.org/axis/wsdd/"
          xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
        <service name="ObjectService" provider="java:RPC" style="wrapped" use="literal">
          <parameter name="className" value="WorkerService" />
          <parameter name="allowedMethods" value="*" />
      
          <beanMapping qname="tns:Worker" xmlns:tns="http://DefaultNamespace"
                       languageSpecificType="java:Worker" />
      
        </service>
      </deployment>
      
    4. 客戶端程式 WorkerClient.java:過程跟之前所說的一樣,先利用 WSDL2Java 以及 service 的 WSDL 檔來產生 proxy 類別,然後把這些類別 import 到客戶端程式 即可。跟之前的範例有一點不太一樣的地方是,這次我們定義了 Worker, 由於我們在 WSDD 內定義它的 namespace 是 DefaultNamespace,因此 WSDL2Java 會產生一個 Worker.java 的程式在 DefaultNamespace 的子目錄內。
      import java.rmi.*;
      import javax.xml.rpc.*;
      
      import localhost.axis.services.ObjectService.*;
      import DefaultNamespace.*;
      import java.util.*;
      
      public class WorkerClient {
        public static void main(String[] args) throws RemoteException, 
                                                      ServiceException {
          WorkerServiceService service = new WorkerServiceServiceLocator();
          WorkerService call = service.getObjectService();
      
          // 先利用 service 設定一個 Worker 物件
          Calendar c = Calendar.getInstance();
          c.set(1970, Calendar.JUNE, 22);
          Worker s = new Worker(c, 55, "Mary Ann"); 
          call.setWorker(s);
          
          // 再取回剛剛的 Worker 物件(???) 
          Worker w = call.getWorker();
          System.out.print(w.getName() + "\t");
          System.out.print(w.getHours() + "\t");
          c = w.getBirthday();
          System.out.println(c.get(Calendar.YEAR) + "/" +
                             (c.get(Calendar.MONTH) + 1) + "/" +
                             c.get(Calendar.DATE) + "\n");
      
          // 取得一個預設的 Worker[] 陣列
          Worker[] ww = call.getArray();
          for(int i=0; i < ww.length; i++) {
            System.out.print(ww[i].getName() + "\t");
            System.out.print(ww[i].getHours() + "\t");
            c = ww[i].getBirthday();
            System.out.println(c.get(Calendar.YEAR) + "/" +
                               (c.get(Calendar.MONTH) + 1) + "/" +
                               c.get(Calendar.DATE));
          }
        }
      }
      
      這個客戶端程式首先利用 setWorker() 設定一個 Worker 物件到 service 端, 然後,試圖從 service 端將剛剛設定好的 Worker 物件取回;最後,再從 service 端取得一個 Worker 物件陣列。 這個程式跟以往的程式大同小異,但是有兩個地方要特別注意,這兩個地方 以棗紅色字體顯示:(1) 雖然在 service 端,Worker 建構元的 參數順序為 name, hours, birthday,但是在 client 端,Worker 建構元的 參數順序卻是 birthday, hours, name。這是因為在 ObjectService 的 WSDL 中(如下圖所示),Worker 的三個 element 依序是 birthday, hours, name。 因此,客戶端的程式必須以 WSDL 的方式撰寫;這其實很合理,因為大部分時間, 程式設計人員是無法知道遠端服務是如何開發的。
      (2) 雖然在程式在第二段中試圖利用 getWorker() 的方式想把剛剛第一段 設定的 Worker 物件取回(也就是 Many Ann),但是執行程式之後,其結果 如以下畫面:
      這個結果清楚顯示,每一次 service 被呼叫,該 service 物件(也就是 WorkerService)都會被重新產生一次,因此取回的 Worker 物件還是預設 建構元所產生的物件。雖然這個特性會使得部分應用變得不方便,但是如果 service 端的服務都配合著資料庫的使用,這個不方便應可以降到最低。
      依據 Axis 的文件,上述情形(每次呼叫 service,該 service 物件都會 重新被產生)稱之為 "request",這是 Axis 的預設行為;Axis 還提供其它 兩種行為模式:一個是 "session"(每一個 session 只有一個 service 物件), 另一個是 "application"(這個 client application 都只使用一個 service 物件)。 由於有可能會有多個程式同時呼叫該 service 物件的方法,使用 "session" 和 "application" 的開發人員必須自行控制可能發生的情形。
      若需要更改 service 物件的產生方式會以 application 為 scope,我們需要 更改 wsdd 的內容,更新後的 WSDD 檔的內容如下:
      <deployment xmlns="http://xml.apache.org/axis/wsdd/"
          xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
        <service name="ObjectService" provider="java:RPC" style="wrapped" use="literal">
      
          <parameter name="scope" value="Application" />
      
          <parameter name="className" value="WorkerService" />
          <parameter name="allowedMethods" value="*" />
          <beanMapping qname="tns:Worker" xmlns:tns="http://DefaultNamespace"
                       languageSpecificType="java:Worker" />
        </service>
      </deployment>
      
      我們建議在註冊(deploy)這個服務之前,請記得先將之前的舊服務先 移除(undeploy)掉;在不更改其他的程式之下,重新執行 WorkerClient, 你將會看到如下的畫面:
      
      
      練習題: 如果將 WorkerService 的 getArray() 中的 Worker[] w = {new Worker(), w2}; 改成 Worker[] w = {worker, w2};,請問在不同的 scope 設定下,輸出的結果 有什麼變化?為什麼?






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



使用 Axis TCP Monitor 來監測 SOAP 訊息

使用 Axis TCP Monitor 來監測 SOAP 訊息

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


SOAP 訊息(SOAP messages)是否已經被傳送出去,或者 SOAP 訊息是否已經被接受, 或者被傳送的 SOAP 訊息的內容以及格式為何,這個時候我們就可以利用 Axis TCP Monitor 來監測 SOAP 訊息。使用 TCP Monitor 的方式很簡單,你只要 在 Axis 伺服器端,產生如下的檔案,並將其命名為 tcpmon.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.utils.tcpmon
set CLASSPATH=%TEMP%
完成後,請執行 tcpmon.bat 即可,執行後,你就可以看到如下的畫面:

在進一步說明 TCPMonitor 的使用方式之前,我們先說明 TCPMonitor 的概念: 在沒有 TCPMonitor 之前,client 會直接呼叫 service;如果 service 的名稱 為 http://mytest.com:8080/testService,那麼 client 程式必須將該服務 名稱設定在程式內。如果想利用 TCPMonitor 來監看送出以及回傳的 SOAP 訊息, client 必須先呼叫 TCPMonitor,然後由 TCPMonitor 呼叫 service;而 service 的回傳資料也會先傳給 TCPMonitor,然後再由 TCPMonitor 回傳給 client。 為了能輕易的完成上列的工作,在畫面中,我們要特別注意兩個比較重要的設定: Listen Port# 以及 Act as a Listener 的 Target Hostname 和 Target Port#。 以之前的範例來說,我們必須在 Listen Port# 的欄位任意輸入一個大於 1024 的整數, 例如 1234,而且在 Target Hostname 要設定為 mytest.com,Target Port# 要設定為 8080;如此一來,client 程式必須將服務名稱改成 http://127.0.0.1:1234/testService,然後當 TCPMonitor 接收到 client 傳來的 SOAP 訊息時,它會將該 SOAP 訊息 轉傳給 http://mytest.com:8080/testService。
假設你已經 deploy 了剛剛的 TimeService,請在 Listen Port# 輸入 1234。 由於 TimeService 服務在 localhost:8080,因此 Target Hostname 和 Target Port# 不需要修改;然後請點選 "Add" 鈕,這時候,程式會新增一個 tab, 請選擇這個 tab 開始監測 SOAP 訊息。
最後,請在瀏覽器內輸入 URL:http://localhost:1234/axis/services/TimeService?method=getTime 來觀察 TCP Monitor, 你應該可以看到類似以下的畫面:


從上述的範例,我們可以知道,雖然我們送出去的要求是到 port 1234,但是 TCP Monitor 會把它轉向 port 8080,然後 web service 執行的結果,會由 port 8080 傳回來,這時候 TCP Monitor 又會把這個訊息轉給 port 1234。 這樣子一來,不但程式可以得到服務,我們又可以經由 TCP Monitor 知道 SOAP 的訊息。

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




加法服務

加法服務

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


在之前的範例中,我們開發了一個回傳字串的服務:TimeService。在這個 範例,我們將提供一個簡單的服務,該服務允許 client 傳給 service 兩個整數, 然後 service 將兩個整數加總後回傳。如果各位是好奇寶寶的話,其實你們可以 嘗試將服務改成其它資料型態、改成減法/乘法、或者攝氏轉華氏的服務等。 發布 AdditionService 服務的步驟整理如下:
  1. 撰寫 Addition.java 如下,並將 compile 好的 .class 檔放到 axis\WEB-INF\classes
    public class Addition {
      public Addition() {
      }
    
      public int addNumbers(int a, int b) {
        return a + b;
      }
    }
    
  2. 產生一個註冊用的 WSDD 檔,以我們的範例而言,這個 WSDD 檔的內容 如下,並假設這個檔案的名稱是 deploy-add.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="AdditionService" provider="java:RPC"
    04               style="wrapped" use="literal">
    05     <parameter name="className" value="Addition" />
    06     <parameter name="allowedMethods" value="*" />
    07   </service>
    08 </deployment>
    
    然後執行下列指令來完成註冊。
    adminclient.bat deploy-add.wsdd
    
  3. 檢查該服務的 WSDL(尤其是比對 document/literal wrapped 的部分):
    <wsdl:types>
      <schema elementFormDefault="qualified" targetNamespace="http://DefaultNamespace" xmlns="http://www.w3.org/2001/XMLSchema">
        <element name="addNumbers">
          <complexType>
            <sequence>
              <element name="in0" type="xsd:int"/>
              <element name="in1" type="xsd:int"/>
            </sequence>
          </complexType>
        </element>
        <element name="addNumbersResponse">
          <complexType>
            <sequence>
              <element name="addNumbersReturn" type="xsd:int"/>
            </sequence>
          </complexType>
        </element>
      </schema>
    </wsdl:types>
    
    <wsdl:message name="addNumbersRequest">
      <wsdl:part element="tns1:addNumbers" name="parameters"/>
    </wsdl:message>
    
    <wsdl:message name="addNumbersResponse">
      <wsdl:part element="tns1:addNumbersResponse" name="parameters"/>
    </wsdl:message>
    
    <wsdl:portType name="Addition">
      <wsdl:operation name="addNumbers">
        <wsdl:input message="impl:addNumbersRequest" name="addNumbersRequest"/>
        <wsdl:output message="impl:addNumbersResponse" name="addNumbersResponse"/>
      </wsdl:operation>
    </wsdl:portType>
    
  4. 測試 service 並檢查 SOAP 訊息:請在瀏覽器上,輸入 http://localhost:8080/axis/services/AdditionService?method=addNumbers&in0=7&in1=6。 剛剛輸入的 URL 有一個地方要注意一下:addNumbers 的兩個參數名稱為 in0 和 in1, 這兩個名稱是從 WSDL 內決定的。執行後,service 回傳的 SOAP 訊息如下:
    <?xml version="1.0" encoding="UTF-8"?>
    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
               xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <soapenv:Body>
        <addNumbersResponse xmlns="">
          <ns1:addNumbersReturn xmlns:ns1="http://localhost:8080/axis/services/AdditionService">13</ns1:addNumbersReturn>
        </addNumbersResponse>
      </soapenv:Body>
    </soapenv:Envelope>
    
  5. 利用 WSDL2Java 來產生所需要的 proxy 類別,並開發 client 程式如下:
    import java.rmi.*;
    import javax.xml.rpc.*;
    import javax.swing.*;
    
    import localhost.axis.services.AdditionService.*;
    
    public class AddClient {
      public static void main(String[] args) throws RemoteException, 
                                                    ServiceException {
        AdditionService service = new AdditionServiceLocator();
        Addition call = service.getAdditionService();
        
        String input = JOptionPane.showInputDialog("第一個整數");
        int num1 = Integer.parseInt(input);
        input = JOptionPane.showInputDialog("第二個整數");
        int num2 = Integer.parseInt(input);
    
        int result = call.addNumbers(num1, num2);
    
        System.out.println("總合 = " + result);
      }
    }
    



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



開發第一個 SOAP 的服務以及 client

開發第一個 SOAP 的服務以及 client

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

請勿轉貼


開始開發 web service 的程式,一般的步驟都是先開發 service 的程式 (也就是伺服器端的程式),然後才開發客戶端的程式。我們進行第一個程式,由 service 提供當地端的時間。提醒大家,這個方式雖然簡單,但是在真正的開發上, 卻很少用,所以看看即可!
  1. service 的程式很簡單,基本上只是使用 java.util.Date 把時間抓出來, 然後回傳時間的字串。Axis 的寫法很簡單,完成下列的 Java 程式,並把程式碼命名為 TimeService.jws(jws 稱之為 Java Web Service)並把這個程式碼放置於 d:\tomcat\webapps\axis 目錄內即完成 service 的佈置。 這個程式提供了一個名稱為 getTime() 的服務。
    import java.util.*;
    public class TimeService {
      public String getTime() {
        Date d = new Date();
        return "Local time is " + d.toString();
      }
    }
      
  2. 在客戶端設定適當的 CLASSPATH 環境變數。我把它寫成一個批次檔 以便於每一次執行的時候,我不必重新輸入一次。請把下列的 set CLASSPATH 指令 串成一行。
    @echo off
    set CLASSPATH=.;d:\axis-1_4\lib\axis.jar;d:\axis-1_4\lib\saaj.jar;
                    d:\axis-1_4\lib\commons-logging-1.0.4.jar;
                    d:\axis-1_4\lib\commons-discovery-0.2.jar;
                    d:\axis-1_4\lib\wsdl4j-1.5.1.jar;
                    d:\tomcat\common\lib\servlet-api.jar;
                    d:\axis-1_4\lib\jaxrpc.jar;
                    d:\tomcat\common\lib\activation.jar;
                    d:\tomcat\common\lib\mail.jar;
                    d:\tomcat\common\lib\xmlsec.jar;
      
  3. 開發客戶端程式 Query.java。(請注意,這種的客戶端程式不常用,常用的 客戶端程式開發方式,將於下節討論)
    import org.apache.axis.client.Call;
    import org.apache.axis.client.Service;
    import org.apache.axis.encoding.XMLType;
    import javax.xml.rpc.ParameterMode;
    
    public class Query {
      public static void main(String[] args) throws Exception {
        // 我們必須知道要連結的端點程式為何,在這個範例中,
        // URL: http://localhost:8080/axis/TimeService.jws 這個程式
        String host = "http://localhost:8080";
        String servicepath = "/axis/TimeService.jws";
        String endpoint = host + servicepath;
    
        String ret = null;
    
        // Service 物件代表一個遠端的服務,即 web service
        Service service = new Service();
    
        // 遠端服務的呼叫是經由一個 Call 的物件,物件的形成方式
        // 就是由 service 物件來產生
        Call call = (Call) service.createCall();
    
        // 開始呼叫以前,先設定連結的服務訊息
        call.setTargetEndpointAddress(new java.net.URL (endpoint));
    
        // 再設定 TimeService.jws 中,我們所需要呼叫的 API,在這個
        // 範例中,就是 getTime() 這個方法
        call.setOperationName("getTime");
    
        // 定義回傳的資料型態。型態的定義為 XML Schema,在
        // 這個範例,回傳的資料型態為字串
        call.setReturnType(XMLType.XSD_STRING);
    
        // 真正的呼叫遠端的服務。由於並沒有傳遞參數到遠端,
        // 因此為 null。回傳的結果,會放到 ret 中。
        ret = (String) call.invoke((Object[])null);
    
        System.out.println("Got result : " + ret);
      }
    }
      
  4. 你可以 compile Query.java 並執行它來確定你的 web service 確定執行了。 執行的畫面如下:
    請注意,畫面中的錯誤訊息是沒有關係的。想要清除錯誤訊息,你需要安裝 activation.jar 和 mail.jar。
  5. 你也可以直接利用 browser 來測試。測試的方式就是直接輸入 http://localhost:8080/axis/TimeService.jws?method=getTime,執行後,你就可以在 browser 上看到 service 回傳的 SOAP 訊息,如下圖所示。





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


使用 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: 國立中興大學資管系呂瑞麟



AXIS 入門

AXIS 入門

This document is provided as is. You are welcomed to use it for non-commercial purpose.
一些感想:這是一份從 Java 觀點所寫的筆記。寫這一份文件的時候, 實在遇到了不少的困難。尤其在 Java (或者更嚴格的說 AXIS)以及 .NET 的整合測試部分,讓我花了不少的心思 與夜晚。尤其找資料的時候,往往微軟的文件都是以 Visual Studio 的環境 來完成,而沒能告訴我它內部是如何達到的,感覺上好像想把 programmers 愚蠢化。唉!哀!
Anyway,如果有我的學生想把這一份筆記更進一步編寫成書,請來找我談。

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

請勿轉貼
看其他教材

目錄

  1. 安裝 Tomcat 和 Axis
  2. 開發第一個 SOAP 的服務以及 client
  3. 使用 WSDD 來發布服務
  4. 使用 Axis TCP Monitor 來監測 SOAP 訊息
  5. 加法服務
  6. 開發回傳字串陣列、物件、以及物件陣列的服務
  7. 呼叫遠端 .NET 的服務
  8. AXIS 和 ASP.NET 的互通性
  9. 處理 SOAP Header








這份文件是幾年前編寫的,由於當初是為了提供 Web Services 的專案所編寫, 所以直覺上就使用了"SOAP 入門";可是它既不是直接解說 SOAP,也沒談 Web Services 的架構,而是以 AXIS 這個平台來說明如何架設、開發 Web Services 環境,所以今天把它正名為"AXIS 入門"。

安裝 Tomcat 和 Axis

當初是為了專題學生學習上比較容易上手,開始編輯這份教材,經過幾次的 增修,目前已經越來越完整了。由於這次的平台是 Windows,所以內容就以 Windows 的環境作介紹。參考的文件是
我們使用 Tomcat 和 Axis 來作為 web services 的平台。Axis 是一個由 Apache 組織所開發的一套 SOAP 伺服器端與客戶端的平台,他必須與另一個 web server 共存,而我們採用的是 Tomcat。雖然 Apache 已經推出 Axis2,但是經過一段時間 的使用之後,目前個人認為還是 Axis 比較容易上手,而且與 .NET 的互通性來說, 目前 Axis 也是優於 Axis2;各位也可以利用 Axis2 來開發本文介紹的範例來比較, 尤其在 .NET 的範例。 安裝與開發 SOAP service 和 client 的基本步驟如下:
  1. 安裝 Tomcat 7.0.x(我們以 7.0.x 版為例;並假設 Tomcat 安裝於 d:\tomcat)。

  2. 安裝 Axis(我們以 1.4 版為例)
    1. 下載 axis-bin-1_4.zip
    2. 假設我們將 axis 解壓縮於 d:\,因此 axis 的安裝路徑為 d:\axis-1_4
    3. 將 d:\axis-1_4\webapps\axis 路徑整個複製到 d:\tomcat\webapps\ 內, 也就是說複製後,你的 d:\tomcat\webapps 內將包含一個 axis 的子目錄, 而其內容就和 d:\axis-1_4\webapps\axis 一樣。

  3. 啟動(或者重新啟動)tomcat。

  4. 利用 browser 打開 http://127.0.0.1:8080/axis/ 測試 axis 是否安裝成功。 如果成功,你將會看到如下圖的 Apache-Axis 網頁。
     
  5. 在這個網頁,請務必執行 Validation 來進一步確認安裝是否成功。 如果 Validation 執行之後,所有 Needed Components 都存在,這就表示 你的安裝成功了(如下圖所示)。

     
  6. 至於 Optional Components,則是可有可無的元件,例如在我個人的環境,我缺少了 activation.jar、mail.jar、以及 xmlsec.jar。如果你想把這些元件也安裝起來的話, 請依照網頁提供的聯結,下載你所需要的 jar 檔,然後把這些 jar 檔複製到 d:\tomcat\common\lib 之後,重新啟動 tomcat。這樣子一來,你應該就有一個完整 的平台了!






Last Updated: