C/C++・PHP・PerlからAxis2/Javaの配列を入出力にしたSOAPサービスを呼び出す例

Axis2/Javaのサーバ側

以降は、以下のようなサービスechoStringArrayをAxis2/Javaで作成した場合の話になります。
以下がサーバ側の処理で、これを呼び出すクライアント側の処理をC/C++PHPPerlで作成する。C/C++は今回はVC++で。

public class ArrayTest {
	public String[] echoStringArray(String[] param) {
		return param;
	}
}

Axis2/Cでスタブを生成して、C/C++で、Axis2/JavaSOAPサービスを呼び出す。

Axis2/Cのインストール
Axis2/Java 1.4.1にWSDL2Cがない件

Axis2/Cのドキュメントを読むとWSDL2CがAxis2/Javaに入っているように読めるんだけど、ない。

以下に書いてあるように、wsdl2java.batからコピーしてwsdl2c.batを作成し、実行するクラス名を変更すればできる。(しかし、後でちゃんと見たら、wsdl2c.batはAxis2/Cには入っていた。)

Axis2/Cでのスタブ生成

wsdl2c.batでスタブが生成される。色々と関数が作成されるが、最初は以下の二つの関数を呼び出すところから始めるようだ。

  • axutil_env_create_all()
  • axis2_stub_create_XXXXXXXX()←XXXXXXXXは自分で命名したサービス名(今回ならArrayTest)
DLL Load Error 126

コンパイルして、実行すると、以下のようなエラーがAxis2/Cのログに出て、axis2_stub_create_XXXXXXXX()がNULLを返す。(Axis2/Cのログの出力先はaxutil_env_create_all()で指定する。)

[error] ..\..\util\src\class_loader.c(167) Loading shared library C:\axis2c-bin-1.6.0-win32/lib/axis2_http_sender.dll Failed. DLERROR IS DLL Load Error 126: 指定されたモジュールが見つかりません。

「DLL Load Error 126」で検索すると必要なDLLがないらしい。Dependency Walker(depends.exe)というので見ると、LIBEAY32.DLL、SSLEAY32.DLLがないと出てくる。OpenSSLのDLLだそうだ。
コンパイル済みのOpenSSLがあるそうなので、それを入れる。
Microsoft Visual C++ 2008 再頒布可能パッケージ」を入れてから、Windows Updateもして、「Win32 OpenSSL v1.0.0c Light」を入れる。(しかし日本語の再頒布可能パッケージを入れたせいなのか、入れた後もOpenSSLのインストーラに文句を言われた。)

クライアント側のソース

以下のようなソースでSOAP呼び出しができた。
生成されたスタブにメモリの解放を行うように見える名前の関数もあるのだが、全然使っていないので、問題あるかもしれない。

// Axis2CStub.cpp : コンソール アプリケーションのエントリ ポイントを定義します。
//

#include "stdafx.h"
#include "axis2_stub_ArrayTest.h"


int _tmain(int argc, _TCHAR* argv[])
{
	axutil_env_t* env = NULL;
	axis2_char_t* client_home = AXIS2_GETENV("AXIS2C_HOME");
	axis2_char_t* endpoint_uri = "endpoint url";
	axis2_stub_t* stub = NULL;

	env = axutil_env_create_all("axis2test.log", AXIS2_LOG_LEVEL_TRACE);

	stub = axis2_stub_create_ArrayTest(env, client_home, endpoint_uri);
	if (stub == NULL) {
		return EXIT_FAILURE;
	}

	adb_echoStringArray_t* echoStringArray = adb_echoStringArray_create(env);
	if (echoStringArray == NULL) {
		return EXIT_FAILURE;
	}

	axis2_char_t* param[] = {
			"aaa",
			"bbb",
			"ccc",
			"ddd",
	};
	// パラメーターを一つずつ設定する。
	for (int i = 0; i < (sizeof(param)/sizeof(param[0])); i++) {
		axis2_status_t status = adb_echoStringArray_add_param(echoStringArray, env, param[i]);
		if (status != AXIS2_SUCCESS) {
			return EXIT_FAILURE;
		}
	}

	adb_echoStringArrayResponse_t* response = NULL;
	response = axis2_stub_op_ArrayTest_echoStringArray(stub, env, echoStringArray);
	if (response == NULL) {
		return EXIT_FAILURE;
	}

	axutil_array_list_t* array_list = NULL;
	array_list = adb_echoStringArrayResponse_get_return(response, env);
	if (array_list == NULL) {
		return EXIT_FAILURE;
	}

	// 結果データ(文字列の配列)を一つずつ取得する。
	int list_size = axutil_array_list_size(array_list, env);
	for (int i = 0; i < list_size; i++) {
		void* vp = axutil_array_list_get(array_list, env, i);
		char* cp = (char*)vp;
		printf("%s\n", cp);
	}

	return EXIT_SUCCESS;
}

PHPの場合

PHPが一番簡単だった。事前にスタブを作成する必要はない。また、普通に配列を書いて呼び出せる。SOAPを利用可能にするには以下が必要。

<?php
	try {
		$client = new SoapClient(
			'http://localhost:8080/axis2/services/XXXXXXXX?wsdl',
			array('soap_version' => SOAP_1_2));
		
		$param = array(
			"aaaa",
			"bbbb",
			"cccc",
		);
		
		$result = $client->echoStringArray(array(
			'param' => $param
		));
		var_dump($result);
	}
	catch (SoapFault $f) {
		print("SoapFault!");
		var_dump($f);
	}
?>

Perlの場合

PerlPHPと同じようにできるだろうと思っていたが、ちょっと違った。

  • SOAP::Liteを入れる必要がある。ppmで入れようとすると依存しているパッケージがいろいろあるみたいなので、全部入れる。
  • SOAP::LiteのWSDLのサポートが完全ではないのか、SOAPの入出力のデータ型が配列になっていると処理できないようだった。PHPと同じように以下のようなコードで動くことを期待したが、どうもうまく動かない。文字列の配列ではなく、ただの文字列が渡ってしまう。

以下のようなコードで動くことを期待したが、駄目だった。

use SOAP::Lite +trace => [qw(debug)];

my $service = SOAP::Lite
	->service('http://localhost:8080/axis2/services/XXXXXXXX?wsdl');

@param = (
	'aaaa',
	'bbbb',
	'cccc',
);

@result = $service->echoStringArray(@param);
print "@result" ."\n";

以下のようにWSDLを使用しないなら何とかなった。以下なら動く。

use SOAP::Lite +trace => [qw(debug)];

my $soap = new SOAP::Lite
	-> uri('name space uri')
	-> proxy('endpoint url')
;

@param = (
	SOAP::Data->name('param')->type(string => 'aaaa'),
	SOAP::Data->name('param')->type(string => 'bbbb'),
	SOAP::Data->name('param')->type(string => 'cccc'),
);

my $response = $soap->call('echoStringArray' => @param);
my $result = $response->result;
my @paramsout = $response->paramsout;
print "$result @paramsout" ."\n";

Perlはあまり使ったことがないので間違っているかも。