invoke を使って別 BPEL の同期実行を実施する BPEL プロセスの実行 - Apache ODE を使って

以下の BPMN で表されるようなビジネスプロセスを BPEL で実現してみた。(今回は「レンタカーを予約」のみを別の BPEL として定義した)

  • 「予約」処理の概要
    • 「ホテルを予約する」と「レンタカーを予約する」を並列実行
    • 「レンタカーを予約する」は「レンタカーを予約するか」の条件が「はい」の場合のみ実施

開発・実行環境は前回 id:fits:20080812 と同様に以下のような環境を使用。

なお、今回参考にした書籍は以下。

詳説 ビジネスプロセスモデリング ―SOAベストプラクティス (THEORY/IN/PRACTICE)

詳説 ビジネスプロセスモデリング ―SOAベストプラクティス (THEORY/IN/PRACTICE)

「レンタカー予約」BPELプロセスの作成

まず、「レンタカーを予約する」に該当する BPEL プロセスを以下のように作成した。

処理の実装は手抜きして、顧客名の文字列長の先頭に 'resid:' を付けた文字列を「予約ID(reservationId)」として出力するように実装。

なお、createOutput 処理の最初に output 変数の雛形を作成している。

RentalCarReservation.bpel ファイル
<?xml version="1.0" encoding="UTF-8"?>
<bpws:process exitOnStandardFault="yes" name="RentalCarReservation"
    suppressJoinFailure="yes" targetNamespace="http://fits/reservation"
    xmlns:bpws="http://docs.oasis-open.org/wsbpel/2.0/process/executable" xmlns:tns="http://fits/reservation">
    <bpws:import importType="http://schemas.xmlsoap.org/wsdl/"
        location="RentalCarReservation.wsdl" namespace="http://fits/reservation"/>
    <bpws:partnerLinks>
        <bpws:partnerLink myRole="RentalCarReservationProvider"
            name="client" partnerLinkType="tns:RentalCarReservation"/>
    </bpws:partnerLinks>
    <bpws:variables>
        <bpws:variable
            messageType="tns:RentalCarReservationRequestMessage" name="input"/>
        <bpws:variable
            messageType="tns:RentalCarReservationResponseMessage" name="output"/>
    </bpws:variables>
    <bpws:sequence name="main">
        <bpws:receive createInstance="yes" name="receiveInput"
            operation="reserve" partnerLink="client"
            portType="tns:RentalCarReservation" variable="input"/>
        <bpws:assign name="createOutput" validate="no">
            <!-- output 変数の雛形作成 -->
            <bpws:copy>
                <bpws:from>
                    <bpws:literal>
                        <RentalCarReservationResponse xmlns="http://fits/reservation">
                            <reservationId/>
                            <dateFrom/>
                            <dateTo/>
                        </RentalCarReservationResponse>
                    </bpws:literal>
                </bpws:from>
                <bpws:to part="payload" variable="output"/>
            </bpws:copy>
            <bpws:copy>
                <bpws:from part="payload" variable="input">
                    <bpws:query queryLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"><![CDATA[/tns:dateFrom]]></bpws:query>
                </bpws:from>
                <bpws:to part="payload" variable="output">
                    <bpws:query queryLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"><![CDATA[/tns:dateFrom]]></bpws:query>
                </bpws:to>
            </bpws:copy>
            <bpws:copy>
                <bpws:from part="payload" variable="input">
                    <bpws:query queryLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"><![CDATA[/tns:dateTo]]></bpws:query>
                </bpws:from>
                <bpws:to part="payload" variable="output">
                    <bpws:query queryLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"><![CDATA[/tns:dateTo]]></bpws:query>
                </bpws:to>
            </bpws:copy>
            <!-- resid: に customerName の文字列長を付けたものを
                 reservation Id に設定 -->
            <bpws:copy>
                <bpws:from><![CDATA[concat('resid:', string-length($input.payload/tns:customerName))]]></bpws:from>
                <bpws:to part="payload" variable="output">
                    <bpws:query queryLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"><![CDATA[/tns:reservationId]]></bpws:query>
                </bpws:to>
            </bpws:copy>
        </bpws:assign>
        <bpws:reply name="replyOutput" operation="reserve"
            partnerLink="client" portType="tns:RentalCarReservation" variable="output"/>
    </bpws:sequence>
