IBM alphaWorksのWeb Services Toolkitバージョン3.2.2に含まれるWeb Services Busは、Webサービス・プロセッサーを構築するためのフレームワークです。(WSTKへのリンクは、このあとの参考文献を参照してください。)Busは、サービス・リクエスターのためのWebサービス (クライアント・サイドのon-ramp) と、サービス・プロバイダーのための Webサービス (サーバー・サイドのoff-ramp) の両方により、動的なe-businessを実施するための拡張された環境を提供します。
この連載記事の第1回では、Webサービス実装を公開し、発見し、呼び出すためのBusのユニークな機能を活用する方法について学習しました。今回の記事では、Busが提供しているフィルター についてさらに学習します。フィルターでは、on-rampとoff-rampの両方で要求と応答を操作することができます。Busフィルターは、Apache Axisハンドラー と類似しています。Busフィルターは、Web Services Invocation Framework (WSIF) の流れを引き継ぐBusと一貫性を保つために、現在のAxisハンドラーのようにSOAP固有の形式を操作するのではなく、正規形式の情報を操作します。
WSTKバージョン3.2.2の一部であるBusプロトタイプには、Busフィルターを配置して使用する方法の実例など、Busのさまざまな機能を示すデモが盛り込まれています。この記事では、それらの実例を土台にして新しいサンプルを作成し、それらのサンプルを利用してBusフィルターについて詳しく調べていきます。さらに、Busフィルターに基づく実装をAxisハンドラーに基づく実装と比較して、Busフィルターの利点を実証します。より詳細な情報については、Busプロトタイプ実装に付属の資料を参照してください。(参考文献を参照。)
この記事のサンプルは、どこからでもアクセスできる株価情報サービスJavaBeanです。BusバージョンのサンプルとAxisバージョンのサンプルで、同じ実装を使用します。この実装は、この連載記事の第1回で使用したものとは若干異なります。このサンプルを興味深いものにしているのは、BusフィルターとAxisハンドラーです。そのどちらも、インターセプター という総称的なカテゴリーに属します。このサンプルのインターセプターは、ペイロードの圧縮と圧縮解除を、非常に単純な技法でシミュレートしています。図1 は、このサンプルで使用されている全体的なアーキテクチャーを示しています。
図1. サンプルのクライアント/サーバー・アーキテクチャー
図1 は、サンプルのクライアント・サイドとサーバー・サイドの両方を示しています。クライアント・サイドでは、サービス・プロバイダーと対話する何らかの形のWebサービス・エンジンを使用して、サービス・リクエスターが株価情報を要求します。そのエンジンとしては、Axisクライアント・エンジンまたはBus on-rampエンジンのどちらでも使用可能です。エンジンは、要求を調べるインターセプターを呼び出し、必要に応じて内容を圧縮し、使用した圧縮技法を示すコンテキスト を要求に挿入します。最終的に、エンジンは、修正された要求をサービス・プロバイダーに送信します。
サーバー・サイドでは、Axisサーバー・エンジンまたはBus off-rampエンジンが、入ってくる要求を受け取り、インターセプターを呼び出します。インターセプターは、要求から取得したコンテキストの中で示されている圧縮技法に基づいて、必要に応じて内容を圧縮解除します。エンジンは、修正された要求を実際のサービス・プロバイダー (この場合は、JavaBean) に引き渡します。このサンプルでは、プロバイダーからの応答が何らかのインターセプターを通ることはありませんが、AxisとBusはどちらもその機能をサポートしています。
リスト1 に、サンプルで使用される株価情報サービスの実装SQ.java を掲載します。
リスト1. SQ.java、株価情報サービスのサンプル
package gaf.filter.sq;
public class SQ {
public float getQuote(String symbol) throws Exception {
if (symbol.equals("XXX")) {
return ((float) 55.25);
} else if (symbol.equals("YYY")) {
return ((float) 125.35);
} else {
return ((float) 0);
}
}
}
|
株価情報サービスのこの実装は、シンボルXXXおよびYYYに対してそれぞれ固有の値を返し、それ以外に対してはゼロを返します。ここで注目するべき重要な点は、サービスでは、実行されているかもしれない圧縮と圧縮解除について知る必要がないという点です。
どちらのクライアントも、WSDLによって駆動されるように記述しました。WSIFはWSDLによって駆動されるため、このことはBusクライアントの場合には必須です。Axisの場合には、これはオプションであり、WSDLによって駆動されるようにすると、絶対的に必要な構成よりもAxisクライアントがやや複雑になります。しかし、そのようにするのは一般に良い習慣であると言えます。サービスを呼び出すのに必要な情報をAxisが自動的に抽出できるようになるからです。
Busクライアント
リスト2 は、Busバージョンのクライアントの概要を示しています。両方のクライアントの完全なソースは、この記事に付属のコード・パッケージに含まれています。
リスト2. Busバージョンのサンプル・クライアントの概要
package services.demos.wsbus.client.examples;
public class BusSQClient extends SQClientBase {
public static void main(String[] args) throws Exception {
System.setProperty(
WSIFServiceFactory.WSIFFACTORY_CLASSNAME,
"com.ibm.wsbus.wsif.WSIFBusServiceFactory");
WSIFBusServiceFactory factory =
(WSIFBusServiceFactory) WSIFServiceFactory.newInstance();
WSIFService service = factory.getService(null, args[0]);
getQuote(service, "XXX");
getQuote(service, "YYY");
getQuote(service, "ZZZ");
}
private static void getQuote(WSIFService service, String sym) throws Exception {
javax.wsdl.Port wp = getPort();
WSIFPort port = service.getPort(wp.getName());
WSIFOperation operation =
port.createOperation("getQuote", "getQuoteRequest", null);
WSIFMessage inputMessage = operation.createInputMessage();
WSIFMessage outputMessage = operation.createOutputMessage();
WSIFMessage faultMessage = operation.createFaultMessage();
inputMessage.setObjectPart("symbol", sym);
boolean operationSucceeded =
operation.executeRequestResponseOperation(
inputMessage, outputMessage,
faultMessage);
System.out.println("The quote: " + outputMessage.getFloatPart("return"));
}
}
|
BusバージョンのクライアントBusSQClient では、まず最初に、Busが使用されていることを確認するのに必要なステップを実行します。それには、システム・プロパティーを適切に設定することや、サービスの表現を生成するためにBus固有のファクトリーを使用することが含まれます。次に、クライアントは、受け取ったWSDL (arg[0]) によって定義されている株価情報サービスの表現を取得します。株価を取得するために、クライアントは、サービスのために定義された単一のポートを取得します。そのポートをファクトリーとして使用して、クライアントは、示されているシグニチャー (操作名とパラメーター名) によって定義されるgetQuote 操作の表現を取得します。最後に、クライアントは、その操作を呼び出します。
Axisクライアント
リスト3 は、Axisバージョンのクライアントの概要を示しています。
リスト3. Axisバージョンのサンプル・クライアント
package services.demos.wsbus.client.examples;
public class AxisSQClient extends SQClientBase {
public static void main(String[] args) throws Exception {
QName serviceQN = wsdlService.getQName();
Service service = new Service(new URL(args[0]), serviceQN);
getQuote(service, "XXX");
getQuote(service, "YYY");
getQuote(service, "ZZZ");
}
private static void getQuote(Service service, String sym) throws Exception {
QName portQN = new QName(def.getTargetNamespace(), getPort().getName());
Call call = (Call) service.createCall(portQN, "getQuote");
Object in[] = new Object[1];
RPCParam p = new RPCParam(", "symbol", sym);
in[0] = p;
Float ret = (Float) call.invoke(in);
System.out.println("The quote: " + ret);
}
}
|
このバージョンのクライアントAxisSQClient では、まず最初に、受け取ったWSDL (arg[0]) によって定義されている株価情報サービスの表現を取得します。株価を取得するために、クライアントは、サービス内の唯一のポートに対する操作の表現を取得します。最後に、クライアントは、その操作を呼び出します。
重要な点として、どちらのクライアントも、何らかの圧縮が実行されたか実行されなかったかを知らないという点に注目してください。
インターセプターは、当然ながら、相補的なものです。つまり、圧縮解除インターセプターは、メッセージの本体に対して圧縮インターセプターが何を実行したかを理解する必要があります。
Busフィルター
リスト4 は、要求を圧縮するために使用されるBusフィルターの概要を示しています。この記事のコード・パッケージに、完全なソースが含まれています。
リスト4. 要求を圧縮するために使用されるBusフィルターの概要
package services.demos.wsbus.client.filters;
public class CompressFilter implements Filter {
private static QName name = new QName("http://gaf.filter.test", "compressType");
public FilterAction filterRequest(WSIFRequest request, WSIFResponse response)
throws WSBusException, FilterException {
try {
// get parameter
WSIFMessage requestMessage = request.getIncomingMessage();
sym = (String) requestMessage.getObjectPart("symbol");
// "compress"
if (sym.equals("XXX")) {
System.out.println("Filter compressing with X type.");
requestMessage.setObjectPart("symbol", "X");
compType = "X";
} else if (sym.equals("YYY")) {
System.out.println("Filter compressing with Y type.");
requestMessage.setObjectPart("symbol", "Y");
compType = "Y";
}
// create element that indicates compression type
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
Document doc = factory.newDocumentBuilder().newDocument();
Element token = doc.createElementNS(name.getNamespaceURI(), name.getLocalPart());
token.setAttribute("type", compType);
// insert context that indicates compression type
WSIFMessage contextMessage = request.getContextMessage();
if (contextMessage == null) {
contextMessage = new WSIFDefaultMessage();
request.setContextMessage(contextMessage);
}
contextMessage.setObjectPart(name.toString(), token);
} catch (Exception e) {
e.printStackTrace();
throw new WSBusException("Compressor: didn't work!");
}
return action;
}
}
|
CompressFilter は、まず最初に、受け取った要求からシンボルを取得します。次に、ごく単純なアルゴリズムを使ってそのシンボルを圧縮します。圧縮した後、フィルターは、要求の中の受信シンボルをその圧縮版で置き換えます。次に、フィルターは、シンボルをどのように圧縮したかを示すコンテキストを作成し、そのコンテキスト情報を全体的な要求コンテキストの中に書き込みます。この作業のすべてが正規形式の情報に対して実施されるため、実際のサービス・プロバイダーと対話するために使用される形式やトランスポートが何であっても同じ作業になることに注意してください。
リスト5 は、要求を圧縮解除するために使用されるBusフィルターの概要を示しています。
リスト5. 要求を圧縮解除するために使用されるBusフィルターの概要
package gaf.filter.sq;
public class DecompressFilter implements Filter {
private static QName name = new QName("http://gaf.filter.test", "compressType");
public FilterAction filterRequest(WSIFRequest request, WSIFResponse response)
throws WSBusException, FilterException {
try {
// get parameter
WSIFMessage requestMessage = request.getIncomingMessage();
sym = (String) requestMessage.getObjectPart("symbol");
// get the compression type
WSIFMessage contextMessage = request.getContextMessage();
// since do 'custom' name creation from qname, use following format
Element token =
(Element) contextMessage.getObjectPart(name.getNamespaceURI() +
":" + name.getLocalPart());
compType = token.getAttribute("type");
// "decompress"
WSIFMessage newReq = null;
if (!compType.equals("none")) {
newReq = new WSIFDefaultMessage();
request.setIncomingMessage(newReq);
}
if (compType.equals("X")) {
System.out.println("Filter decompressing with type X.");
newReq.setObjectPart("symbol", "XXX");
} else if (compType.equals("Y")) {
System.out.println("Filter decompressing with type Y.");
newReq.setObjectPart("symbol", "YYY");
}
} catch (Exception ex) {
throw new WSBusException("Decompressor: can't work!");
}
return action;
}
}
|
DecompressFilter は、まず最初に、受け取った圧縮済みのシンボルを取得します。次に、フィルターは、圧縮のタイプを示すコンテキストの部分を取得し、その情報を抽出します。off-ramp内のコンテキストの名前は、コンテキストのQName から派生させられます。Busは、<namespace>:<local name> という形式を使ってコンテキストに名前を付けます。次に、フィルターは、圧縮のタイプに従ってシンボルを圧縮解除し、要求の中の圧縮されたシンボルを圧縮解除したシンボルで置き換えます。
Axisハンドラー
リスト6 は、要求を圧縮するために使用されるAxisハンドラーの概要を示しています。
リスト6. 要求を圧縮するために使用されるAxisハンドラー
package services.demos.wsbus.client.filters;
public class CompressHandler extends BasicHandler {
private static QName name = new QName("http://gaf.filter.test", "compressType");
public void invoke(MessageContext context) throws AxisFault {
try {
// get the parameter
SOAPPart part =
(org.apache.axis.SOAPPart) context.getCurrentMessage().getSOAPPart();
SOAPEnvelope envelope = part.getAsSOAPEnvelope();
RPCElement pel = (RPCElement) envelope.getFirstBody();
RPCParam parm = pel.getParam("symbol");
sym = (String) parm.getValue();
// "compress"
if (sym.equals("XXX")) {
System.out.println("Handler compressing with X type.");
parm.setValue("X");
compType = "X";
} else if (sym.equals("YYY")) {
System.out.println("Handler compressing with Y type.");
parm.setValue("Y");
compType = "Y";
}
// create element that indicates compression type
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
Document doc = factory.newDocumentBuilder().newDocument();
Element token = doc.createElementNS(name.getNamespaceURI(), name.getLocalPart());
token.setAttribute("type", compType);
// insert a header that indicates compression type
envelope.addHeader(new SOAPHeaderElement(token));
part.setSOAPEnvelope(envelope);
} catch (Exception ex) {
AxisFault fault = AxisFault.makeFault(ex);
throw fault;
}
}
}
|
CompressHandler は、まず最初に、株式シンボルを取得します。次に、そのシンボルを、Busフィルターで使用されたのと同じアルゴリズムで圧縮します。ハンドラーは、シンボルの圧縮版を要求の中に書き込み、元のシンボルと置き換えます。次に、ハンドラーは、シンボルをどのように圧縮したかを示すヘッダーを作成し、そのヘッダーを全体的な要求コンテキストの中に書き込みます。この作業のすべてが、SOAP固有形式の情報に対して実施されることに注意してください。これは、この2種類の圧縮インターセプターの重要な違いです。なお、圧縮のアルゴリズムと、BusコンテキストやSOAPヘッダーで使用される情報の形式は同じです。
リスト7 は、要求を圧縮解除するために使用されるAxisハンドラーを示しています。
リスト7. 要求を圧縮解除するために使用されるAxisハンドラー
package gaf.filter.sq;
public class DecompressHandler extends BasicHandler {
private static QName name = new QName("http://gaf.filter.test", "compressType");
public void invoke(MessageContext context) throws AxisFault {
try {
// get the parameter
SOAPPart part =
(org.apache.axis.SOAPPart) context.getCurrentMessage().getSOAPPart();
SOAPEnvelope envelope = part.getAsSOAPEnvelope();
RPCElement pel = (RPCElement) envelope.getFirstBody();
RPCParam parm = pel.getParam("symbol");
sym = (String) parm.getValue();
// get the compression type
SOAPHeaderElement header =
envelope.getHeaderByName(name.getNamespaceURI(), name.getLocalPart());
Element token = header.getAsDOM();
compType = token.getAttribute("type");
// "decompress"
if (compType.equals("X")) {
System.out.println("Handler decompressing with type X.");
parm.setValue("XXX");
} else if (compType.equals("Y")) {
System.out.println("Handler decompressing with type Y.");
parm.setValue("YYY");
}
} catch (Exception ex) {
AxisFault fault = AxisFault.makeFault(ex);
throw fault;
}
}
}
|
DecompressHandler は、まず最初に、受け取った圧縮済みのシンボルを取得します。次に、ハンドラーは、圧縮のタイプを示すヘッダーを取得し、ヘッダーからタイプを抽出します。次に、ハンドラーは、圧縮のタイプに従ってシンボルを圧縮解除し、圧縮されたシンボルを圧縮解除したシンボルで置き換えます。圧縮インターセプターの場合と同じく、重要な違いは、Busフィルターは正規形式のデータを処理するのに対して、AxisフィルターはSOAP固有のデータを処理するということです。なお、圧縮解除のアルゴリズムと、BusコンテキストやSOAPヘッダーの内容は同じです。
この記事で紹介しているサンプルを実行するには、IBM alphaWorks (参考文献を参照) から入手可能なWeb Services Toolkitバージョン3.2.2をインストールして構成する必要があります。WSTKには、Axis実装と、Web Services Busプロトタイプ実装が含まれています。Busアーキテクチャーでは、クライアントとサーバーの間で通信するために複数のデータ形式およびトランスポート・プロトコルをBusが使用できることになっています。しかし、Busのプロトタイプでは、Axisを使用するSOAP/HTTPだけを実装しています。
WSTKは、WindowsおよびLinuxの多くのWebアプリケーション・サーバー上で稼動します。この記事のサンプルでは、WSTKがWindows上のIBM WSDK内にインストールされていることを想定しています。それとは異なるプラットフォームを使用している場合には、パス名、ホスト名、コマンド・ファイル、その他を、環境に合わせて調整する必要があるかもしれません。(参考文献を参照。)
WSTKをインストールした後は、Webアプリケーション・サーバーを始動し、Busプロトタイプが稼動していることを確認してください。コマンド・ウィンドウで、WSTK_HOME/services/demos/wsbus/client を開きます (WSTK_HOME はWSTKのインストール先ディレクトリー)。そのディレクトリーで、1つまたは複数のデモを実行します。たとえば、Axisと、Busのクライアント・サイドとサーバー・サイドの両方をテストするSimpleClientFilter.bat を実行するといいでしょう。デモが正常に実行されたら、次のセットアップ手順に進みます。
次に、この記事に付属のコード・パッケージから、いくつかのファイルを適切な位置にコピーする必要があります。このパッケージは、WSTKのディレクトリー構造を反映しています。したがって、パッケージ・ディレクトリーservices/demos/wsbus/client にあるファイルは、サブディレクトリー内のファイルもすべて含めて、WSTK_HOME/services/demos/wsbus/client にコピーします。また、パッケージ・ディレクトリーservices/demos/wsbus/webapp 内のファイルは、Webアプリケーション・サーバーのインストールされたアプリケーション・ディレクトリー内のWSDK WARディレクトリーにコピーします。WSDKの場合、このディレクトリーはWSDK_HOME/WebSphere/installedApps/wstk.ear/wstk.war です (WSDK_HOME はWSDKのインストール先ディレクトリー)。
言うまでもなく、サンプルを実行するには、単にファイルをコピーするだけでなく、それ以上の作業が必要です。たとえば、株価情報サービスとインターセプターを、クライアント・サイドとサーバー・サイドの両方に配置する必要があります。上記の手順でコピーしたファイルの中に、必要なコンポーネントを配置し、株価情報を要求し、最後にコンポーネントをアンデプロイ (配置削除) するのに必要なバッチ・ファイルと配置ディスクリプターが含まれています。
まずは簡単な実例から始めるために、BusクライアントをBusサーバーに対して実行し、AxisクライアントをAxisサーバーに対して実行することにします。この後のセクションではもう少し難しい例を紹介しますが、最初は基礎から始めましょう。
BusクライアントをBusサーバーに対して実行するには、バッチ・ファイルWSTK_HOME/services/demos/wsbus/client/RunBusSQ.bat を実行します。このバッチ・ファイルは、以下の処理を行います。
- Busサービスと圧縮解除フィルターをBus off-rampに対して配置する。
- Busサービスと圧縮フィルターをBus on-rampに対して配置し、
BusSQClientを実行する。 - Busサービスと圧縮解除フィルターをBus off-rampからアンデプロイする。
上記のステップ1 で使用される配置ディスクリプターdeployBusSQ.xml を、リスト8 に示します。
リスト8. deployBusSQ.xml
<deploy force="true">
<filters>
<filter name="decompressor" started="true"
className="gaf.filter.sq.DecompressFilter"/>
</filters>
<busServices>
<busService serviceName="SQBus" ...>
<channels>
<channelReference channel="axis"/>
</channels>
<targetServices>
<targetService className="gaf.filter.sq.SQ" serviceName="SQService" .../>
</targetServices>
<preFilters>
<filterReference filter="decompressor" />
</preFilters>
</busService>
</busServices>
</deploy>
|
この配置ディスクリプターにより、Bus off-rampには、クラスgaf.filter.sq.DecompressFilter がBusフィルターとしてdecompressor という名前で配置され、JavaBeangaf.filter.sq.SQ がbusサービスとしてSQBus という名前で配置されます。さらに、off-rampでは、decompressor がSQBus に対するプリフィルターとして列挙されます。これにより、Bus off-rampでは、要求をJavaBeanに引き渡す前に圧縮解除フィルターが実行されるようになります。
上記の手順のステップ2 では、Bus on-rampの動的構成メカニズムを使用して、クライアントを実行するための完全な構成をセットアップします。WSTKに付属のデモによって使用されるデフォルトのon-ramp構成に対して、永続的な影響が及ぶことはありません。リスト9 は、クライアントを実行するために (wsbusClientBusSQ.cfg によって) 確立される構成を示しています。
リスト9. クライアントを実行するために確立される構成
<busInfo started="true" >
<filters >
<filter name="compressor"
className="services.demos.wsbus.client.filters.CompressFilter" ... />
</filters>
<busServices >
<busService serviceName="SQBus" ... >
<preFilters >
<filterReference filter="compressor" >
</filterReference>
</preFilters>
</busService>
</busServices>
</busInfo>
|
このon-ramp構成では、クラスservices.demos.wsbus.client.filters.CompressFilter がBusフィルターとしてcompressor という名前で配置され、compressor がBusサービスに対するプリフィルターとしてSQBus という名前で列挙されます。これにより、Bus on-rampでは、要求がWSIFプロバイダーに送られて直列化され、実際のサービス・プロバイダーに送信される前に、圧縮フィルターが実行されるようになります。Busの場合、サービス名は、そのサービスを記述しているWSDLのservice要素のname属性から取られることに注意してください。RunBusSQ.bat の中を少し見てみれば、Busに配置される株価情報サービスを記述しているWSDLのURLがhttp://<hostname>:<port>/wstk/wsbus/ServiceDefinition?name=SQBus になっていることがわかるはずです (hostnameとportは、使用しているWebアプリケーション・サーバーに応じて適切に設定されている)。このURLは、Bus off-rampの動的WSDL生成機能を反映しています。この機能では、SQBus がservice要素のname属性として生成されます。
RunBusSQ.bat を (パラメーターを指定しないで) 実行すると、コマンド・ウィンドウにリスト10 のような結果が表示されるはずです。まず最初に、off-rampへの配置が完了したことが表示されます (ステップ1)。次に、WSDL文書を正しく取得できたことが表示され、さらに、一番大切な点として、クライアント上での圧縮とサーバー上での圧縮解除という往復の処理が正しく完了したことが表示されます (ステップ2)。最後に、off-rampからのアンデプロイが完了したことが表示されます (ステップ3)。
リスト10. BusクライアントとBusサーバーを実行する: 結果
C:\wstk-3.2.2\services\demos\wsbus\client>RunBusSQ.bat
===> Deploy the service and filter in the Bus off-ramp engine
Results: OK
===> Deploy service and filter in Bus on-ramp engine
===> Request quotes
Retrieving document at 'http://<hostname>:<port>/wstk/wsbus/ServiceDefinition?name=SQBus'.
Retrieving import document at 'http://<hostname>:<port>/wstk/wsbus/ServiceInterface?name=SQBus
Using WSIFServiceFactory
BusServiceFactory: no class name for discovery or selection. Using default.
Filter compressing with X type.
The quote: 55.25
Filter compressing with Y type.
The quote: 125.35
The quote: 0.0
===> Undeploy the service and filter in the Bus off-ramp
Results: OK
|
もしお望みなら、コンソールかログを調べることにより、off-rampフィルターの実行を確認することができます。リスト11にあるようなログ項目が見つかるはずです。
リスト11. BusクライアントとBusサーバーの実行に関するログ項目
[<date & time >] 6fa289b6 SystemOut U Filter decompressing with type X.
[<date & time >] 6fa289b6 SystemOut U Filter decompressing with type Y.
|
AxisクライアントをAxisサーバーに対して実行するには、バッチ・ファイルWSTK_HOME/services/demos/wsbus/client/RunAxisSQ.bat を実行します。このバッチ・ファイルは、以下の処理を行います。
- サービスと圧縮解除ハンドラーをAxisサーバーに対して配置する。
- サービスと圧縮ハンドラーをAxisクライアントに対して配置する。
-
AxisSQClientを実行する。 - サービスと圧縮ハンドラーをAxisクライアントからアンデプロイする。
- サービスと圧縮解除ハンドラーをAxisサーバーからアンデプロイする。
ステップ1 で使用される配置ディスクリプター (deployAxisSQ.wsdd) を、リスト12 に示します。
リスト12. deployAxis.wsdd
<deployment xmlns="http://xml.apache.org/axis/wsdd/"
xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
<handler name="decompressor" type="java:gaf.filter.sq.DecompressHandler">
</handler>
<service name="SQAxis" provider="java:RPC">
<parameter name="className" value="gaf.filter.sq.SQ"/>
<parameter name="allowedMethods" value="getQuote"/>
<requestFlow>
<handler type="decompressor"/>
</requestFlow>
</service>
</deployment>
|
この配置ディスクリプターにより、Axisサーバー・エンジンには、クラスgaf.filter.sq.DecompressHandler がハンドラーとしてdecompressor という名前で配置され、JavaBeangaf.filter.sq.SQ がサービスとしてSQAxis という名前で配置されます。さらに、decompressor が、SQAxis に対する要求フローの中に列挙されます。これにより、Axisサーバー・エンジンでは、要求をJavaBeanに引き渡す前に圧縮解除フィルターが実行されるようになります。
上記の手順のステップ2 では、配置ディスクリプターdeployAxisClient.wsdd (リスト13 を参照) が使用されます。
リスト13. deployAxisClient.wsdd
<deployment xmlns="http://xml.apache.org/axis/wsdd/"
xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
<handler name="compressor"
type="java:services.demos.wsbus.client.filters.CompressHandler">
</handler>
<service name="http://localhost/wstk/services/SQAxis/wstk/services/SQAxis"
provider="java:RPC">
<requestFlow>
<handler type="compressor"/>
</requestFlow>
</service>
</deployment>
|
この配置ディスクリプターにより、クラスservices.demos.wsbus.client.filters.CompressHandler がハンドラーとしてcompressor という名前で配置され、compressor がサービスに対する要求フローの中にhttp://localhost/wstk/services/SQAxis/wstk/services/SQAxis という名前で列挙されます。これにより、Axisクライアント・エンジンでは、要求を実際のサービス・プロバイダーに送信する前に圧縮フィルターが実行されるようになります。Axisでは、サービス名を派生させる方法がBusの場合と異なります。サービスを記述しているWSDLに、SOAPバインディング要素が含まれています。その要素にinput要素が含まれており、Axisは、そのinput要素のnamespace属性からサービス名を派生させます。RunAxisSQ.bat の中を少し見てみれば、Axisに配置される株価情報サービスを記述しているWSDLのURLがhttp://<hostname>:<port>/wstk/service/SQAxis?wsdl になっていることがわかるはずです (hostnameとportは、使用しているWebアプリケーション・サーバーに応じて適切に設定されている)。このURLは、Axisの動的なWSDL生成機能を反映しています。上記で使用されている名前は、デフォルトのポート (80) を使用するWebアプリケーション・サーバーを反映しています。もし別のポートを使用する場合は、配置ディスクリプター (および、それに対応する、アンデプロイに使用される配置ディスクリプター) を修正する必要があります。
RunAxisSQ.bat を (パラメーターを指定しないで) 実行すると、コマンド・ウィンドウにリスト14 のような結果が表示されるはずです。まず、Axisサーバーへの配置完了 (ステップ1) と、クライアントへの配置完了 (ステップ2) が表示されます。次に、WSDL文書を正しく取得できたことが表示され、さらに、一番大切な点として、クライアント上での圧縮とサーバー上での圧縮解除という往復の処理が正しく完了したことが表示されます (ステップ3)。最後に、アンデプロイの完了が表示されます (ステップ4および5)。
リスト14. AxisクライアントとAxisサーバーを実行する: 結果
C:\wstk-3.2.2\services\demos\wsbus\client>RunAxisSQ.bat
===> Deploy the service and handler in the Axis server engine
- Processing file deployment/deployAxisSQ.wsdd
<Admin>Done processing</Admin>
===> Deploy the service and handler in the Axis client engine
===> Request quotes
Retrieving document at 'http://<hostname>:<port>/wstk/services/SQAxis?wsdl'.
Handler compressing with X type.
The quote: 55.25
Handler compressing with Y type.
The quote: 125.35
The quote: 0.0
===> Undeploy the service and handler from Axis client engine
===> Undeploy the service and handler from the Axis server engine
- Processing file deployment/undeployAxisSQ.wsdd
<Admin>Done processing</Admin>
|
もしお望みなら、コンソールかログを調べることにより、Axisハンドラーの実行を確認することができます。リスト15 にあるようなログ項目が見つかるはずです。
リスト15. AxisクライアントとAxisサーバーの実行に関するログ項目
[<date & time >] 6fce09b6 SystemOut U Handler decompressing with type X.
[<date & time >] 6fce09b6 SystemOut U Handler decompressing with type Y.
|
では次に、興味深い実例として、Webサービスに本来備わっている相互運用性をデモンストレーションしてみましょう。次の2つのセクションでは、まずBusクライアントを使用してAxisに配置されたサービスと対話し、次にAxisクライアントを使用してBusに配置されたサービスと対話します。これらのサンプルを実行するのに、コードの変更は不要です。これが可能なのは、Busプロトタイプで唯一のトランスポート・メカニズムとしてAxisを使用しているからにほかなりません。理論上は、Busは別の形式およびプロトコル (たとえば、RMI/IIOP) も使用できますが、そうした場合には、Axisハンドラーは利用できなくなります。しかし、Busフィルターなら利用できます。
BusクライアントをAxisサーバーに対して実行するには、バッチ・ファイルWSTK_HOME/services/demos/wsbus/client/RunBusSQX.bat を実行します。このバッチ・ファイルは、以下の処理を行います。
- サービスと圧縮解除ハンドラーをAxisサーバーに対して配置する。
- busサービスと圧縮フィルターをBus on-rampに対して配置し、
BusSQClientを実行する。 - busサービスと圧縮解除ハンドラーをAxisサーバーからアンデプロイする。
ステップ1 で使用される配置ディスクリプター (deployAxisSQ.wsdd) は、「Axisクライアント/Axisサーバー」のサンプルでAxisサーバー・コンポーネントを配置するのに使用されたものと同じです。そのコードを詳しく調べるには、リスト12 を参照してください。
ステップ2 では、Bus on-rampの動的構成メカニズムを使用して、クライアントを実行するための完全な構成をセットアップします。リスト16 は、クライアントを実行するために (wsbusClientBusSQX.cfg によって) 確立される構成を示しています。
リスト16. クライアントを実行するために確立される構成
<busInfo started="true" >
<filters >
<filter name="compressor"
className="services.demos.wsbus.client.filters.CompressFilter" ... />
</filters>
<busServices >
<busService serviceName="SQService" ... >
<preFilters >
<filterReference filter="compressor" >
</filterReference>
</preFilters>
</busService>
</busServices>
</busInfo>
|
このon-ramp構成は、「Busクライアント/Busサーバー」のサンプルでの構成と、busサービスの名前が違っているだけです。つまり、SQBus ではなく、SQService にする必要があります。RunBusSQX.bat の中を少し見てみれば、Bus on-rampに配置される株価情報サービスを記述しているWSDLのURLがhttp://<hostname>:<port>/wstk/services/SQAxis?wsdl になっていることがわかるはずです。Axisは、Busがサービスを識別するために使用するservice要素のname属性に対してSQService という名前を生成します。
RunBusSQX.bat を (パラメーターを指定しないで) 実行すると、コマンド・ウィンドウにリスト17 のような結果が表示されるはずです。まず、Axisサーバーの配置完了 (ステップ1) が表示されます。次に、WSDL文書を正しく取得できたことが表示され、さらに、一番大切な点として、Busクライアント上での圧縮とAxisサーバー上での圧縮解除という往復の処理が正しく完了したことが表示されます (ステップ2)。最後に、Axisサーバーからのアンデプロイが完了したことが表示されます (ステップ3)。
リスト17. BusクライアントとAxisサーバーを実行する: 結果
C:\wstk-3.2.2\services\demos\wsbus\client>RunBusSQX.bat
===> Deploy the service and handler in the Axis server engine
- Processing file deployment/deployAxisSQ.wsdd
<Admin>Done processing</Admin>
===> Deploy service and filter in Bus on-ramp engine
===> Request quotes
Retrieving document at 'http://<hostname>:<port>/wstk/services/SQAxis?wsdl'.
using WSIFServiceFactory
BusServiceFactory: no class name for discovery or selection. Using default.
Filter compressing with X type.
The quote: 55.25
Filter compressing with Y type.
The quote: 125.35
The quote: 0.0
===> Undeploy the service and handler from the Axis server engine
- Processing file deployment/undeployAxisSQ.wsdd
<Admin>Done processing</Admin>
|
もしお望みなら、コンソールかログを調べることにより、Axisサーバー内のハンドラーの実行を確認することができます。リスト18 にあるようなログ項目が見つかるはずです。
リスト18. BusクライアントとAxisサーバーの実行に関するログ項目
[<date & time >] 6fa289b6 SystemOut U Handler decompressing with type X.
[<date & time >] 6fa289b6 SystemOut U Handler decompressing with type Y.
|
AxisクライアントをBusサーバーに対して実行するには、バッチ・ファイルWSTK_HOME/services/demos/wsbus/client/RunAxisSQX.bat を実行します。このバッチ・ファイルは、以下の処理を行います。
- busサービスと圧縮解除フィルターをBus off-rampに対して配置する。
- サービスと圧縮ハンドラーをAxisクライアントに対して配置する。
-
AxisSQClientを実行する。 - サービスと圧縮ハンドラーをAxisクライアントからアンデプロイする。
- busサービスと圧縮解除フィルターをBus off-rampからアンデプロイする。
ステップ1 で使用される配置ディスクリプター (deployBusSQ.xml) は、「Busクライアント/Busサーバー」のサンプルでBus on-rampコンポーネントを配置するのに使用されたものと同じです。そのコードを詳しく調べるには、リスト8 を参照してください。
ステップ2 では、deployAxisClientX.wsdd という配置ディスクリプター (リスト19 に示されている) を使用します。
リスト19. deployAxisClientX.wsdd
<deployment xmlns="http://xml.apache.org/axis/wsdd/"
xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
<handler name="compressor"
type="java:services.demos.wsbus.client.filters.CompressHandler">
</handler>
<service name="http://mybus/#SQBus" provider="java:RPC">
<requestFlow>
<handler type="compressor"/>
</requestFlow>
</service>
</deployment>
|
この配置ディスクリプターと、「Axisクライアント/Axisサーバー」のサンプルで使用した配置ディスクリプターの唯一の違いは、サービス名としてhttp://localhost/wstk/services/SQAxis/wstk/services/SQAxis ではなくhttp://mybus/#SQBus を使用していることです。Axisの場合、サービス名は、そのサービスを記述しているWSDLのSOAPバインディング要素内にあるinput要素のnamespace属性から取られることに注意してください。RunAxisSQ.bat の中を少し見てみれば、Axisに配置される株価情報サービスを記述しているWSDLのURLがhttp://<hostname>:<port>/wstk/wsbus/ServiceDefinition?name=SQBus になっていることがわかるはずです。Busは、SOAPバインディング要素に対してhttp://mybus/#SQBus を生成します。Busプロトタイプ構成のnamespaceURI 属性がhttp://mybus/ であり、Busはそれに# とサービス名を付加するからです。
RunAxisSQX.bat を (パラメーターを指定しないで) 実行すると、コマンド・ウィンドウにリスト20 のような結果が表示されるはずです。まず、Bus on-rampの配置完了 (ステップ1) と、Axisクライアントの配置完了 (ステップ2) が表示されます。次に、WSDL文書を正しく取得できたことが表示され、さらに、一番大切な点として、Axisクライアント上での圧縮とBusサーバー上での圧縮解除という往復の処理が正しく完了したことが表示されます (ステップ3)。最後に、アンデプロイの完了が表示されます (ステップ4および5)。
リスト20. AxisクライアントとBusサーバーを実行する: 結果
C:\wstk-3.2.2\services\demos\wsbus\client>RunAxisSQX.bat
===> Deploy the service and filter in the Bus off-ramp engine
Results: OK
===> Deploy the service and handler in the Axis client engine
===> Request quotes
Retrieving document at 'http://<hostname>:<port>/wstk/wsbus/ServiceDefinition?name=SQBus'.
Retrieving import document at 'http://<hostname>:<port>/wstk/wsbus/ServiceInterface?name=SQBus
Handler compressing with X type.
The quote: 55.25
Handler compressing with Y type.
The quote: 125.35
The quote: 0.0
===> Undeploy the service and handler from Axis client engine
===> Undeploy the service and filter in the Bus off-ramp
Results: OK
|
もしお望みなら、コンソールかログを調べることにより、Bus off-rampフィルターの実行を確認することができます。リスト21 にあるようなログ項目が見つかるはずです。
リスト21. AxisクライアントとBusサーバーの実行に関するログ項目
[<date & time >] 6fbe49b6 SystemOut U Filter decompressing with type X.
[<date & time >] 6fbe49b6 SystemOut U Filter decompressing with type Y.
|
この記事では、Web Services Busフィルターを、Bus on-ramp (クライアント) とBus off-ramp (サーバー) の両方で使用する方法を学習しました。さらに、クライアント・サイドのフィルターとサーバー・サイドのフィルターの間でコンテキストを確立する方法について調べました。それに加えて、BusフィルターとAxisハンドラーを使用して比較しました。最後に、特定の条件下で、BusクライアントとAxisサーバーをうまく組み合わせたり、その逆の組み合わせを実現したりできることを見ました。
この連載記事では、Web Services Busのいくつかの機能をデモンストレーションしました。これらの機能が組み合わさることにより、Busは、Webサービスを利用して実施される異種混合環境の動的なe-businessのための優れたフレームワークとなっています。この記事を読み終えた読者の皆さんが、Web Services Busについて独力で調べる準備が整い、個々のプロジェクトにとってそれがどのように便利なものとなり得るかを見極められるようになっていることを願います。
- この連載の最初の記事、Web Services Busを調べる 第1回 (developerWorks、2002年11月) をお読みください。
- この記事に付属のコード・パッケージをダウンロードできます。これには、ソース、WSDLファイル、バッチ・ファイル、およびサンプルに必要なその他のファイルが含まれています。コードはWSTK環境で稼動するように書かれており、他のWSTKデモと同じような構造になっています。
-
Web Services Toolkitバージョン3.3 は、Web Services Busを含む数多くのWebサービス・テクノロジーを提供します。
-
IBM WebSphere SDK for Web Services (WSDK) は、Webサービス・プロバイダーの環境を提供し、WSTKのインストールをサポートします。
-
Axis は、Webサービスのリクエスターおよびプロバイダーの環境を提供します。これは、WSTK内で使用可能です。

Greg Flurryは、幅広いe-business環境のための最先端テクノロジーの調査を目的とするIBM Software Group Emerging Technologiesのメンバーです。連絡先はflurry@us.ibm.com です。