AXIS 和 .NET web service的互通性

转自:http://web.nchu.edu.tw/~jlu/cyut/axis/axis1net.shtml#string,请看原作

開發 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>

原文地址:https://www.cnblogs.com/oisiv/p/1495217.html