</bpws:process>
RentalCarReservation.wsdl ファイル
<?xml version="1.0"?>
<definitions name="RentalCarReservation"
        targetNamespace="http://fits/reservation"
        xmlns:tns="http://fits/reservation"
        xmlns:plnk="http://docs.oasis-open.org/wsbpel/2.0/plnktype"
        xmlns="http://schemas.xmlsoap.org/wsdl/"
        xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <types>
        <schema attributeFormDefault="unqualified" elementFormDefault="qualified" 
                targetNamespace="http://fits/reservation" 
                xmlns="http://www.w3.org/2001/XMLSchema">

            <element name="RentalCarReservationRequest">
                <complexType>
                    <sequence>
                        <element name="customerName" type="xsd:string"></element>
                        <element name="dateFrom" type="xsd:date" />
                        <element name="dateTo" type="xsd:date"></element>
                    </sequence>
                </complexType>
            </element>

            <element name="RentalCarReservationResponse">
                <complexType>
                    <sequence>
                        <element name="reservationId" type="xsd:string" />
                        <element name="dateFrom" type="xsd:date"></element>
                        <element name="dateTo" type="xsd:date"></element>
                    </sequence>
                </complexType>
            </element>
        </schema>
    </types>
    <message name="RentalCarReservationRequestMessage">
        <part name="payload" element="tns:RentalCarReservationRequest"/>
    </message>
    <message name="RentalCarReservationResponseMessage">
        <part name="payload" element="tns:RentalCarReservationResponse"/>
    </message>
    <portType name="RentalCarReservation">
        <operation name="reserve">
            <input  message="tns:RentalCarReservationRequestMessage" />
            <output message="tns:RentalCarReservationResponseMessage"/>
        </operation>
    </portType>
    <plnk:partnerLinkType name="RentalCarReservation">
        <plnk:role name="RentalCarReservationProvider" portType="tns:RentalCarReservation"/>
    </plnk:partnerLinkType>
    <binding name="RentalCarReservation"
        type="tns:RentalCarReservation">
        <soap:binding style="document"
            transport="http://schemas.xmlsoap.org/soap/http">
        </soap:binding>
        <operation name="reserve">
            <soap:operation
                soapAction="http://fits/reservation/reserve">
            </soap:operation>
            <input>
                <soap:body use="literal"></soap:body>
            </input>
            <output>
                <soap:body use="literal"></soap:body>
            </output>
        </operation>
    </binding>
    <service name="RentalCarReservationService">
        <port name="RentalCarReservationPort" binding="tns:RentalCarReservation">
            <soap:address location="http://localhost:8080/ode/processes/rentalCarReservation" />
        </port>
    </service>
</definitions>

「予約」BPELプロセスの作成

メインの BPEL を以下のように作成。「ホテル予約」の処理は手抜きして、このプロセス内で処理を実装している。(固定値を output 変数の hotelReservation に設定する)

主な特徴は以下の通り。

  • flow を使ってホテル予約(ReserveHotel)とレンタカー予約(ReserveRentalCar)の並列処理を実装
  • if を使って「レンタカー予約」の実行を条件分岐(doCarRental の値が 0 より大きい場合にのみ「レンタカー予約」を実行)
  • BPEL で定義した「レンタカー予約」は rentalCar という名称で Partner Link 定義
  • InvokeRentalCar で「レンタカー予約」の処理を実行(別 BPEL の処理を実行)
  • rentalCar の Partner Link の定義は partnerRole に RentalCarReservationProvider を、myRole は None に設定(同期呼出の場合、MyRole の設定は不要みたい)
  • 「レンタカー予約」呼び出しの入力値と出力値を rentalCarInput と rentalCarOutput として Variable 定義

