Axis2 で Web サービスを実装しました。メッセージの内容は、AXIOM(Axis2 の DOM?)を使って生の XML で扱う方法を採用しました。そんな中、SOAP レスポンスにヘッダーを追加する必要があり、方法を調べました。
もくじ
今回作成したい Web サービスの内容
お客様サポートの問い合わせをリクエストとして受け取り、それに対する返事をレスポンスとする Web サービスを作成することにします。とはいえ、Web サービスの内容自体は重要ではなく、「SOAP レスポンスにヘッダーを追加し、「お問い合わせ番号」を設定する」ことを目的とします。
SOAP リクエストは次の通りです。
<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
<soapenv:Body>
<Inquire xmlns="urn:userSupport">
<UserName>みかん大好き</UserName>
<Content>△△店では「ガ〇ン、とみかん」マルチパックの取り扱いはありますか?</Content>
</Inquire>
</soapenv:Body>
</soapenv:Envelope>
期待する SOAP レスポンスは次の通りです。ヘッダーに「お問い合わせ番号」が設定されているところがポイントです。
<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
<soapenv:Header>
<InquiryNumber xmlns="urn:userSupoport">INQ-12345</InquiryId>
</soapenv:Header>
<soapenv:Body>
<InquireResponse xmlns="urn:userSupoport">
<Content>お問い合わせを受付けました。後ほど改めましてご返信差し上げます。お問い合わせ番号は「INQ-12345」です。</Content>
</InquireResponse>
</soapenv:Body>
</soapenv:Envelope>
SOAP ヘッダーの追加はどこで行うか
サービス クラスの実装は、次の通りです。サービス メソッドの戻り値は、あくまでレスポンスのボディ部分のみです。
public class UserSupport {
// 問い合わせを受けるサービス メソッド
public OMElement Inquire(OMElement requestElement) {
// ここでリクエストの XML(requestElement パラメーター)から、問い合わせ内容などを取得します(省略)。
// レスポンスの XML を作成します。
OMFactory omFactory = OMAbstractFactory.getOMFactory();
OMNamespace ns = omFactory.createOMNamespace("urn:userSupoport", "");
OMElement responseElement = omFactory.createOMElement("InquireResponse", ns);
OMElement contentElement = omFactory.createOMElement("Content", ns, responseElement);
contentElement.setText("お問い合わせを受付けました。後ほど改めましてご返信差し上げます。お問い合わせ番号は「INQ-12345」です。");
return responseElement;
}
}
サービスを呼び出した際の SOAP レスポンスは次の通りです。当然ですが、この時点ではヘッダーがありません。
<Envelope xmlns="http://www.w3.org/2003/05/soap-envelope">
<Body>
<InquireResponse xmlns="urn:userSupport">
<Content>お問い合わせを受付けました。後ほど改めましてご返信差し上げます。お問い合わせ番号は「INQ-12345」です。</Content>
</InquireResponse>
</Body>
</Envelope>
それでは、ヘッダーを追加したいときは、どうするのか?ということになるのですが、結論としては、サービス クラスだけでは追加できません。別途、『モジュール』というものを作成する必要があります。
『モジュール』『フロー』『フェーズ』『ハンドラー』とは
そもそも『モジュール』とはなんでしょうか。また、モジュールを作成するにあたり『フロー』『フェーズ』『ハンドラー』という概念が出てくるので、先にこれらについてまとめておきます。
- Axis2 では、『モジュール』を作成することにより、独自の機能拡張を行うことができる。
- SOAP レスポンスにヘッダーを追加するには、この『モジュール』を作成することが必要。
- Axis2 の処理は一連の『フロー』で構成されている。また、各フローは一連の『フェーズ』で構成されている。作成したモジュールは、どのフローで実行するかを指定してやる必要がある。
『フロー』とは
『フロー』は、Axis2 エンジンが実行する処理の流れを、4 種類の論理的な区分に分けたものです。4 種類の区分とは、InFlow
、InFaultFlow
、OutFlow
、OutFaultFlow
です。
フロー | 意味 |
---|---|
InFlow | メッセージの入力を処理するフロー |
InFaultFlow | メッセージの入力(Fault 時)を処理するフロー |
OutFlow | メッセージの出力を処理するフロー |
OutFaultFlow | メッセージの出力(Fault 時)を処理するフロー |
『フェーズ』とは
『フェーズ』とは、1 つのフローをさらに論理的に区切ったものです。つまり、1 つのフローの中に、フェーズが複数存在します。また、フローはあらかじめ Axis2 でによって 4 種類に決まっていますが、フェーズは何個でも存在でき、自作のフェーズを新たに定義することもできます。
『ハンドラー』とは
『ハンドラー』とは、フェーズに対してあらかじめ登録しておくものです。Axis2 エンジンによって対象のフェーズが実行される時に、登録されているハンドラーの処理が呼び出されます。
文章だとややこしいので、図にしました。
モジュールを作成する
モジュールを作成するには、いろいろなファイルを作成または編集する必要があり、けっこう大変です。具体的には、次の作業を行うことになります。
- モジュール クラスを実装する
- ハンドラー クラスを実装する
- フェーズを定義する(
axis2.xml
ファイルの編集)※任意 - モジュール定義を作成する(
module.xml
ファイルの作成) - サービスにモジュールを関連付ける(
services.xml
ファイルの編集)
モジュール クラスを実装する
まず、Module
インターフェイスを実装したクラスを作成します。このインターフェイスには 5 つのメソッドがあり、モジュールの初期化時や終了時の処理などを実装しますが、今回は特に何もすることがないので、処理は空にしておきます。
package axis2module.module;
import org.apache.axis2.AxisFault;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.axis2.description.AxisDescription;
import org.apache.axis2.description.AxisModule;
import org.apache.axis2.modules.Module;
import org.apache.neethi.Assertion;
import org.apache.neethi.Policy;
public class UserSupportModule implements Module {
public void init(ConfigurationContext configurationContext, AxisModule axisModule) throws AxisFault {
}
public void engageNotify(AxisDescription axisDescription) throws AxisFault {
}
public boolean canSupportAssertion(Assertion assertion) {
return false;
}
public void applyPolicy(Policy policy, AxisDescription axisDescription) throws AxisFault {
}
public void shutdown(ConfigurationContext configurationContext) throws AxisFault {
}
}
ハンドラー クラスを実装する
次に、AbstractHandler
クラスを継承したクラスを作成します。invoke
メソッドをオーバーライドし、ここに実際に行いたい処理、つまり今回であれば SOAP ヘッダーを追加する処理を実装します。
package axis2module.module;
import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMFactory;
import org.apache.axiom.om.OMNamespace;
import org.apache.axiom.soap.SOAPEnvelope;
import org.apache.axiom.soap.SOAPHeader;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.handlers.AbstractHandler;
public class AddSoapHeaderHandler extends AbstractHandler {
public InvocationResponse invoke(MessageContext ctx) {
// SOAP ヘッダーを追加します。
OMFactory omFactory = OMAbstractFactory.getOMFactory();
SOAPEnvelope envelope = ctx.getEnvelope();
SOAPHeader header = envelope.getHeader();
OMNamespace ns = omFactory.createOMNamespace("urn:userSupoport", "");
OMElement inquiryIdElement = omFactory.createOMElement("InquiryNumber", ns, header);
inquiryIdElement.setText("INQ-12345");
// 他の処理を継続して実行します。
return InvocationResponse.CONTINUE;
}
}
フェーズを定義する
Axis2 の設定ファイル(axis2.xml
)には、以下の通り、はじめから数種類のフェーズが定義されています。
<!-- ================================================= -->
<!-- Phases -->
<!-- ================================================= -->
<phaseOrder type="InFlow">
<!-- System predefined phases -->
<phase name="Transport">
:
</phase>
<phase name="Addressing">
:
</phase>
<phase name="Security"/>
<phase name="PreDispatch"/>
<phase name="Dispatch" class="org.apache.axis2.engine.DispatchPhase">
:
</phase>
<phase name="RMPhase"/>
<!-- System predefined phases -->
<!-- After Postdispatch phase module author or service author can add any phase he want -->
<phase name="OperationInPhase">
:
</phase>
<phase name="soapmonitorPhase"/>
</phaseOrder>
<phaseOrder type="OutFlow">
<!-- user can add his own phases to this area -->
<phase name="soapmonitorPhase"/>
<phase name="OperationOutPhase"/>
<!-- system predefined phase -->
<!-- these phase will run irrespective of the service -->
<phase name="RMPhase"/>
<phase name="PolicyDetermination"/>
<phase name="MessageOut"/>
<phase name="Security"/>
</phaseOrder>
<phaseOrder type="InFaultFlow">
<phase name="Addressing">
:
</phase>
<phase name="Security"/>
<phase name="PreDispatch"/>
<phase name="Dispatch" class="org.apache.axis2.engine.DispatchPhase">
:
</phase>
<phase name="RMPhase"/>
<!-- user can add his own phases to this area -->
<phase name="OperationInFaultPhase"/>
<phase name="soapmonitorPhase"/>
</phaseOrder>
<phaseOrder type="OutFaultFlow">
<!-- user can add his own phases to this area -->
<phase name="soapmonitorPhase"/>
<phase name="OperationOutFaultPhase"/>
<phase name="RMPhase"/>
<phase name="PolicyDetermination"/>
<phase name="MessageOut"/>
<phase name="Security"/>
</phaseOrder>
定義済みフェーズにおいてモジュールを実行する場合は、設定を編集する必要はありません。モジュールを実行するために専用のフェーズを追加する場合は、定義済みフェーズと同じ記述方法で、フェーズを追加します。
今回は定義済みフェーズである OperationOutPhase
にて、モジュールを実行することにします。よって、設定ファイルは編集しませんでした。
モジュール定義を作成する
次に、モジュールの定義を記述するファイルを作成します。ファイル名は module.xml
とします。
<!-- モジュール名を指定し、モジュール クラスを指定します。ここで付けたモジュール名は、後でサービスとモジュールを関連付ける時に使用します。 -->
<module name="userSupport" class="axis2module.module.UserSupportModule">
<!-- 実行するフローを指定します。 -->
<OutFlow>
<!-- 使用するハンドラー クラスを指定し、名前をつけます。 -->
<handler name="addSoapHeaderHandler" class="axis2module.module.AddSoapHeaderHandler">
<!-- 実行するフェーズを指定します。 -->
<order phase="OperationOutPhase" />
</handler>
</OutFlow>
</module>
サービスにモジュールを関連付ける
最後に、サービスとモジュールを関連付けます。サービス定義ファイル(services.xml
)を次のように編集します。
<service name="UserSupport">
<Description>Axis2 module test.</Description>
<parameter name="ServiceClass" locked="false">axis2module.service.UserSupport</parameter>
<!-- このサービス内のすべてのメソッドでモジュールを実行する場合は、<service> 要素の直下に <module> 要素を記述することもできます。 -->
<!-- <module ref="userSupport"/> -->
<operation name="Inquire">
<!-- サービス内の個々のメソッドで実行するモジュールを指定する場合は、対象の <operation> 要素の直下に <module> 要素を記述します。 -->
<!-- ref 属性には、module.xml で定義したモジュール名を指定します。 -->
<module ref="userSupport"/>
</operation>
</service>
ここまでを行ってから Web サービスを呼び出すと、ようやく SOAP ヘッダーが付与されたレスポンスが出力されます。ヘッダーを追加するだけにしてはけっこう大変です…。ですが、この手法を知っておけばヘッダーを追加するだけではなくいろいろな機能を追加できますし、作成したモジュールやハンドラーは再利用することもできます。
デプロイ
上記の一連の作業を、私は IDE(Eclipse)上で試していたので、クラスや設定ファイルを生で扱っていたのですが、実行環境にデプロイするときは、モジュール一式を『mar ファイル』にする必要があります。mar ファイルは、拡張子が「.mar」のファイルで、『jar ファイル』と同じように作成します。Axis2 のサービスを『aar ファイル』にするのと同じ考え方でしょう。
プロジェクトの構成
最終的なプロジェクトの構成は、次のようになりました。