作成日: 2003/09/25

.NET で作成した Web サービスに Java クライアントからアクセスする

概要

.NET を使うと Web サービスも Web サービスクライアントも驚くほど簡単に作成できます。 しかし時には、サーバは .NET だけど、クライアントは Java で作成しなければならないことがあります。 Java でも Web Service 用のクラスライブラリ/ツールが出回っており、これまた驚くほど簡単に実装できます。 ためしにオープンソースの SOAP サーバ/クライアント 用コンポーネントである Apache Axis を利用して .NET で作成した Web サービスにアクセスしてみました。 なお、以下の説明では Windows XP Professional 上で、 .NET C# 2002 & IIS 5.1(Web サービス用)、IE 6、JDK 1.4.2、Apache Axis 1.1 を使用しています。

Apache Axis

Apache Axis は http://ws.apache.org/axis/ で公開されています(2003年9月25日現在)。 とりあえず、そこからバイナリ (axis-V_R.zip) をダウンロードします(V_R はバージョン番号になります)。 2003年9月25日時点での最新バージョンは 1.1 でしたので、私は axis-1_1.zip をダウンロードしました。 なお、サイズが 10MB ほどありますので、ダイアルアップ環境だとちょっとつらいかもしれません。 現に私の環境も 56K のダイアルアップモデムですが、約 1 時間ほどかかりました。

ダウンロードが済んだら、これを適当なディレクトリに展開し、lib 以下に存在するクラスライブラリを クラスパスに設定します。私は、C:\Bin\axis-1_1 に展開しました。 どの jar が最低限必要なものか分からなかったので、とりあえず、すべての jar ファイルをクラスパスに設定しています。 ちなみに、私の環境ではクラスパスは以下のようになりました。
※途中で折り返して表示しています。

CLASSPATH=.;C:\Bin\axis-1_1\lib\axis-ant.jar;C:\Bin\axis-1_1\lib\axis.jar;C:\Bin\axis-1_1\lib\common
s-discovery.jar;C:\Bin\axis-1_1\lib\commons-logging.jar;C:\Bin\axis-1_1\lib\jaxrpc.jar;C:\Bin\axis-1
_1\lib\log4j-1.2.8.jar;C:\Bin\axis-1_1\lib\saaj.jar;C:\Bin\axis-1_1\lib\wsdl4j.jar

また、Axis は別途 XML パーサを必要とします。 JDK 1.4 には XML パーサが付属していますので、何もする必要はありませんが、それ以外の環境では、 別途用意する必要があります。Axis のドキュメントでは Xerces を推奨しています。 が、JAXP 1.1 準拠の XML パーサであれば何でも構わないので、お好きなパーサを選んでください。 私は JDK 1.4.2 に付属のパーサを使用しましたので、別途にパーサのインストールは行っていません。

Web サービス

さっそく .NET で Web サービスを作成します。
なお、IIS のインストールが済んでおり、かつ、.NET から利用できるようになっている必要があります。 .NET のインストール時に、既に IIS がインストールされていた場合はセットアップが済んでいるので、そのまま利用できますが、 .NET のインストール後に IIS のインストールを行った場合、.NET Framework のインストールを修復する必要があります。 修復方法については、MSDN の 検索文字列 [IIS, インストール] で表示される [Visual Studio .NET のソフトウェア要件] - [ソフトウェア要件における問題の解決] に書かれていますので、参照し、実行してください。