なお、「レンタカー予約」の実行有無を指定する doCarRental の型として xsd:boolean では無く xsd:int を使っているが、これは xsd:boolean での条件判定式が意図したように動作しなかったため。
ちなみに、doCarRental の型が xsd:boolean の場合に条件式 "$input.payload/tns:doCarRental = true()" を に設定すると、常に(false の場合でも) true として処理されてしまった。


ちなみに、BPEL Visual Designer 上での主な操作は以下のようになる。

  • Partner Link(rentalCar)の追加
    1. Partner Link の追加ボタン押下(Partner Links の「+」)
    2. Partner Link Type の「Browse」ボタン押下
    3. 「Add WSDL」ボタン押下
    4. WSDL ファイル(RentalCarReservation.wsdl)の選択
    5. RentalCarReservation 選択(丸に I のアイコンの方)
  • Variables(rentalCarInput, rentalCarOutput)の追加
    1. Variable の追加ボタン押下
    2. Filter の「Messages」にチェック
    3. 該当する Message(RentalCarReservationRequestMessage, RentalCarReservationResponseMessage)を選択
TravelReservation.bpel ファイル
<?xml version="1.0" encoding="UTF-8"?>
<bpws:process exitOnStandardFault="yes" name="TravelReservation"
    suppressJoinFailure="yes" targetNamespace="http://fits/reservation"
    xmlns:bpws="http://docs.oasis-open.org/wsbpel/2.0/process/executable" xmlns:tns="http://fits/reservation">
    <bpws:import importType="http://schemas.xmlsoap.org/wsdl/"
        location="TravelReservation.wsdl" namespace="http://fits/reservation"/>
    <bpws:import importType="http://schemas.xmlsoap.org/wsdl/"
        location="RentalCarReservation.wsdl" namespace="http://fits/reservation"/>
    <bpws:partnerLinks>
        <bpws:partnerLink myRole="TravelReservationProvider"
            name="client" partnerLinkType="tns:TravelReservation"/>
        <bpws:partnerLink name="rentalCar"
            partnerLinkType="tns:RentalCarReservation" partnerRole="RentalCarReservationProvider"/>
    </bpws:partnerLinks>
    <bpws:variables>
        <bpws:variable messageType="tns:TravelReservationRequestMessage" name="input"/>
        <bpws:variable
            messageType="tns:TravelReservationResponseMessage" name="output"/>
        <bpws:variable
            messageType="tns:RentalCarReservationRequestMessage" name="rentalCarInput"/>
        <bpws:variable
            messageType="tns:RentalCarReservationResponseMessage" name="rentalCarOutput"/>
    </bpws:variables>
    <bpws:sequence name="main">
        <bpws:receive createInstance="yes" name="receiveInput"
            operation="reserve" partnerLink="client"
            portType="tns:TravelReservation" variable="input"/>
        <bpws:assign name="initOutput" validate="no">
            <bpws:copy>
                <bpws:from>
                    <bpws:literal>
                        <TravelReservationResponse xmlns="http://fits/reservation">
                            <hotelReservation/>
                            <rentalCarReservation/>
                        </TravelReservationResponse>
                    </bpws:literal>
                </bpws:from>
                <bpws:to part="payload" variable="output"/>
            </bpws:copy>
        </bpws:assign>
        <bpws:flow name="Flow">
            <bpws:sequence name="ReserveHotel">
                <bpws:assign name="createResult" validate="no">
                    <bpws:copy>
                        <bpws:from>
                            <bpws:literal>id:1001</bpws:literal>
                        </bpws:from>
                        <bpws:to part="payload" variable="output">
                            <bpws:query queryLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"><![CDATA[/tns:hotelReservation]]></bpws:query>
                        </bpws:to>
                    </bpws:copy>
                </bpws:assign>
            </bpws:sequence>
            <bpws:sequence name="ReserveRentalCar">
                <bpws:if name="If">
                    <bpws:sequence name="Sequence">
                        <bpws:assign name="createInput" validate="no">
                            <bpws:copy>
                                <bpws:from>
                                    <bpws:literal>
                                    <RentalCarReservationRequest xmlns="http://fits/reservation">
                                    <customerName/>
                                    <dateFrom/>
                                    <dateTo/>
                                    </RentalCarReservationRequest>
                                    </bpws:literal>
                                </bpws:from>
                                <bpws:to part="payload" variable="rentalCarInput"/>
                            </bpws:copy>
                            <bpws:copy>
                                <bpws:from part="payload" variable="input">
                                    <bpws:query queryLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"><![CDATA[/tns:customerName]]></bpws:query>
                                </bpws:from>
                                <bpws:to part="payload" variable="rentalCarInput">
                                    <bpws:query queryLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"><![CDATA[/tns:customerName]]></bpws:query>
                                </bpws:to>
                            </bpws:copy>
                            <bpws:copy>
                                <bpws:from part="payload" variable="input">
                                    <bpws:query queryLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"><![CDATA[/tns:dateFrom]]></bpws:query>
                                </bpws:from>
                                <bpws:to part="payload" variable="rentalCarInput">
                                    <bpws:query queryLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"><![CDATA[/tns:dateFrom]]></bpws:query>
                                </bpws:to>
                            </bpws:copy>
                            <bpws:copy>
                                <bpws:from part="payload" variable="input">
                                    <bpws:query queryLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"><![CDATA[/tns:dateTo]]></bpws:query>
                                </bpws:from>
                                <bpws:to part="payload" variable="rentalCarInput">
                                    <bpws:query queryLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"><![CDATA[/tns:dateTo]]></bpws:query>
                                </bpws:to>
                            </bpws:copy>
                        </bpws:assign>
                        <bpws:invoke inputVariable="rentalCarInput"
                            name="InvokeRentalCar" operation="reserve"
                            outputVariable="rentalCarOutput"
                            partnerLink="rentalCar" portType="tns:RentalCarReservation"/>
                        <bpws:assign name="createResult" validate="no">
                            <bpws:copy>
                                <bpws:from><![CDATA[$rentalCarOutput.payload/tns:reservationId]]></bpws:from>
                                <bpws:to part="payload" variable="output">
                                    <bpws:query queryLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"><![CDATA[/tns:rentalCarReservation]]></bpws:query>
                                </bpws:to>
                            </bpws:copy>
                        </bpws:assign>
                    </bpws:sequence>
                    <bpws:condition><![CDATA[$input.payload/tns:doCarRental > 0]]></bpws:condition>
                </bpws:if>
            </bpws:sequence>
        </bpws:flow>
        <bpws:reply name="replyOutput" operation="reserve"
            partnerLink="client" portType="tns:TravelReservation" variable="output"/>
    </bpws:sequence>
