レベル: 中級 Bilal Siddiqui, Freelance consultant
2008年 09月 30日 この簡潔な連載の第 2 回目では、構成可能なビジネス・ワークフローに関してそのロジックを BPEL によって表現する方法を Bilal Siddiqui が説明します。BPEL エンジンで BPEL アプリケーションをホストして、IoC (Inversion of Control: 制御の反転) の実装と BPEL アプリケーションを連動させる方法を学んでください。
この 2 回連載の第 1 回目では、動的ビジネス・ワークフローを分析するための 2 層構造のモデルを紹介し、IoC (Inversion of Control: 制御の反転) と WS-BPEL (Web Services Business Process Execution Language) を使ってこの 2 つの層を実装する方法を説明しました。第 2 回目となる今回の記事では、BPEL ではどのようにワークフローのビジネス・ロジックを表現するかを説明し、IoC と BPEL をデプロイしてビジネス・ワークフローの振る舞いを制御する方法を実演します。
まず始めに説明するのは、BPEL を使ってビジネス・ワークフローの動的振る舞いを表現する方法です。続いて BPEL ファイルを Apache ODE (Java™ ベースのオープンソース BPEL 実装) 上にホストする手順、タスク層の個々の Java Bean のサービスを Web サービスとして公開するために必要な手順を説明します。そして最後に、BPEL と IoC をベースにした実際に機能するサンプル・アプリケーションを紹介します。
BPEL 変数の使用
第 1 回のリスト 2 に記載した <variables> タグと <sequence> タグを思い出してください。ビジネス・ワークフローのロジックは、この 2 つの BPEL タグが連動することによって公開されます。このセクションではまず、<variables> タグの使い方について説明します。
多種類ワークフローと大量生産ワークフローがマージされた生産ワークフローの変数の定義
まず必要なのは、ワークフローの実行中に処理するビジネス・データを保持する変数を定義することです。変数は、例えば生産注文書に含まれる項目のリストを保持するために使用することができます。ここでは、多種類ワークフローと大量生産ワークフローがマージされた生産ワークフローを実装するために以下の変数を使用します。
productionOrder が保持するのは、マージされた生産ワークフローが受け取る生産注文書です。
response は、タスク層のすべてのタスクが実行された後、マージされた生産ワークフローの最終レスポンスを保持します。
listOfItems が保持するのは、生産注文書に含まれる製品のリストです。
AitemType は、生産する製品のタイプを指定します。この変数を使用して、生産する製品が多種類生産または大量生産どちらの製品であるかをチェックします。
processDetails は、多種類生産製品の場合のプロセス詳細を保持します。
workOrder が保持するのは作業指示書です。
manufacturingSchedule が保持するのは、大量生産製品の場合の製造スケジュールです。
- この他に、一時処理データを保持する変数がいくつかあります。
上記の変数の使用方法は、BPEL ファイルに定義する処理ロジックについて解説するなかで取り上げていきます。
BPEL ファイルでは、これらの変数をリスト 1 のように定義します。
リスト 1. マージされた生産ワークフローに対する変数の定義
<?xml version="1.0" encoding="UTF-8"?>
<process name="MergedProductionWorkflow">
<import />
<!-- other import tags -->
<partnerLinks />
<variables>
<variable name="productionOrder"
messageType="prod:ProductionOrderMessage"/>
<variable name="response"
messageType="prod:ProductionResponseMessage"/>
<variable name="productionItems"
messageType="prod:ProductionItemsMessage"/>
<variable name="itemType" type="xsd:string"/>
<variable name="processDetails"
messageType="prod:ProcessDetailsMessage"/>
<variable name="workOrder"
messageType="prod:WorkOrderMessage"/>
<variable name="manufacturingSchedule"
messageType="prod:ManufacturingScheduleMessage"/>
<variable name="productionItem"
messageType="prod:ProductionItemMessage"/>
<!-- A few other variables to hold temporary data -->
</variables>
<sequence />
</process>
|
リスト 1 の <variables> タグは、第 1 回のリスト 2 に記載したタグを展開したものです。この <variables> タグに、皆さんが使用する生産ワークフローのために定義する変数それぞれに対応する <variable> タグが含まれます。
それぞれの <variable> タグが定義するのは、各変数の名前とデータ型です。変数の名前は、name 属性が定義します。例えば、リスト 1 の最初の <variable> タグでは、名前が productionOrder となっています。messageType 属性は変数のデータ型を定義します。例えば productionOrder 変数のデータ型は、prod 名前空間によって定義された prod:ProductionOrderMessage です。次に、BPEL 変数のデータ型を BPEL アプリケーションとそのパートナー・サービスとの間で交換されるメッセージに対応付ける方法を説明します。
変数とパートナー・サービスとのリンク
第 1 回で、BPEL ファイルをパートナー・サービスにリンクしたことを思い出してください (「サービス・パートナーへのリンク」セクションを参照)。それぞれのパートナー・サービスがタスク層を構成する個別のタスクの機能を提供し、その機能を WSDL (Web Service Definition Language) インターフェースによって公開します (この記事のソース・コード・ダウンロードには、各パートナー・サービスのすべての WSDL ファイルが含まれます。「ダウンロード」を参照してください)。
BPEL ファイルで使用されている変数に保持されたデータは、パートナー・サービスに渡されて処理されます。例えば、BPEL エンジンは productionOrder 変数に保持された生産注文書のデータを Items2BProducedPartnerService という名前のパートナー・サービスに渡します。すると、このパートナー・サービスが生産注文書から製品のリストを抽出します。つまり、productionOrder 変数のデータ型は、Items2BProducedPartnerService への入力として定義された WSDL データ型と一致するということです。
BPEL 変数のデータ型と WSDL メッセージがどのように対応しているかわかるように、リスト 2 に Items2BProducedPartnerService の WSDL ファイルを記載します。
リスト 2. Items2BProducedPartnerService の WSDL インターフェース
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions ...>
<wsdl:import namespace="http://fictitiousManufacturingEnterprise.com/pms"
location="ProductionOrderRecivingService.wsdl" />
<wsdl:types>
<xsd:element name="productionItem">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="itemID" type="xsd:string" />
<xsd:element name="itemName" type="xsd:string" />
<xsd:element name="itemType" type="xsd:string" />
<xsd:element name="itemQuantity" type="xsd:string"/>
<xsd:element ref="processDetails" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="productionItems">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="productionItem"
minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</wsdl:types>
<wsdl:message name="ProductionOrderMessage">
<wsdl:part name="productionOrder" element="prod:productionOrder" />
</wsdl:message>
<wsdl:message name="ProductionItemsMessage">
<wsdl:part name="productionItems" element="prod:productionItems" />
</wsdl:message>
<wsdl:portType />
<wsdl:binding />
<wsdl:service />
<plnk:partnerLinkType name="Items2BProducedPartnerLinkType">
<plnk:role name="items2BProducedService"
portType="prod:Items2BProducedPortType"/>
</plnk:partnerLinkType>
</wsdl:definitions>
|
 | |
