【SOA における WCF の役割 (6)】 BizTalk Server からの LOB Adapter の使用 - orchestration 編
環境:
.NET Framework 3.0
BizTalk Server 2006 R2
Visual Studio 2005
WCF LOB Adapter SDK
BizTalk LOB Adapter Pack (※現在、Beta 版を使用可能です)
Oracle 10 g Express Edition
Oracle Data Access Components (ODAC) 2.0
こんにちは。
今回は、前回の続きとして、orchestration を作成していきます。
- WCF と BizTalk、そして LOB Adapter
- WCF における Binding と Channel の基礎
- WCF LOB Adapter SDK によるカスタムアダプター実装
- WCF からの LOB Adapter の使用
- BizTalk Server からの LOB Adapter の使用 - schema 編
- BizTalk Server からの LOB Adapter の使用 - orchestration 編
- BizTalk Server からの LOB Adapter の使用 - deploy 編
↑ すみません、前回、タイトルを「・・・前編」としていたと思うのですが、今回非常に長すぎてまたわけることにしましたので、前回投稿のタイトルを変更しました _O_
BizTalk プロジェクトの構築 (オーケストレーションの作成)
では、前回のプロジェクトを開いて、続きから設定をしていきます。
- ソリューションエクスプローラでプロジェクトを右クリックして、[追加] - [新しい項目の追加] で、[BizTalk オーケストレーション] を選択します (下図の .odx ファイルが作成されます)。
また、作成されたオーケストレーションの [オーケストレーションのプロパティ] で [トランザクションの種類] として「長時間トランザクション」を選択しておきます。

- つぎにメッセージ (Message) を構築します。登場するメッセージは 3 つです。
1 つは Oracle から inbound 処理を使って更新データを取得する「メッセージ」、もう 1 つは CustomAdapter1 の outbound 処理 (EchoTimes オペレーション) にデータを送信する「メッセージ」、さいごに CustomAdapter1 の outbound 処理 (EchoTimes オペレーション) から応えをもらう「メッセージ」という 3 つのメッセージです。
- まず Oracle の更新データを取得の「メッセージ」を作成します。
[オーケストレーションの種類] と書かれたウィンドウ上で、[メッセージ] を右クリックして、[新しいメッセージ] を選択し、メッセージの名称 (識別子) を「Receive」、メッセージの種類として、前回登録したスキーマ定義の要素の中から 「IntegrationSample.OracleDBBindingSchema.POLLINGSTMT」を選択します。(これで、前回登録したスキーマ定義を元にメッセージが作成されました)

- 同様の方法で、残りの、CustomAdapter1 の outbound 処理にデータを送信する「Out」メッセージと、CustomAdapter1 の outbound 処理からデータを取得する「In」メッセージを以下の通り作成します。
識別子 : Out
メッセージの種類 : IntegrationSample.CustomAdapter1BindingSchema.EchoTimes
識別子 : In
メッセージの種類 : IntegrationSample.CustomAdapter1BindingSchema.EchoTimesResponse
- つぎに、Oracle の更新データを受信する「受信」アクティビティと、Oracle 接続のための論理ポートを作成します。
- 左のツールボックスから [受信] アクティビティをオーケストレーションの内部にドラッグアンドドロップし、
さらにツールボックスから [ポート画面] のフレーム内に [ポート] をドラッグアンドドロップします。
- この際、[ポート構成ウィザード] というウィザードが表示されますので、ウィザードに従って以下の通り設定します
名前 : OracleReceivePort (← 一意なら何でもOKです)
ポートの種類名 : OracleReceivePortType (← 一意なら何でもOKです)
通信方向 : 一方向
アクセスの制限 : 内部 - このプロジェクトに限定
ポートの通信方向 : 常にこのポートでメッセージを受信する
ポートのバインド : あとで指定する
なお、この段階では、まだ論理ポートを作っただけで、アダプターには接続されていません。
- 上記で挿入した [受信] アクティビティを選択し、プロパティウィンドウで [メッセージ] プロパティとして「Receive」を選択します。
また、この [受信] アクティビティの呼び出しと同時にオーケストレーションのインスタンスを新規作成するため、この [受信] アクティビティの [アクティブ化] プロパティを true に設定します。
- [ポート] と[受信] のコネクタをマウスで接続します。(最終的には、下図のようになります)