</bpws:process>
TravelReservation.wsdl ファイル
<?xml version="1.0"?>
<definitions name="TravelReservation"
        targetNamespace="http://fits/reservation"
        xmlns:tns="http://fits/reservation"
        xmlns:plnk="http://docs.oasis-open.org/wsbpel/2.0/plnktype"
        xmlns="http://schemas.xmlsoap.org/wsdl/"
        xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <types>
        <schema attributeFormDefault="unqualified" elementFormDefault="qualified" 
                targetNamespace="http://fits/reservation" 
                xmlns="http://www.w3.org/2001/XMLSchema">

            <element name="TravelReservationRequest">
                <complexType>
                    <sequence>
                        <element name="customerName" type="xsd:string" />
                        <element name="dateFrom" type="xsd:date" />
                        <element name="dateTo" type="xsd:date" />
                        <element name="doCarRental" type="xsd:int" />
                    </sequence>
                </complexType>
            </element>

            <element name="TravelReservationResponse">
                <complexType>
                    <sequence>
                        <element name="hotelReservation" type="xsd:string" />
                        <element name="rentalCarReservation" type="xsd:string" />
                    </sequence>
                </complexType>
            </element>
        </schema>
    </types>
    <message name="TravelReservationRequestMessage">
        <part name="payload" element="tns:TravelReservationRequest"/>
    </message>
    <message name="TravelReservationResponseMessage">
        <part name="payload" element="tns:TravelReservationResponse"/>
    </message>
    <portType name="TravelReservation">
        <operation name="reserve">
            <input  message="tns:TravelReservationRequestMessage" />
            <output message="tns:TravelReservationResponseMessage"/>
        </operation>
    </portType>
    <plnk:partnerLinkType name="TravelReservation">
        <plnk:role name="TravelReservationProvider" portType="tns:TravelReservation"/>
    </plnk:partnerLinkType>
    <binding name="TravelReservationBinding"
        type="tns:TravelReservation">
        <soap:binding style="document"
            transport="http://schemas.xmlsoap.org/soap/http">
        </soap:binding>
        <operation name="reserve">
            <soap:operation
                soapAction="http://fits/reservation/reserve">
            </soap:operation>
            <input>
                <soap:body use="literal"></soap:body>
            </input>
            <output>
                <soap:body use="literal"></soap:body>
            </output>
        </operation>
    </binding>
    <service name="TravelReservationService">
        <port name="TravelReservationPort" binding="tns:TravelReservationBinding">
            <soap:address location="http://localhost:8080/ode/processes/travelReservation" />
        </port>
    </service>