ビジネス・ワークフロー用の XML スキーマ
パートナー・サービスには、それぞれに対応する WSDL ファイルごとに XML スキーマを定義しました。このようにしたのは、ただ単に、複雑にならないようにしたいという理由からだけです。実際の作業では、ビジネス・ワークフローに合わせた独自の XML スキーマを設計してから、そのスキーマ全体をWSDL ファイルと BPEL ファイルにインポートすることができます。
|
|
上記のリスト 2 は、第 1 回のリスト 4 に記載した WSDL ファイルと同じものですが、第 1 回では詳細を省略した <wsdl:types> タグを記載し、<wsdl:message> タグも展開して記載しています。
リスト 2 の <wdl:types> タグは、この後説明する Item2BProducedPartnerService の XML スキーマを定義します。一方、リスト 2 の 2 つの <wsdl:message> タグが定義しているのは、Items2BProducedPartnerService の入力メッセージと出力メッセージです。
最初の <wsdl:message> タグは、Items2BProducedPartnerService の入力 (つまり、生産注文書) を定義します。これが、リスト 2 の最初の <wsdl:message> タグに含まれる name 属性の値 (ProductionOrderMessage) が、リスト 1 に定義された最初の変数のデータ型と一致する理由です。
2 番目の <wsdl:message> タグは Items2BProducedPartnerService の出力を定義します。この出力とはつまり、生産する製品のリストです。したがって、2 番目の <wsdl:message> タグに含まれる name 属性の値 (ProductionItemsMessage) は、リスト 1 に記載した BPEL ファイルで定義された listOfItems 変数のデータ型と一致します。
タスク層のすべての WSDL でも同じように、リスト 1 の BPEL ファイルに変数として定義されたデータ型と同じデータ型を定義します。このように、変数のデータ型によって BPEL アプリケーションとそのパートナー・サービスとの相互運用性が実現します。
マージされた生産ワークフローの XML スキーマの定義
ここで、マージされた生産ワークフローの XML スキーマを紹介します。このスキーマは、ワークフローの BPEL ファイルおよび WSDL ファイル両方で使用します。このスキーマには 5 つのデータ型 (productionOrder、productionItem、processDetails、workOrder、manufacturingSchedule) が含まれます (リスト 3 を参照)。
リスト 3. マージされた生産ワークフローで使用されるデータ型
<xsd:element name="productionOrder">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="orderID" type="xsd:string" />
<xsd:element name="orderDate" type="xsd:date" />
<xsd:element name="departmentID" type="xsd:string" />
<xsd:element name="orderItems" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="productionItem">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="itemID" type="xsd:string" />
<xsd:element name="itemName" type="xsd:string" />
<xsd:element ref="itemType" />
<xsd:element name="itemQuantity" type="xsd:string"/>
<xsd:element name="processDetails" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="itemType" type="xsd:string"/>
<xsd:element name="processDetails">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="processID" type="xsd:string" />
<xsd:element name="processType" type="xsd:string" />
<xsd:element name="processDescription" type="xsd:string" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="workOrder">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="workOrderID" type="xsd:string" />
<xsd:element name="orderData" type="xsd:string" />
<xsd:element ref="productionOrder" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="manufacturingSchedule">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="itemID" type="xsd:string" />
<xsd:element name="scheduleDate" type="xsd:string" />
<xsd:element name="itemQuantity" type="xsd:string" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
|
BPEL では XML Schema 仕様を用いて変数のデータ型を定義します。この話題についてはこの記事では説明しません (「参考文献」に XML スキーマの作成方法について説明している記事のリンクを記載しているので、そちらを参照してください。
リスト 3 では、各データ型が <xsd:element> タグの name 属性によって指定されていることに注意してください。リスト 3 にはパートナー・サービスの各種入出力メッセージが使用するデータ型がすべて記載されています。例えば、リスト 3 の最初の <xsd:element> タグに含まれる name 属性は、productionOrder となっています。これは、リスト 2 の最初の <wsdl:message> タグの子である <wsdl:part> の element 属性と一致します。
Items2BProducedPartnerService への入力メッセージは単に、リスト 3 の最初の <xsd:element> タグで定義された <productionOrder> タグをラップするだけに過ぎません。リスト 4 を見るとわかるように、このメッセージは BPEL アプリケーションが Items2BProducedPartnerService に送信する SOAP リクエスト・メッセージです。
リスト 4. Items2BProducedPartnerService に対する SOAP リクエスト・メッセージ
<?xml version='1.0' encoding='utf-8'?>
<soap:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" >
<soap:Body>
<sns:productionOrder xmlns:sns="http://fictitiousManufacturingEnterprise.com/pms">
<!-- production order data -->
</sns:productionOrder >
</soap:Body>
</soap:Envelope>
|
ビジネス・ワークフロー内でのタスク・シーケンスの定義
マージされた生産ワークフローの BPEL ファイルで使用する変数の定義は完了したので、今度は第 1 回のリスト 2 に記載した <sequence> タグを使って、マージされた生産ワークフロー内でのタスク・シーケンスを定義する方法を説明します。このシーケンスでは、リスト 1 で定義した変数を使用します。
第 1 回の「プロセス実行層の操作」セクションで紹介した 9 つのプロセス実行ステップを思い出してください。この 9 つのタスクそれぞれについて、<sequence> タグとその子タグを拡張する必要があります。
ステップ 1: 生産注文書の受信
マージされた生産ワークフローの最初のステップは、生産注文書を受信することです。受信するためには、シーケンス内に <receive> 子タグが必要となります (リスト 5 を参照)。
リスト 5. 生産注文書の受信
<process name="MergedProductionWorkflow">
<import />
<partnerLinks />
<variables />
<sequence>
<!-- Step 1: The process-execution layer receives a production order from
a client application. -->
<receive
name="receiveProductionOrder"
partnerLink="productionManagementPartnerLink"
portType="prod:ProductionManagementPortType"
operation="receiveProductionOrder"
createInstance="yes"
variable="productionOrder"/>
<!-- Other steps to process the production order -->
<!-- Response back to requesting client application-->
<reply
name="replyPurchaseOrder"
partnerLink="productionManagementPartnerLink"
portType="prod:ProductionManagementPortType"
operation="receiveProductionOrder"
variable="responseMessage" />
</sequence>
</process>
|
リスト 5 には <receive> の他に、<reply> タグも示されています。<receive> タグが、外部クライアント・アプリケーション (会社の営業部門で使用しているソフトウェア・モジュールなど) から生産注文書を受け取り、マージされた生産ワークフローのすべてのステップ (後で説明) を BPEL エンジンが実行します。そしてワークフローのステップが実行された後には、<reply> タグが外部クライアントに応答を送信します。
つまり <receive> タグと <reply> タグがワークフロー全体を囲むラッパーとなり、WSDL インターフェースを介してワークフローを外部クライアントに公開するというわけです (第 1 回の図 3 に示した、生産管理システムの WSDL ファイル全体を参照してください)。
生産注文書リクエストを受信してから、外部クライアント・アプリケーションに応答を送信するまでの間、マージされた生産ワークフローはパートナー・サービスの助けを借りて生産注文書を処理します。生産注文書の処理については、以降のセクションで説明します。
リスト 5 を見るとわかるように、<receive> タグには以下の 6 つの属性があります。
name は、実行対象のタスクまたはアクティビティー (この例の場合、receiveProductionOrder) の名前を指定します。
partnerLink は、生産注文書を受信するパートナー・サービス (第 1 回の図 3 に示した WSDL ファイル全体) へのリンクを指定します。
portType は、パートナー・サービスがリクエストをリッスンするポート・タイプを識別します。
operation は、生産注文書を受信するパートナー・サービスの操作を識別します。この例の場合、生産注文書を受信するサービスの receiveProductionOrder 操作が生産注文書を受信することになります。
createInstance は、BPEL エンジンに実行対象プロセスの新規インスタンスを作成させるかどうかを指定します。リスト 5 の createInstance の値は yes となっているので、新しい生産注文書を受信するたびに、マージされた生産ワークフローの新しいインスタンスが作成されることになります。
variable は、生産注文書を受信する変数の名前を指定します。上記の例で指定されている変数の名前は productionOrder (リスト 1 で定義されている最初の変数) です。つまり、receiveProductionOrder 操作が生産注文書を受信すると、BPEL エンジンは発注書を productionOrder 変数に保管し、ワークフローの後のアクティビティーがこの発注書を処理できるようにします。
リスト 5 の <reply> タグが持つ属性も、このタグに必要のない createInstance 属性を除き、上記 <receive> タグと同じです。
ステップ 2: 生産する製品リストの抽出
次のステップは、ステップ 1 で受信した生産注文書から、生産する製品のリストを抽出することです。マージされた生産ワークフローはこのリストを抽出するために、items2BProduced という Java Bean を使用します。この items2BProduced Bean の機能を公開するパートナー・サービスは、items2BProducedPartenerService です。Items2BProduced パートナー・サービスを呼び出す <invoke> アクティビティーは、リスト 6 のように定義します。
リスト 6. 生産注文書からの製品リストの抽出
<process name="MergedProductionWorkflow">
<import />
<partnerLinks />
<variables />
<sequence>
<!-- Step 1: The process-execution layer receives a production order from
a client application. -->
<receive />
<!-- Step 2: The process-execution layer uses the items2BProduced Java bean
to extract the list of items that need to be produced from the
production order. -->
<invoke name="getListOfProductionItems"
partnerLink="productionItemsListPartnerLink"
portType="list:ProductionItemsListPortType"
operation="getListOfProductionItems"
inputVariable="productionOrder"
outputVariable="productionItems">
</invoke>
<!-- Other steps to process the production order -->
<!-- Response back to requesting client application-->
<reply />
</sequence>
</process>
|
リスト 6 の <invoke> タグが、Items2BProducedPartnerService を呼び出します。
<invoke> タグの name、partnerLink、portType、および operation 属性はリスト 5 の <receive> タグの属性と同様なので、改めて説明することはしません。BPEL ファイル内での <invoke> タグはクライアント・サイドの Web サービス操作となり、この操作が SOAP リクエスト・メッセージを送信することによってパートナー・サービスが呼び出されます。パートナー・サービスはメッセージへの応答として、SOAP レスポンスを BPEL エンジンに送信します。
<invoke> タグの以下の 2 つの属性については、説明の必要があります。
inputVariable は、パートナー・サービスに必要な入力パラメーターを指定します。この例の場合、Items2BProducedPartnerService は生産注文書を入力として取るため、inputVariable 属性の値には productionOrder が指定されています。productionOrder は、受信アクティビティー中に生産注文書を受け取る変数と同じであることに注意してください。
outputVariable は、パートナー・サービスから受信したレスポンスをどの BPEL 変数で保持するかを指定します。この例でのレスポンスは生産する製品のリストです。そのため、リスト 6 の outputVariable 属性の値としては productionItems が指定されています。
ステップ 3: 生産する製品タイプのチェック
マージされた生産ワークフローでの 3 番目のステップは、生産する各製品のタイプ (つまり、製品が多種類カテゴリーまたは大量生産カテゴリーのどちらに属するか) をチェックすることです。これをチェックするには、リスト 7 に示すように多数のサブステップが必要になってきます。
リスト 7. 生産する製品タイプのチェック
<process name="MergedProductionWorkflow">
<import />
<partnerLinks />
<variables />
<sequence>
<!-- Step 1: The process-execution layer receives a production order from
a client application. -->
<receive />
<!-- Step 2: The process-execution layer uses the items2BProduced Java bean
to extract the list of items that need to be produced from the
production order. -->
<invoke />
<!-- Step 3: For each item in the production order, the process-execution
layer uses the typeOfItem Java bean to check whether the item belongs
to the large-variety category or the bulk-production category. -->
<!-- Sub-step 3-1: Check how many items are in the list of items to be
produced -->
<assign>
<copy>
<!-- Copy items size in a local variable named numberOfItems-->
<from>
count($productionItems.productionItems/prod:productionItem)
</from>
<to variable="numberOfItems"/>
</copy>
</assign>
<!-- Sub-step 3-2: Run a loop to check each item in the list-->
<while>
<condition>$numberOfItems > 0</condition>
<scope>
<sequence>
<!-- Sub-step 3-3: Copy an item from the list to a local variable-->
<assign>
<copy>
<from>$productionItems.productionItems[$numberOfItems]</from>
<to variable="productionItem" part="productionItem"/>
</copy>
</assign>
<!-- Sub-step 3-4: Invoke typeOfItemPartner service-->
<invoke name="getItemType"
partnerLink="typeOfItemPartnerLink"
portType="type:ProductionItemTypePortType"
operation="getTypeOfProductionItem"
inputVariable="productionItem"
outputVariable="itemType">
</invoke>
</sequence>
</scope>
</condition>
</while>
<!-- Response back to requesting client application-->
<reply />
</sequence>
</process>
|
リスト 7 に示されているように、最初のサブステップでは、生産する製品のリストに含まれる項目数をチェックします。リスト 7 でこの役目を引き受けているのは <assign> タグで、その手段は値を変数に割り当てるというものです。リスト 7 のサブステップ 3-1 では、<assign> タグの子タグ <copy> が値を変数にコピーします。ご覧のように、<copy> タグにはさらに <from> と <to> という 2 つの子タグがあり、それぞれが値、変数を定義します。<from> タグでは XPath 式を使用して、変数にコピーする値を指定します。この XPath 式は、生産する製品リストに含まれる項目数を評価するだけに過ぎません (XPath の詳細については、「参考文献」に記載したリンクを参照してください)。
<copy> タグのもう 1 つの子タグ、<to> は単に値のコピー先とする変数 (numberOfItems) を定義します。サブステップ 3-1 の後、リスト内の項目数が numberOfItems 変数にコピーされます。次のサブステップ 3-2 では、リスト内の項目ごとに while ループを実行します。ループを実行するのは、<while> タグとその子タグである <condition> です。ここで、リスト 7 のサブステップ 3-3 を見てください。これはもう 1 つのコピー操作で、製品リストの個別の項目に適用されます。その目的は、リスト項目のローカル・コピーを作成することです。
最後のサブステップ 3-4 では、typeOfItemPartnerService を呼び出し、パートナー・サービスに項目のローカル・コピーを渡します。そしてこの typeOfItemPartnerService パートナー・サービスが、製品のタイプ (多種類または大量生産) を返すというわけです。
ステップ 4 から 9: 多種類製品と大量生産製品の処理
マージされた生産ワークフローの残りの処理ステップ (第 1 回の「プロセス実行層の操作」セクションで説明したステップ 4 からステップ 9) は、リスト 8 のとおりです。
リスト 8. 多種類製品と大量生産製品の処理
<process name="MergedProductionWorkflow">
<import />
<partnerLinks />
<variables />
<sequence>
<!-- Steps 1, 2 and 3-1 go here. -->
<!-- Sub-step 3-2: Run a loop to check each item in the list-->
<while> <condition /> <scope /> <sequence />
<!-- Sub-steps 3-3 and 3-4 go here-->
<!-- Steps 4 to 9: Rest of the workflow steps. -->
<if>
<condition>'LARGE_VARIETY' = $typeOfItem </condition>
<flow>
<invoke name="fetchProcessDetails"
partnerLink="processDetailsPartnerLink"
portType="prod:ProcessDetailsPortType"
operation="getProcessDetailsOfLargeVarietyItem"
inputVariable="productionItem"
outputVariable="processDetails">
</invoke>
<invoke name="issueLargeVarietyWO"
partnerLink="largeVarietyWOPartnerLink"
portType="prod:LargeVarietyWOPortType"
operation="issueLargeVarietyWO"
inputVariable="largeVarietyWO"
outputVariable="workOrder">
</invoke>
</flow>
</if>
<if>
<condition>'BULK_PRODUCTION' = $typeOfItem </condition>
<flow>
<invoke name="checkManufacturingSchedule"
partnerLink="manufacturingSchedulePartnerLink"
portType="prod:ManufacturingSchedulePortType"
operation="getManufacturingSchedule"
inputVariable="itemID"
outputVariable="manufacturingSchedule">
</invoke>
<assign>
<copy>
<from>
$productionItem.productionItem
/prod:productionItem/prod:itemQuantity
</from>
<to variable="itemQuantity" />
</copy>
</assign>
<invoke name="checkAdditionalProductionRequirement"
partnerLink="additionalProductionRequirementPartnerLink"
portType="prod:AdditionalProductionRequirementPortType"
operation="checkAdditionalProductionRequirement"
inputVariable="bulkProdRequirement"
outputVariable="itemQuantity2Produce">
</invoke>
<assign>
<copy>
<from variable="itemQuantity2Produce"
part="quantity2Produce"/>
<to variable="qty2Produce"/>
</copy>
</assign>
<if>
<condition> $qty2Produce > 0 </condition>
<invoke name="reserveProduction"
partnerLink="productionReservationPartnerLink"
portType="prod:ProductionReservationPortType"
operation="reserveProductionInCurrentMS"
inputVariable="qty2Reserve"
outputVariable="reservationResponse">
</invoke>
</if>
<invoke name="issueBulkProductionWO"
partnerLink="bulkProductionWOPartnerLink"
portType="prod:BulkProductionWOPortType"
operation="issueWorkOrderForBulkProductionItem"
inputVariable="itemID"
outputVariable="reservationAck">
</invoke>
</flow>
</if>
</sequence> </scope> </condition> </while>
<!-- Response back to requesting client application-->
<reply />
</sequence>
</process>
|
リスト 8 を見るとわかるように、生産する製品をそのタイプに応じて処理するには、処理の前に条件判断を行う <if> タグが必要なだけです。
マージされた生産ワークフローの BPEL のデプロイメント
マージされた生産ワークフローのビジネス・ロジックを BPEL を使って表現する方法がわかったところで、次は BPEL ファイルを Apache ODE BPEL エンジンにデプロイする作業に取り掛かります。
ODE には、BPEL アプリケーションのデプロイメント記述子を作成する必要があります。このデプロイメント記述子は、リスト 9 に記載する XML ファイルです。
リスト 9. マージされた生産ワークフローのパートナー・サービス用デプロイメント記述子
<deploy xmlns="http://www.apache.org/ode/schemas/dd/2007/03"
xmlns:prod="http:// http://fictitiousManufacturingEnterprise.com/bpel"
xmlns:sns="http://fictitiousManufacturingEnterprise.com/pms">
<process name="prod:MergedProductionOrder">
<provide partnerLink="productionOrderReceivingPartnerLink">
<service name="sns:productionOrderReceivingService"
port="productionOrderReceivingPort"/>
</provide>
<invoke partnerLink="items2BProducedPartnerLink">
<service name="sns:items2BProducedService" port="items2BProducedPort"/>
</invoke>
<invoke partnerLink="typeOfItemPartnerLink">
<service name="sns:typeOfItemService" port="typeOfItemPort"/>
</invoke>
<invoke partnerLink="processDetailsPartnerLink">
<service name="sns:processDetailsService" port="processDetailsPort"/>
</invoke>
<!-- other services invoked by merged production workflow -->
</process>
</deploy>
|
リスト 9 には <deploy> というルート・タグがあります。このルート・タグの唯一の子タグは <process> で、この <process> タグにはさらに多数の子タグが含まれています。<process> タグの最初の子は <provide> です。
<provide> タグは、マージされた生産ワークフローが提供するサービスを記述します。このワークフローが提供するサービスは 1 つしかありません。それはつまり、生産注文書を受け取って処理することです。したがって、リスト 9 には 1 つの <provide> タグしか含まれないことになります。
<provide> タグの partnerLink 属性は、生産注文書を受け取るパートナー・サービスを指定します。そして <provide> タグの子、<service> がパートナー・サービスの名前とポートを指定します。
続いてリスト 9 にはさまざまな <invoke> タグがあることに注目してください。このそれぞれが、マージされた生産ワークフローが呼び出すパートナー・サービスを記述します。記述方法として <invoke> タグが指定しているのは、パートナー・サービスのパートナー・リンク、名前、ポートです。
マージされた生産ワークフローの BPEL ファイルとデプロイメント記述子は、この記事のソース・コードに含まれています (「ダウンロード」を参照)。
マージされた生産ワークフローのタスク層のホスト
タスク層の 8 つの Java Bean を Spring の IoC Bean として構成する作業は、第 1 回の「タスク層の IoC 構成」セクションで完了しています。今回必要となるのは、IoC Bean がそれぞれのサービスを Web サービス・インターフェースによって公開するように Spring を構成する作業です。
Apache ODE は人気の高いもう 1 つのオープンソース・プロジェクト、Apache Axis2 をベースにビルドされています。Axis2 では、Java オブジェクト (この例での IoC Bean など) を XML および SOAP ベースの Web サービスとしてホストすることが可能です。ODE は BPEL に必要な機能を実装し、Web サービスに関連する下位レベルのすべての機能 (SOAP メッセージの作成、送信、受信、処理など) に対して Axis2 を使用します。つまり、ここで必要となる作業は、IoC Bean が Axis2 と連動するように構成することだけです。そうすれば、ODE が自動的に IoC Bean を使用して起動するようになります。
Axis2 による IoC Bean の構成
IoC Bean の機能を Axis2 Web サービスとして公開するには、以下の作業が必要です。
- Axis2 フレームワークと通信可能なラッパー・クラスで IoC Bean をラップします。
- Spring 構成ファイルで Bean 定義を拡張し、上記のラッパー・クラスを反映させます。
Axis2 に対応した IoC Bean のラッピング
Axis2 が実装する XML 処理モデルは、JSR (Java Specification Request) 173 に準拠した AXIOM (Axis Object Model) です。
 | |
AXIOM の使用について
必ずしも Axis2 の AXIOM アーキテクチャーを使用しなければならないということはありません。SOAP レスポンスの処理には、Axis2 の従来の DOM アーキテクチャーを使用することもできます。ただし、AXIOM のほうが遥かに優れた柔軟性を提供するため、BPEL エンジンからパートナー・サービスに送信された SOAP リクエストを処理する際に役立ちます。そのため、この記事では AXIOM を使用することにしています。
|
|
Axis2 の AXIOM アーキテクチャーでは、あらゆる Web サービス操作が、OMElement という AXIOM クラスのインスタンスを入力として受け取り、これと同じクラスの別のオブジェクトを出力として返さなければなりません。したがって、Web サービスのシグニチャーは以下のようになります。
OMElement myWebServiceOperation (OMElement input2MyWebServiceOperation){ }
Axis2 はその OMElement クラスに、XML データを受け取り、作成し、処理するための機能を実装します。
IoC Bean をラップするメソッドは、Axis2 の AXIOM アーキテクチャーに準拠したシグニチャーを持っていなければなりません。第 1 回の「タスク層の IoC 構成」セクションで構成した items2BProduced IoC Bean (タスク層の一部) をラップする AxisAwareItems2BProduced というクラスは、リスト 10 のようになります。
リスト 10. Items2BProduced Web サービスの Axis 対応ラッパー
package sample.wf.;
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.om.OMText;
public class AxisAwareItems2BProduced {
private Items2BProduced items2BProduced= null;
public void setItems2BProduced(Items2BProduced items2BProduced) {
this.items2BProduced = items2BProduced;
}
//The getListOfItems operation of Items2BProduced web service
public OMElement getListOfItems(OMElement productionOrderOMElement) {
//Step 1: Getting production order request data from OMElement object
OMElement productionOrderRequest =
productionOrderOM.getFirstElement();
//Step 2: Invoking the IoC bean
OMElement itemsList =
factory.createOMElement(
this.items2BProduced.getListOfItems(productionOrderRequest));
//Step 3: Create an empty OMElement response object using a factory class
OMFactory Factory=
OMAbstractFactory.getOMFactory();
OMNamespace items2ProduceNs= factory.createOMNamespace(
"http:// http://fictitiousManufacturingEnterprise.com/items2BProduced",
"productionOrder");
OMElement responseWrapper =
factory.createOMElement("itemsList", items2ProduceNs);
//Step 4: Populate response object with response data
responseWrapper.addChild(itemsList);
//Step 5: Return response object
return responseWrapper;
}
}
|
Items2BProducedPartnerService がリクエストを受信するたびに、Axis2 は AxisAwareItems2BProduced クラスの getListOfItems() メソッドを呼び出し、それによってこのメソッドが Items2BProduced IoC Bean を呼び出します。Axis2 フレームワークはリクエストのデータを OMElement オブジェクトにラップし、この OMElement オブジェクトを入力パラメーターとして getListOfItems() メソッドに渡します。
リスト 10 に示されているように、getListOfItems() メソッドで OMElement オブジェクトを処理する際には以下の 5 つのステップからなる処理ストラテジーを使用します。
OMElement オブジェクトからリクエスト・データを抽出します。
items2BProduced IoC Bean の getListOfItems() メソッドを呼び出し、このメソッド呼び出しと併せてステップ 1 で抽出したリクエスト・データを渡します。
- ファクトリー・クラスを使用して空の
OMElement オブジェクトを作成します。
- ステップ 3 で作成した空の
OMElement レスポンス・オブジェクトに、ステップ 2 で IoC Bean から受信したデータを取り込みます。
OMElement レスポンス・オブジェクトを BPEL エンジンに返します。
この記事のソース・コードに含まれるすべての IoC Bean には、これと同じようなラッパー・クラスを提供しました。次のセクションでは、これらのラッパー・クラスを反映するように Spring 構成で Bean 定義を拡張する方法を説明します。
Axis 対応ラッパー・クラスに合わせた Spring 構成の拡張
これから、上記で作成したラッパー・クラスに合わせて IoC Bean 定義を更新します。更新するのは簡単なことで、Spring 構成ファイル内のラッパー・クラスごとに <bean> タグが必要なだけです (リスト 11 を参照)。
リスト 11. Spring の XML 構成ファイルでの Axis 対応 Bean の構成
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" ....>
<!-- Axis-aware wrapper for items2BProduced bean -->
<bean id="axisAwareItems2BProduced"
class="sample.wf.Axis.AxisAwareItems2BProduced" >
<property name="items2BProduced" ref="items2BProduced"/>
</bean>
<bean id="items2BProduced" class="sample.wf.Items2BProduced" />
<bean id="typeOfItem" class="sample.wf.TypeOfItem" />
<!-- Other beans of task layer -->
</beans>
|
リスト 11 を見るとわかるように、ラッパー Bean (axisAwareItems2BProduced) の定義は items2BProduced というプロパティーを使用して、対応する IoC Bean (items2BProduced) を参照します。
最後の仕上げ
いよいよマージされた生産ワークフローを試す段階です。この記事のソース・コードには、ワークフローに必要なすべてのものが含まれています (「ダウンロード」を参照)。以下のステップに従って、ワークフローを試すための準備を整えてください。
- パートナー・サービスを Axis2 インストール・ディレクトリーにコピーします。
- すべてのパートナー・サービスの .aar ファイルを Axis2 インストール・ディレクトリーの Web-INF/services フォルダーにコピーします。
- .jar ファイル (タスク層の IoC Bean が含まれているファイル) を Axis2 インストール・ディレクトリーの Web-INF/lib フォルダーにコピーします。
各パートナー・サービスの .aar ファイルと .jar ファイルは、ソース・コードの PartnerServices フォルダーに置かれています。
- ソース・コードには、IoC Bean 構成が含まれる applicationContext.xml ファイルがあります。このファイルを Axis2 インストール・ディレクトリーの Web-INF フォルダーのルートにコピーしてください。
- spring.jar ファイルをダウンロードして (「参考文献」を参照)、Axis2 インストール・ディレクトリーの Web-INF/lib フォルダーにコピーします。
- BPEL アプリケーションを ODE にコピーするために、ソース・コード・ダウンロードの MergedProductionWorkflow フォルダーを ODE インストール・ディレクトリーの Web-INF/processes フォルダーにコピーします。
Tomcat を起動して、ソース・コード・ダウンロードの BPELClient フォルダーに置かれた BPELClient.bat ファイルを実行します。BPELClient.bat はマージされたワークフロー・アプリケーションに生産注文書を送信し、ワークフロー・シーケンスが開始されます。すると、Axis2 と ODE 両方のコンソール出力にシーケンスのすべてのステップが表示されるはずです。また、BPEL クライアントにはマージされた生産ワークフローから受信したレスポンスが表示されます。
まとめ
この連載では、動的ビジネス・ワークフローを 2 層構造モデルで分析する方法を説明しました。ワークフローを細分化したタスクに分割し、それぞれのタスクを IoC Bean として実装および構成する方法、そしてワークフローのビジネス・ロジックを BPEL で表現する方法を学んだ後、最後に完成したコードを組み合わせて 1 つの生産管理ワークフロー・アプリケーションを構築しました。
ダウンロード | 内容 | ファイル名 | サイズ | ダウンロード形式 |
|---|
| Sample code for this article | j-bpelioc2.zip | 56KB | HTTP |
|---|
参考文献 学ぶために
製品や技術を入手するために
- ODE および Axis2: この 2 つの Apache フレームワークをダウンロードしてください。
- spring.jar: この最新の Spring リリースをダウンロードしてください。
議論するために
著者について  | |  | Bilal Siddiqui は、電気工学エンジニア、XML コンサルタント、そして e-business の簡易化を専門とする会社、WaxSys の共同設立者でもあります。1995年に電子工学技術の学位で University of Engineering and Technology, Lahore を卒業した後、産業用制御システムを対象としたソフトウェア・ソリューションの設計を始めました。その後、XML に転向し、C++ のプログラミング経験を生かして Web ベースや Wap ベースの XML 処理ツール、サーバー側の構文解析ソリューション、そしてサービス・アプリケーションを作成しました。技術の熱烈な支持者である彼が書いた技術書は、頻繁に発行されています。 |
記事の評価
|