では、Web サービスをちゃちゃっと作成します。
.NET を起動し、メニュー - [ファイル] - [新規作成] - [プロジェクト] を選択します。 表示される [新しいプロジェクト] ダイアログで、[プロジェクトの種類] で [Visual C# プロジェクト] を選択し、 [テンプレート] に [ASP.NET Web サービス] を選択します。 [場所] には作成したい Web サービスプロジェクトの場所を指定します。 ここでは、[http://localhost/WebSample] としました。入力が完了したら [OK] をクリックします。
※IIS のサービスを手動にして止めている場合、事前にサービスを起動しておいてください。 私は普段、このサービスを止めているため、よくそのまま Web サービスを作成しようとして、失敗してから気付いています。

新しいプロジェクト

プロジェクトの作成中は、以下のような小さなダイアログが表示されます。Web のパブリックフォルダに、 ディレクトリが作成され、必要なファイルが作成された後、プロジェクトが開かれます。

新しい Web の作成 プロジェクトウィンドウ

上の図中で表示されている [Service1.asmx] というのがサービスのコードを実装するファイルになります。 Service1 では格好悪いので、好きな名前に変えてください。ただし、ここでは名前を変えずにそのままいきます。

[ここをクリックするとコード ビューに切り替わります] の箇所をクリックして、 コードビューを表示します。以下のようになっていると思います。

コードビュー

コメント化されている [WebMethod] のところを変更して Web サービスメソッドを実装します。
ここでは、HelloWorld() を変更して、引数文字列を 1 つとり "Hello, " + 引数文字列 の文字列を返す、 HelloDear() メソッドに変更します。
また、デフォルトのままだと、このクラスで実装するWeb サービスの名前空間は、 [http://tempuri.org/] のままなので、これを変更し、[http://localhost/WebSample/] とするために、WebService 属性の Namespace プロパティを設定します。
なお、コードを変更せずに、[Web サービスの例] のコメント通りに [WebMethod] HelloWorld() を有効にして、 そのまま実行すると、サービスの説明とともに名前空間の説明が簡単に表示されます。
変更後のコードは例えば以下のようになります。

サービスメソッドの追加

Web サービスの名前空間を指定しています。

[WebService(Namespace="http://localhost/WebSample/")]

[WebMethod] HelloWorld() は、以下のように変更しました。

[WebMethod]
public string HelloDear(string mydear)
{
    return "Hello " + mydear;
}

以上で、簡単ですが Web サービスが完成しました。

Web サービスの動作確認

作成した Web サービスの動作確認を行います。 確認には .NET で Web サービスクライアントを作成し、実際に Web サービスを利用してみます。

.NET を起動し、メニュー - [ファイル] - [新規作成] - [プロジェクト] を選択します。 表示される [新しいプロジェクト] ダイアログで、[プロジェクトの種類] で [Visual C# プロジェクト] を選択し、 [テンプレート] に [コンソールアプリケーション] を選択します。 後はデフォルトのまま [OK] をクリックします。

新しいプロジェクト

次に、プロジェクトから作成した Web サービスを参照できるようにします。
メニュー - [プロジェクト] - [Web 参照の追加] を選択します。表示される [Web 参照の追加] ダイアログで、 [アドレス] に [http://localhost/WebSample/Service1.asmx] と入力し、[Enter] を押下します。

Web参照の追加

先に作成した Web サービスの情報が表示されるので、[参照の追加] をクリックして、Web 参照を追加します。
.NET は Web 参照を追加すると同時に、Web サービスを利用するための C# のスタブクラスも作成してくれます。 Web サービスの呼び出しは、このスタブクラスを使います。例えば、プログラムの Main を以下のように変更します。

コードビュー

上図の例では、Web サービスを呼び出し、結果を表示するために、以下の 3 行を追加しています。

localhost.Service1 s1 = new localhost.Service1();
string result = s1.HelloDear("myname!");
Console.WriteLine(result);

ビルドして、実行してみてください。

Hello myname!

のようにコンソールに表示されれば、成功です。

Web サービス呼び出しのためのコードをみると、 Web サービスで作成した Service1 クラスを直接 new し、メソッド HelloDear() を呼び出すコードと 何ら変わりません。 クライアントと Web サービス間で SOAP による通信がなされているのを忘れてしまうくらいに簡単です。
けれど、実際には HelloDear() 呼び出し時には、以下のような HTTP 要求が Web サービスに対してなされています。

POST /WebSample/Service1.asmx HTTP/1.1
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; MS Web Services Client Protocol 1.0.3705.288)
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://localhost/WebSample/HelloDear"
Content-Length: 329
Expect: 100-continue
Connection: Keep-Alive
Host: localhost

<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/env
elope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLS
chema"><soap:Body><HelloDear xmlns="http://localhost/WebSample/"><mydear>myname!</mydear></HelloDear
></soap:Body></soap:Envelope>

この要求に対し、以下のような応答が Web サービス側から返ってきます。

HTTP/1.1 200 OK
Server: Microsoft-IIS/5.1
Date: Thu, 25 Sep 2003 01:40:50 GMT
X-Powered-By: ASP.NET
X-AspNet-Version: 1.1.4322
Cache-Control: private, max-age=0
Content-Type: text/xml; charset=utf-8
Content-Length: 369

<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/env
elope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLS
chema"><soap:Body><HelloDearResponse xmlns="http://localhost/WebSample/"><HelloDearResult>Hello myna
me!</HelloDearResult></HelloDearResponse></soap:Body></soap:Envelope>

※両方とも途中で折り返して表示しています。

.NET を使って Web サービス/クライアントを作成すると、SOAP によるこれらの通信部分を プログラマが意識する必要がなくなっています。本当に便利です。

WSDL

.NET で作成する Web サービスクライアントは驚くほど簡単でした。では、これを Java で実装するとしたらどうでしょう。 先の SOAP 通信部分を自前でパースして作成することも可能かもしれませんが、正直なところ、あまりやりたくありません。 それに開発中は、Web サービスのインタフェースが変わることがありますので、作り方によっては、 変更のたびにパース部分やメソッドの型情報などをあわせて修正していく必要があるかもしれません。

ここで登場するのが WSDL です。WSDL によって定義された Web サービスのインタフェース情報を使えば、 Web サービスにアクセスするために必要な情報が手に入れられます。また、というかこれが最大の利点なのですが、 WSDL を適当なツールに読み込ますことで、 .NET が作成してくれたような通信の詳細を隠したスタブクラスを作成することも可能になります。

以下で使用する Apache Axis にも WSDL から Java のスタブクラスを生成してくれるツールが付属しています。
では、さきほど作成した Web サービスの WSDL を取得します。 Web ブラウザを起動し、アドレス欄に [http://localhost/WebSample/Service1.asmx?wsdl] と入力し、 [Enter] を押下します。

WSDLを表示する

何やら、XML で記述された文書が表示されました。これが WSDL です。これを適当なファイルとして保存します。 メニュー - [ファイル] - [名前を付けて保存] を選択し、 表示される [名前を付けて保存] ダイアログから適当な名前を付けて保存します。 ここでは、Service1.xml として保存しました。

名前をつけて保存

※WSDL については、ネット上に詳細な情報がありますので参照してみてください。
ちなみに @IT (http://www.atmarkit.co.jp/) の XML & Web Services フォーラムにはたくさんの解説記事があり、 基礎を勉強するには大変有用でした。

スタブクラスの生成

取得した WSDL を使って Java のスタブクラスを生成します。 コマンドプロンプトを開き、さきほどの WSDL (Service1.xml) を保存したディレクトリへ移動し、 以下のコマンドを実行します。

java org.apache.axis.wsdl.WSDL2Java -a Service1.xml

実際に実行してみた例です。作成後には、[localhost] というディレクトリが作成されているのがわかります。
※途中で折り返して表示しています。

C:\Documents and Settings\Bono\My Documents\Temporary\WSDL>dir
 ドライブ C のボリューム ラベルがありません。
 ボリューム シリアル番号は 10A2-7BD7 です

 C:\Documents and Settings\Bono\My Documents\Temporary\WSDL のディレクトリ

2003/09/25  11:49    <DIR>          .
2003/09/25  11:49    <DIR>          ..
2003/09/25  11:38             2,182 Service1.xml
               1 個のファイル               2,182 バイト
               2 個のディレクトリ  142,329,511,936 バイトの空き領域

C:\Documents and Settings\Bono\My Documents\Temporary\WSDL>java org.apache.axis.wsdl.WSDL2Java -a Se
rvice1.xml

C:\Documents and Settings\Bono\My Documents\Temporary\WSDL>dir
 ドライブ C のボリューム ラベルがありません。
 ボリューム シリアル番号は 10A2-7BD7 です

 C:\Documents and Settings\Bono\My Documents\Temporary\WSDL のディレクトリ

2003/09/25  11:50    <DIR>          .
2003/09/25  11:50    <DIR>          ..
2003/09/25  11:50    <DIR>          localhost
2003/09/25  11:38             2,182 Service1.xml
               1 個のファイル               2,182 バイト
               3 個のディレクトリ  142,329,495,552 バイトの空き領域

C:\Documents and Settings\Bono\My Documents\Temporary\WSDL>

[localhost] は以下のように localhost/WebSample/ となっており、 その下にスタブクラスの Java ソースが生成されています。

生成されたファイル

※わざわざ、XML をファイルとして保存しなくても、直接 WSDL の URL を指定することでも、 同じようにスタブクラスを作成することができます。

java org.apache.axis.wsdl.WSDL2Java -a http://localhost/WebSample/Service1.asmx?wsdl
Web サービスクライアントの作成

では、適当なエディタを使用して、以下の Web サービスクライアント (というにはあまりにも簡素ですが・・) のコードを入力してください。

import localhost.WebSample.*;

public class TestClient {
   public static void main(String [] args) {
       try {
            Service1Locator locator = new Service1Locator();
            Service1Soap service = locator.getService1Soap();
            String result = service.helloDear("myname!");
            System.out.println(result);
       } catch (Exception e) {
           System.err.println(e.toString());
       }
   }
}

入力したら、さきほどのスタブクラスを作成していたディレクトリへ [TestClient.java] として保存し、コマンドプロンプトを起動して、保存したディレクトリへ移動し、コンパイル/実行します。

C:\Documents and Settings\Bono\My Documents\Temporary\WSDL>dir
 ドライブ C のボリューム ラベルがありません。
 ボリューム シリアル番号は 10A2-7BD7 です

 C:\Documents and Settings\Bono\My Documents\Temporary\WSDL のディレクトリ

2003/09/25  12:22    <DIR>          .
2003/09/25  12:22    <DIR>          ..
2003/09/25  11:50    <DIR>          localhost
2003/09/25  11:38             2,182 Service1.xml
2003/09/25  12:20               391 TestClient.java
               2 個のファイル               2,573 バイト
               3 個のディレクトリ  142,327,709,696 バイトの空き領域

C:\Documents and Settings\Bono\My Documents\Temporary\WSDL>javac TestClient.java

C:\Documents and Settings\Bono\My Documents\Temporary\WSDL>java TestClient
Hello myname!

C:\Documents and Settings\Bono\My Documents\Temporary\WSDL>

"Hello myname!" のように表示されれば、正しく動作しています。
ちなみにこのとき、以下のような HTTP 要求が Web サービスに対して行われています。

POST /WebSample/Service1.asmx HTTP/1.0
Content-Type: text/xml; charset=utf-8
Accept: application/soap+xml, application/dime, multipart/related, text/*
User-Agent: Axis/1.1
Host: localhost
Cache-Control: no-cache
Pragma: no-cache
SOAPAction: "http://localhost/WebSample/HelloDear"
Content-Length: 360

<?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>
  <HelloDear xmlns="http://localhost/WebSample/">
   <mydear>myname!</mydear>
  </HelloDear>
 </soapenv:Body>
</soapenv:Envelope>

.NET クライアントのときと、名前空間の命名の仕方が少し違いますが、意味的には同じになっているのが分かります。 そして、この要求に対し、以下のような応答が Web サービス側から返ってきます。 こちらは、クライアントが .NET でも Java でも変わりありません。

HTTP/1.1 200 OK
Server: Microsoft-IIS/5.1
Date: Thu, 25 Sep 2003 03:38:02 GMT
X-Powered-By: ASP.NET
X-AspNet-Version: 1.1.4322
Cache-Control: private, max-age=0
Content-Type: text/xml; charset=utf-8
Content-Length: 369

<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/env
elope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLS
chema"><soap:Body><HelloDearResponse xmlns="http://localhost/WebSample/"><HelloDearResult>Hello myna
me!</HelloDearResult></HelloDearResponse></soap:Body></soap:Envelope>

※両方とも途中で折り返して表示しています。

めんどくさそうな Java での Web サービスの処理もこれらのライブラリを使うことで .NET と同じくらいに簡単に実装できます。Apache Axis ではクライアントだけではなく、Web サービスも作成できます (というか、そっちがメインのようで・・)。
ほんとに便利なツール/ライブラリで、これがオープンソースとして公開されているとは・・。素晴らしいのひとことに尽きます。

おまけ

SOAP ヘッダを利用して認証情報を渡すように Web サービスを変更します。また、Java のクライアントもこれに対応させます。
まず、Web サービス側を変更します。コードビューで、C# のソースを以下のように変更します。

認証情報を追加

SoapHeader クラスを参照するために追加しています。

using System.Web.Services.Protocols;

認証情報を渡すための構造を定義しています。

    public class MyAuthHeader : SoapHeader {
        public string userid;
        public string password;
    }

認証情報を格納する変数です。

        public MyAuthHeader myheader;

.NET により SOAP ヘッダの内容が変数 myheader に格納されるように指示します。

        [SoapHeader("myheader", Required=true)]

認証情報をチェックするコードです。

            if (myheader != null
             && myheader.userid == "m(__)m"
             && myheader.password == "v(^^)v") {
                return "Hello " + mydear;
            } else {
                throw new HttpException(401, "認証できません。");
            }

変更が済んだら、コードをビルドします。
次に Java のスタブクラスを再作成します。念のため既存の localhost ディレクトリは削除してから行います。 以下では、XML をファイルに保存せず、直接 URL 指定を行いスタブクラスを生成しました。
※途中で折り返して表示しています。

C:\Documents and Settings\Bono\My Documents\Temporary\WSDL>dir
 ドライブ C のボリューム ラベルがありません。
 ボリューム シリアル番号は 10A2-7BD7 です

 C:\Documents and Settings\Bono\My Documents\Temporary\WSDL のディレクトリ

2003/09/25  13:51    <DIR>          .
2003/09/25  13:51    <DIR>          ..
2003/09/25  11:38             2,182 Service1.xml
2003/09/25  13:39               788 TestClient.java
               2 個のファイル               2,970 バイト
               2 個のディレクトリ  142,273,626,112 バイトの空き領域

C:\Documents and Settings\Bono\My Documents\Temporary\WSDL>java org.apache.axis.wsdl.WSDL2Java -a ht
tp://localhost/WebSample/Service1.asmx?wsdl

C:\Documents and Settings\Bono\My Documents\Temporary\WSDL>dir
 ドライブ C のボリューム ラベルがありません。
 ボリューム シリアル番号は 10A2-7BD7 です

 C:\Documents and Settings\Bono\My Documents\Temporary\WSDL のディレクトリ

2003/09/25  13:51    <DIR>          .
2003/09/25  13:51    <DIR>          ..
2003/09/25  11:50    <DIR>          localhost
2003/09/25  11:38             2,182 Service1.xml
2003/09/25  13:39               788 TestClient.java
               2 個のファイル               2,970 バイト
               3 個のディレクトリ  142,273,605,632 バイトの空き領域

C:\Documents and Settings\Bono\My Documents\Temporary\WSDL>

作成されたファイルを見ると、認証情報格納のためのクラスもきちんと作成されています。

生成されたファイル

次に Java の Web サービスクライアントを変更します。 MyAuthHeader クラスを使用して認証情報を設定した後、SOAP ヘッダとして追加しています。

import localhost.WebSample.*;
import org.apache.axis.message.*;

public class TestClient {
   public static void main(String [] args) {
       try {

            Service1Locator locator = new Service1Locator();
            Service1Soap service = locator.getService1Soap();

            // 認証情報を格納した SOAPヘッダを付加する。
            MyAuthHeader header = new MyAuthHeader();
            header.setUserid("m(__)m");
            header.setPassword("v(^^)v");
            Service1SoapStub svcstub = (Service1SoapStub) service;
            SOAPHeaderElement helm = new SOAPHeaderElement("http://localhost/WebSample/", "MyAuthHeader", header);
            svcstub.setHeader(helm);

            String result = service.helloDear("myname!");
            System.out.println(result);
       } catch (Exception e) {
           System.err.println(e.toString());
       }
   }
}

これを、先ほどスタブクラスを生成したディレクトリに保存し、コンパイル/実行します。

C:\Documents and Settings\Bono\My Documents\Temporary\WSDL>dir
 ドライブ C のボリューム ラベルがありません。
 ボリューム シリアル番号は 10A2-7BD7 です

 C:\Documents and Settings\Bono\My Documents\Temporary\WSDL のディレクトリ

2003/09/25  13:51    <DIR>          .
2003/09/25  13:51    <DIR>          ..
2003/09/25  11:50    <DIR>          localhost
2003/09/25  11:38             2,182 Service1.xml
2003/09/25  13:39               788 TestClient.java
               2 個のファイル               2,970 バイト
               3 個のディレクトリ  142,272,671,744 バイトの空き領域

C:\Documents and Settings\Bono\My Documents\Temporary\WSDL>javac TestClient.java

C:\Documents and Settings\Bono\My Documents\Temporary\WSDL>java TestClient
Hello myname!

C:\Documents and Settings\Bono\My Documents\Temporary\WSDL>

"Hello myname!" のように表示されれば、正しく動作しています。
.NET 側のコードにブレークポイントを設定し、Debug モードで実行してから、 Java クライアントを実行すれば、要求が行われ、ヘッダ部分に格納した userid、password に値が設定されているのを確認することもできます。
また、このとき、クライアントから Web サービスに対しては以下のような HTTP 要求が行われています。 MyAuthHeader とその中身 (userid、password) が SOAP ヘッダに設定されているのが確認できます (サーバ側の応答は前と同じなので省略しました)。
※途中で折り返して表示しています。

POST /WebSample/Service1.asmx HTTP/1.0
Content-Type: text/xml; charset=utf-8
Accept: application/soap+xml, application/dime, multipart/related, text/*
User-Agent: Axis/1.1
Host: localhost
Cache-Control: no-cache
Pragma: no-cache
SOAPAction: "http://localhost/WebSample/HelloDear"
Content-Length: 609

<?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:Header>
  <ns1:MyAuthHeader soapenv:mustUnderstand="0" xsi:type="ns1:MyAuthHeader" xmlns:ns1="http://localho
st/WebSample/">
   <ns1:userid>m(__)m</ns1:userid>
   <ns1:password>v(^^)v</ns1:password>
  </ns1:MyAuthHeader>
 </soapenv:Header>
 <soapenv:Body>
  <HelloDear xmlns="http://localhost/WebSample/">
   <mydear>myname!</mydear>
  </HelloDear>
 </soapenv:Body>
</soapenv:Envelope>
サンプルソース
WebSample.zip (18.5 KB)
.NET で作成した Web サービスです。おまけを追加した実装になっています。
ConsoleApplication1.zip (21.8 KB)
.NET で作成した Web サービスクライアントです。
WSDL.zip (5.98 KB)
作成した Java の Web サービスクライアントです。これもおまけを追加した実装になっています。
参考文献

プログラミング .NET - ASP.NET によるサーバーサイド開発
Jeff Prosise 著 豊田 孝 監訳
日経BPソフトプレス ISBN 4-89100-315-4