</definitions>

デプロイ

以下のような Apache ODE へのデプロイファイルを作成し、前項までに作成した .bpel・.wsdl ファイルと共に任意のディレクトリに配置して Apache ODE にデプロイ。(ディレクトリごと webapps/ode/WEB-INF/processes/ に配置)

deploy.xml ファイル
<?xml version="1.0" encoding="UTF-8"?>
<deploy xmlns="http://www.apache.org/ode/schemas/dd/2007/03"
    xmlns:pns="http://fits/reservation">
    <!-- レンタカー予約サービスの定義 -->
    <process name="pns:RentalCarReservation">
        <active>true</active>
        <provide partnerLink="client">
            <service name="pns:RentalCarReservationService" port="RentalCarReservationPort"/>
        </provide>
    </process>
    <!-- 予約サービスの定義 -->
    <process name="pns:TravelReservation">
        <active>true</active>
        <provide partnerLink="client">
            <service name="pns:TravelReservationService" port="TravelReservationPort"/>
        </provide>
        <invoke partnerLink="rentalCar">
            <service name="pns:RentalCarReservationService" port="RentalCarReservationPort"/>
        </invoke>
    </process>
</deploy>

動作確認

まず、以下の URL にアクセスして 「レンタカー予約(RentalCarReservation)」が正常に実行できることを確認。

「レンタカー予約」実行 URL
http://localhost:8080/ode/processes/rentalCarReservation/reserve?customerName=abcd&dateFrom=2008-08-18&dateTo=2008-08-19
「レンタカー予約」実行結果
<RentalCarReservationResponse xmlns="http://fits/reservation">
    <reservationId>resid:4</reservationId>
    <dateFrom>2008-08-18</dateFrom>
    <dateTo>2008-08-19</dateTo>
</RentalCarReservationResponse>

次に、以下の URL にアクセスして「予約(TravelReservation)」が正常に実行され、「レンタカー予約(RentalCarReservation)」を呼び出している事を確認。

「レンタカー予約」を実行する「予約」実行 URL
http://localhost:8080/ode/processes/travelReservation/reserve?customerName=abcd&dateFrom=2008-08-18&dateTo=2008-08-19&doCarRental=1
「レンタカー予約」を実行する「予約」実行結果
<TravelReservationResponse xmlns="http://fits/reservation">
    <hotelReservation>id:1001</hotelReservation>
    <rentalCarReservation>resid:4</rentalCarReservation>
</TravelReservationResponse>

最後に、以下の URL にアクセスして doCarRental が 0以下の場合に、「レンタカー予約(RentalCarReservation)」を呼び出さない事を確認。

「レンタカー予約」を実行しない「予約」実行 URL
http://localhost:8080/ode/processes/travelReservation/reserve?customerName=abcd&dateFrom=2008-08-18&dateTo=2008-08-19&doCarRental=0
「レンタカー予約」を実行しない「予約」実行結果
<TravelReservationResponse xmlns="http://fits/reservation">
    <hotelReservation>id:1001</hotelReservation>
    <rentalCarReservation />
</TravelReservationResponse>