- 続いて、受信したメッセージからレコードを解析して 1 レコードずつ取り出す必要がありますが、ここは簡単にプログラミングをしていきましょう。なお、ここで紹介する方法以外にも xslt によるスキーマ変換などでも作成可能ですし、むずかしい処理の場合には xsd.exeコマンドを使用してserializable な .NET class を作成し、取得した XML をこのクラスに Deserilialize すれば、コード(式)の中ではクラスのまま扱っていつもの要領で Programming をおこなうことも可能です (今回は、下記の通り XML の素のまま処理します)。
そのための準備として、まず、[参照の追加] で %Program Files%\Microsoft BizTalk Server 2006 の中にある Microsoft.XLANGs.BaseTypes.dll を追加しておいてください。
また、オーケストレーションの変数として、以下の変数を作成します (用途は、下記の「説明」欄の通りです)。
ItemCount
型 : int32
説明 : レコードの総数を入れる
ItemCurrent
型 : int32
初期値 : 0
説明 : 現在処理中のレコード番号
ReceiveMessage
型 : Microsoft.XLANGs.BaseTypes.XLANGMessage
説明 : Receive メッセージを入れる XLANGMessage 型の変数
ReceivePart
型 : Microsoft.XLANGs.BaseTypes.XLANGPart
説明 : XLANGMessage の中のメッセージ本体
ReceiveDocument
型 : System.Xml.XmlDocument
説明 : ReceivePart から取り出した XmlDocument

ItemCurrent 変数は、[初期値] プロパティを 0 に設定しておきますので注意してください。
また、BizTalk のメッセージは、すべて XLANGMessage 型のオブジェクトとしてコードで扱うことが可能になっており、さらに、このメッセージオブジェクトは、MIME などのマルチパートに対応していて、XLANGPart 型のオブジェクトのコレクションになっています。そして、通常は、そのコレクションの 0 番目の要素 (XLANGMessage[0]) がメッセージの XML が入った本体になっています。
この概念は、このあとのプログラミングでも必要になる知識ですので、おぼえておいてください。
オーケストレーションのインスタンスは長時間の実行の場合などにはメモリから退避されますので、上述のオーケストレーションの変数はすべてシリアライズ可能である必要があるということもおぼえておきましょう。
- では、初期化の処理をプログラムで作成していきます。初期化の処理では、1 件ずつレコードを取得する (ループ) のに備え、レコード件数(総数)を取得するようにします。
- 上述したように、インスタンスは、長時間の実行時など適切なタイミングでメモリから退避されますが、この初期化処理では、アトミックな (メモリ中のみで扱われる不揮発な) 変数、つまり、シリアライズ可能でない変数も使用したいと思います。
そこで、ツールボックスから [スコープ] アクティビティをドラッグアンドドロップし、このアクティビティの [トランザクションの種類] として「アトミック」を選択し、この中に [式] アクティビティをドラッグアンドドロップします。(下図)

- スコープ内で使用する一時的な変数として、以下を宣言します。(今回はアトミックなスコープなので、シリアライズ可能である必要はなく、あらゆるクラスを使用できます)
ReceiveNodeList
型 : System.Xml.XmlNodeList
ReceiveNamespaceManager
型 : System.Xml.XmlNamespaceManager

- 上記で挿入した [式] アクティビティをダブルクリックし、以下のコードを記述します。
// メッセージから XmlDocument を取り出す
ReceiveMessage = Receive;
ReceivePart = ReceiveMessage[0];
ReceiveDocument = (System.Xml.XmlDocument) ReceivePart.RetrieveAs(typeof(System.Xml.XmlDocument));
// XML を解析して、返ってきたレコードの件数を取り出す
ReceiveNamespaceManager = new System.Xml.XmlNamespaceManager(ReceiveDocument.NameTable);
ReceiveNamespaceManager.AddNamespace("p", @"http://Microsoft.LobServices.OracleDB/2007/03/POLLINGSTMT");
ReceiveNodeList = ReceiveDocument.SelectNodes(@"/p:POLLINGSTMT/p:POLLINGSTMTRECORD/p:POLLINGSTMTRECORD", ReceiveNamespaceManager);
ItemCount = ReceiveNodeList.Count;
- これで Receive メッセージの中の XML ドキュメント (XmlDocument) と、レコード件数 (総数) が取得できました。つづいて、レコードの中から 1 件ずつ取り出してループ処理を作成します。
まずは、ツールボックスから [ループ] アクティビティをドラッグアンドドロップし、このアクティビティをダブルクリックして、以下の条件式 (bool を返す判断の式) を入力します。
ItemCurrent < ItemCount
- つぎに、レコードを取り出す処理をまたコードを使って記述しましょう。
先ほどと同様に、アトミックな [スコープ] アクティビティを上記で挿入したループの中に挿入し、準備としてスコープに、こんどは以下の変数を宣言しておきます。(これらの変数の使い方は、このあと記載するコードを参照してください)
NodeCurrent
型 : System.Xml.XmlNode
NodeTarget
型 : System.Xml.XmlNode
ReceiveNodeList
型 : System.Xml.XmlNodeList
ReceiveNamespaceManager
型 : System.Xml.XmlNamespaceManager
OutDocument
型 : System.Xml.XmlDocument
OutParmNode
型 : System.Xml.XmlNode
OutParmText
型 : System.Xml.XmlCharacterData
OutRootElement
型 : System.Xml.XmlElement

