ファイヤープロジェクト
SOAP MessagingにおけるSOAP Bodyの操作(AXIS 1.3)
2005-10-22T17:40+09:00   matsu
SOAP Messagingのメッセージ送受信の方法が分かったので,今度は送受信するメッセージの解析や作成方法を調査してみた.
今回は,クライアント側から現在時刻のミリ秒と日付フォーマットをサーバに送信し,サーバ側ではそのミリ秒を指定されたフォーマットに変換してから返すというサンプルを作成した. deploy.wsddは以下.
要求電文は以下.
これに対する回答電文は以下.
今回のポイントは以下である.
  • クライアント側での要求電文の動的な作成
  • サーバ側での要求電文の解析と,それに応じた回答電文の動的な作成
  • サーバ側での動的な処理にともなうエラーハンドリング
  • クライアント側での回答電文の解析
以下にサンプルのクライアントのコードを示す.
メソッドcreateReqSOAPBodyElementにおいて,要求電文のSOAPBodyを作成している.
    private static SOAPBodyElement createReqSOAPBodyElement() throws SOAPException { 
	SOAPBodyElement resultElement = new SOAPBodyElement();
	resultElement.setQName(new QName("", "requestMessage"));
	SOAPElement parameterElement = resultElement.addChildElement("parameter");
	SOAPElement valueElement = parameterElement.addChildElement("value");
	valueElement.addTextNode(Long.toString(Calendar.getInstance().getTimeInMillis()));
	SOAPElement formatElement = parameterElement.addChildElement("format");
	formatElement.addTextNode("yyyy-MM-dd");
	return resultElement;
    }
要求電文はXML文書であることもあり,その作成は上記のようにDOMプログラミングの拡張のようになっている. SOAPBodyを作成すれば,あとのSOAPEnvelopeはライブラリ側でやってくれるので,サービスの呼び出しはinvokeするだけでよい.
	    SOAPBodyElement[] input = new SOAPBodyElement[1];
	    input[0] = createReqSOAPBodyElement();
            Object result = call.invoke(input);
以下にサンプルのサービスDateFormatServiceを示す.
メソッドcreateDateFormatParamsにおいて,要求電文の解析を行っている. パラメータのinputは,呼び出し元メソッドformatDateでAXISからわたされるオブジェクトである. サーバ側のログで
	StringWriter writer = new StringWriter();
	XMLUtils.ElementToWriter(rootElement, writer);
	System.out.println("rootElement = " + writer.toString());
を確認したところ,
rootElement = <requestMessage><parameter><value>1129966273627</value><format>yyyy-MM-dd</format></parameter></requestMessage>
となっていた. すなわちdeployment.wsddにて登録したメソッドformatDateの引数には,SOAPBodyがDocumentオブジェクトとして渡される. ここまで分かれば,あとは要求電文の作成と同様である. ただし,拡張ではなくDOMプログラミングそのものになっている点に注意. 回答電文の作成はメソッドcreateResDocumentにおいて行っている. こちらはDOMプログラミングそのままとなっている. このようにサーバ側処理は完全にDOMプログラミングとなっており,AXIS特有の要素クラスをほとんどインポートせずに実装できている点に注意.
SOAPでは,サーバ側で処理異常があった場合,SOAPFaultを返すことになっている. さらにHTTPバインディングの場合は,HTTPリターンコードは500とすることになっている. AXISを使用する場合,サービスメソッドがAxisFaultを投げれば,上記のような処理を行ってくれる. 今回のサービスは,SimpleDateFormatがサポートしていないものを日付フォーマットに指定すると,サーバ側で処理が継続できない. したがって,以下の要求はフォーマットに"XX"などとあり不正である.
これに対する回答は以下である.
まず,HTTPリターンコードが500となっている. さらにSOAPBodyにはsoapenv:Faultという要素ある. サービス処理を行うメソッドからAxisFautlを投げると,Axisがこの要素以下を作成して回答する. 上の例ではFaultの子には三つの子要素がある.
faultcode
必須要素. コードはSOAPで定められた接頭辞をもつ.
VersionMismatch
SOAPEnvelopeの名前空間が不正
MustUnderStand
サーバが必須ヘッダを理解できない. ヘッダ処理については別の頁にて調査する.
Client
要求電文不正.
Server
サーバ側での処理失敗.
faultstring
必須要素. 失敗の概要を記述する.
detail
詳細情報.
これらはAxisFaultオブジェクトに対して設定してthrowすれば,Axisが回答電文に反映してくれる. また,先の例の場合,回答電文のfaultcodeの接頭辞にはClientを設定すべきのように思える.
回答電文はinvokeメソッドの返り値で取得できる.
            Object result = call.invoke(input);

	    System.out.println("className of result = " + result.getClass().getName());
	    System.out.println("className of result[0] = " + ((Vector)result).get(0).getClass().getName());
	    System.out.println("result = " + result);
上記部分の出力は,
className of result = java.util.Vector
result.size = 1
className of result[0] = org.apache.axis.message.RPCElement
result = [<responceMessage><value>2005-10-22</value></responceMessage>]
となっている. RPCElementはSOAPBodyElementを実装しているので,ここからSOAPBodyを取り出して処理することができる. この処理はやはりDOMプログラミングの拡張となるので,割愛した. SOAP MessagingであるのにRPCElementが返ることに違和感がある場合は,MessageContextから回答SOAPEnvelopeを取得することができる.
	    MessageContext context = call.getMessageContext();
	    Message res = context.getResponseMessage();
	    SOAPEnvelope resEnvelope = res.getSOAPEnvelope();
	    SOAPBodyElement resBody = resEnvelope.getFirstBody();

	    StringWriter writer = new StringWriter();
	    XMLUtils.ElementToWriter(resBody, writer);
	    System.out.println("elementBody = " + writer.toString());
これの出力は以下.
elementBody = <responceMessage><value>2005-10-22</value></responceMessage>
SOAPEnvelopeからSOAPBodyを取得した後は,先と同様となる.
matsu(C)
Since 2002
Mail to matsu