- さて、この中でおこなう処理ですが、今回はレコードの中身を解析して、1 レコードずつに対して上記で宣言した Out メッセージを作成していくという処理になりますので、このスコープアクティビティの中に、ツールボックスの中から [メッセージの構築] アクティビティと、その中にさらに [メッセージの割り当て] アクティビティを挿入します。
さらに、[メッセージの構築] アクティビティの [構築メッセージ] プロパティでは 「Out」 を選択します。

- 上記で挿入した [メッセージの割り当て] アクティビティをダブルクリックし、以下の式を記述します。(実施している内容については、コメントを参照してください)
// XML を解析して、レコードの NodeList を取得します
ReceiveNamespaceManager = new System.Xml.XmlNamespaceManager(ReceiveDocument.NameTable);
ReceiveNamespaceManager.AddNamespace("p", @"http://Microsoft.LobServices.OracleDB/2007/03/POLLINGSTMT");
ReceiveNodeList = ReceiveDocument.SelectNodes(@"/p:POLLINGSTMT/p:POLLINGSTMTRECORD/p:POLLINGSTMTRECORD", ReceiveNamespaceManager);
// 送信用 (Outメッセージ) の XmlDocument を作成します
OutDocument = new System.Xml.XmlDocument();
OutRootElement = OutDocument.CreateElement("EchoTimes", @"myscheme://myadapter");
OutParmNode = OutDocument.CreateNode(System.Xml.XmlNodeType.Element, "param", @"myscheme://myadapter");
NodeCurrent = ReceiveNodeList.Item(ItemCurrent); // 現在のノードを取り出す
NodeTarget = NodeCurrent.ChildNodes.Item(1); // そのノードの NAME の箇所を取り出す
OutParmText = OutDocument.CreateTextNode(NodeTarget.InnerText); // NAME の「値」を設定
OutParmNode.AppendChild(OutParmText);
OutRootElement.AppendChild(OutParmNode);
OutDocument.AppendChild(OutRootElement);
// XmlDocument から XLANGMessage を作成
// (XML 形式の XLANGMessage 型では、このように XmlDocument 型の引数のコンストラクタが使えます)
Out = OutDocument;
// カウンタを追加
ItemCurrent = ItemCurrent + 1;
- これで Out メッセージ (EchoTimes オペレーションに渡す形式の XML) がコードにより構築されましたので、あとは、この Out メッセージを CutomAdapter1 に送信して応えを受信するのみです。
まずは、[送信] アクティビティ ([メッセージ] プロパティとして「Out」を選択してください) と [ポート] を挿入し、ポート構成ウィザードで以下の通り入力します。(こんどのポートは、受信と送信の順番で双方をおこないますので、以下の通り設定します。)
名前 : CustomAdapterPort (← 一意なら何でもOKです)
ポートの種類名 : CustomAdapterPortType (← 一意なら何でもOKです)
通信方法 : 要求 - 応答
アクセスの制限 : 内部 - このプロジェクトに限定
ポートの通信方法 : 要求の送信と応答の受信をおこないます。 (←注意: 順番が重要です。逆を選ばないように注意してください)
ポートのバインド : 後で指定する
- [送信] アクティビティと、上記のポートを接続してください。
- つぎに、CustomAdapter1 からの応えを受け取ります。
[受信] アクティビティを追加して、今度は [メッセージ] プロパティとして「In」を選択して、上記のポートと接続します。(ここまでで、下図のようになります)

- さいごに、受信した応答 (XML 形式の In メッセージ) をファイルに出力します。
いつものように、[送信] アクティビティと [ポート] を追加し、[送信] アクティビティの [メッセージ] プロパティとして「In」を選択し、両者を接続します。
ポートは、以下のように設定しておいてください。
名前 : FilePort (← 一意なら何でもOKです)
ポートの種類名 : FilePortType (← 一意なら何でもOKです)
通信方向 : 一方向
アクセスの制限 : 内部 - このプロジェクトに限定
ポートの通信方向 : 常にこのポートでメッセージを送信する
ポートのバインド : あとで指定する
以上で、オーケストレーションが完了しました。
作成されたオーケストレーションは、(画面に入りきってませんが、、、) 以下のような感じです。

このオーケストレーションでは「ポート」と呼ばれる接続オブジェクトを設定しましたが、ここではまだ実際に「接続」の設定をおこなうものでなく、論理的なものである (依存しているのは、スキーマ定義のみ) という点に注意してください。
物理的な接続は次回以降の配置の設定でおこなうことになります。
では、次回は、いよいよ配置と動作確認をおこなってみたいと思います。(← いつも「いよいよ」ですみません。もう 1 回続きます、、、)