HOME    BRMSブログ  No.18 柔軟なデータ構造を扱えるCorticon (Corticon Server編)

BRMS徹底活用ブログ

No.18 柔軟なデータ構造を扱えるCorticon (Corticon Server編)

2017.07.28 Progress Corticon

本エントリーは株式会社アシスト様が寄稿したエントリー(https://www.ashisuto.co.jp/product/category/brms/progress_corticon/column/detail/brmstech18.html)を転載したものとなります。

No.18 柔軟なデータ構造を扱えるCorticon (Corticon Server編)(2017年7月26日)

前回の記事「柔軟なデータ構造を扱えるCorticon 」では、Corticonの入出力データとして利用可能な階層型XMLとフラット型XMLという2種類のXMLを紹介し、それら2種類のXMLの違いと、Corticon Studio上でフラット型XMLを用いて多対多の入出力データを取り扱う方法を紹介しました。
今回の記事では、前回の記事で作成したルールをCorticon Serverへデプロイし、階層型とフラット型のリクエストXMLでディシジョン・サービスを呼び出して実行する2種類のJavaプログラムを紹介します。

[記事執筆環境]
Corticon Studio 5.5.2.7
Corticon Server 5.5.2.7
Axis2-1.6.2

ルールのコンパイルとデプロイ

今回の記事で使用するルールは、前回の記事「柔軟なデータ構造を扱えるCorticon 」で作成した、買物かごと商品のサンプルルールをそのまま使用します。語彙やルールの内容は前回の記事を参照してください。
このルールでは、入力XMLの形式が階層型かフラット型かによって実行結果が変わることに注意してください。
また、語彙のXML要素名に全角文字を使用すると記事後半で作成するWSDLやJavaのStubに影響するため、語彙のXML要素名すべてを半角英字としていますので注意してください。

<語彙の設定表>

エンティティ名 XML要素名
買い物かご Cart
属性名 XML要素名
商品数 numItems 整数
合計金額 totalPrice 整数
関連性名 XML要素名 カーディナリティ, 関連方向性
商品 Items *->*, 双方向
エンティティ名 XML要素名
商品 Item
属性名 XML要素名
商品名 Name 文字列
価格 Price 整数
関連姓名 XML要素名 カーディナリティ, 関連方向性
買い物かご Carts *->*, 双方向

■ディシジョン・サービスのXMLメッセージスタイル

Corticon Serverでは、ディシジョン・サービス毎に、「XMLメッセージスタイル」という設定を持っています。
これは、そのディシジョン・サービスがどのような型のXMLで入出力を行うかという設定で、「XMLメッセージスタイル」の設定可能な値はつぎの3つです。

●「自動検知 」
 ►デフォルト値。
 ►フラット型, 階層型のどちらの型のリクエストXMLも受け付ける。
 ►リクエストXMLがどちらの型かを自動的に検知してディシジョン・サービスを実行し、
  元のリクエストXMLの型と同じ型のレスポンスXMLを返す。
 ►Deployment Consoleでは「自動選択」になっているが意味は同じ。

●「フラット」
 ►基本的な動作は「自動検知」と同じ。
 ►リクエストXMLがどちらの型かを自動的に検知して元のリクエストXMLと同じ型のレスポンスXMLを返すが、
  リクエストXMLがどちらの型か明確ではない場合にフラット型のレスポンスXMLを返す。

●「階層」
 ►基本的な動作は「自動検知」と同じ。
 ►リクエストXMLがどちらの型かを自動的に検知して元のリクエストXMLと同じ型のレスポンスXMLを返すが、
  リクエストXMLがどちらの型か明確ではない場合に階層型のレスポンスXMLを返す。

ディシジョン・サービスの「XMLメッセージスタイル」のデフォルトは「自動検知」(自動選択)なので、ルールのデプロイ時に特に何も設定しなかった場合は、「自動検知」になります。
あえてディシジョン・サービスの設定を、「フラット」もしくは「階層」にしたい場合は以下を参考にしてください。

1.CDDでデプロイする場合
 ①Deployment Console で、「デシジョンサービス デプロイメントのプロパティ」の下部のスライドバーを一番右ま
  でスライドさせると表示される、「XMLメッセージングスタイル」を「自動選択」から「フラット」か「階層」に
  変更してから、「デプロイメントファイルの保存」ボタンを押す。

画像1

2.サーバー・コンソールの「デシジョンサービスのデプロイ 」メニューでデプロイする場合
 ①「XMLメッセージスタイル:」の箇所を、「自動検知」から「フラット」もしくは「階層」に変更して「デプロイ」ボタンを押す。

画像2

3.ディシジョン・サービスのデプロイ後に、サーバー・コンソールで設定を変更する場合
 ①サーバー・コンソールのディシジョン・サービスの詳細画面で、「サービスのコンフィグレーション」タブを開き、「XMLメッセージスタイル:」を「自動検知」から「フラット」もしくは「階層」に変更して、「更新」ボタンを押す。

画像3

今回の記事では、前回の記事「柔軟なデータ構造を扱えるCorticon 」で作成したルールを「FlatTest」というディシジョン・サービス名でCorticon Serverにデプロイしました。
デプロイ時に、XMLメッセージスタイルの設定を変更していないので、デフォルト設定の「自動検知」のままです。

ディシジョン・サービスのテスト

ルールをデプロイしたら、前回の記事「柔軟なデータ構造を扱えるCorticon 」で作成したテスト(ert)のテストサブジェクトを、Corticon Serverのディシジョン・サービス「FlatTest」に変更して、同じテストを行います。

まずは、Corticonでは標準的な階層型のリクエストXMLで実行した場合の結果は以下です。

<入力を階層型にした場合のテスト結果>

画像4

次に、フラット型のリクエストXMLで多対多のデータを作成した場合の実行結果は以下です。

<入力をフラット型にした場合のテスト結果>

画像5

どちらも、テストシートのテストサブジェクト(赤枠の部分)が、Corticon ServerのURLと対象ディシジョン・サービス「FlatTest」になっていることに注意してください。
前回の記事「柔軟なデータ構造を扱えるCorticon 」のCorticon Studio内でのテストと同様、Corticon Serverのディシジョン・サービスに対するテストでも、入力するデータの型によって結果が異なることが確認できます。
Corticon Serverは、Corticon StudioからのリクエストXMLがフラット型か階層型かを自動的に検知して、それぞれの型に応じたルール処理を行い、それぞれの型に応じたレスポンスXMLをCorticon Studioに返しますので、このような結果となります。

SOAPで階層型XMLとフラット型XMLを送受信するJavaクライアントの実装

次にSOAPでこのディシジョン・サービス「FlatTest」を実行するJavaクライアントプログラムを実装します。
フラット型と階層型のXMLを送受信するJavaクライアントをそれぞれ実装し、それぞれの作成手順の違いとプログラムの内容の違いを解説します。
なお、本記事で利用しているSOAP通信用のライブラリは「Axis2-1.6.2」です。

階層型XMLのJavaクライアントの実装

階層型XMLを送受信するJavaクライアントの実装手順は以下です。

1.Deployment Consoleで「階層」型XML用のWSDLを作成する。
 ①Deployment Consoleを起動し、デプロイしたルールフローを指定して、「デシジョンサービス名」 を「FlatTest」にする。
 ②「サービスコントラクト仕様」を設定する。
   ►デシジョンサービスレベルまたは語彙レベルのどちらでも問題ないが、
    ここでは「デシジョンサービスレベル」を選択する。
   ►型: 「WSDL」を選択する。
   ►XMLメッセージングスタイル: 「階層」を選択する。
   ►SOAPサーバURL: Corticon ServerのURLを指定する。
 ③「サービスコントラクトの生成」ボタンを押す。

画像6

2.Axis2のwsdl2javaを使用して、作成したWSDLからStubプログラムを生成する。
●実行するStub生成コマンドは以下。
    <Axis2Folder>\bin\wsdl2java.bat -uri <WSDLfilePath> -o <OutputFolder>
3.作成したStubとAxis2のライブラリを基に、SOAP通信を実行するJavaプログラムを実装する。


<ソースコード>


package sample ;

import org.apache.log4j.*;

import corticonservice.FlatTestStub;
import corticonservice.FlatTestStub.*;

public class FlatTestMain {

	public static void main(String[] args) {
		PropertyConfigurator.configure("conf/log4j.properties");
		Logger.getRootLogger().setLevel(Level.OFF);
		// Logger.getRootLogger().setLevel(Level.DEBUG);

		// ルールに入力するデータ構造体を生成
		Cart c1 = new Cart();
		Item[] items = {new Item(), new Item()};
			items[0].setName("バナナ");
			items[0].setPrice(100);
			items[1].setName("りんご");
			items[1].setPrice(200);
		c1.setItems(items);
		WorkDocumentsChoice choice1 = new WorkDocumentsChoice();
		choice1.setCart(c1);

		Cart c2 = new Cart();
		Item[] items2 = {new Item(), new Item()};
			items2[0].setName("バナナ");
			items2[0].setPrice(100);
			items2[1].setName("りんご");
			items2[1].setPrice(200);
		c2.setItems(items2);
		WorkDocumentsChoice choice2 = new WorkDocumentsChoice();
		choice2.setCart(c2);

		// データ構造体をWorkDocumentsオブジェクトに追加する
		WorkDocuments WDs = new WorkDocuments();
		WDs.addWorkDocumentsChoice(choice1);
		WDs.addWorkDocumentsChoice(choice2);

		// リクエストXMLの基となる最もシンプルな設定
		CorticonRequest Req = new CorticonRequest();
		Req.setDecisionServiceName("FlatTest");
		Req.setWorkDocuments(WDs);

		try {
			// Soapリクエスト実行
			CorticonRequestE ReqE = new CorticonRequestE();
			ReqE.setCorticonRequest(Req);
			CorticonResponseE ResE = new CorticonResponseE();
			FlatTestStub stub = new FlatTestStub();
			ResE = stub.processRequest(ReqE);

			// 結果の表示
			WorkDocuments rWDs = ResE.getCorticonResponse().getWorkDocuments();
			for (WorkDocumentsChoice rWDC: rWDs.getWorkDocumentsChoice()) {
				Cart rc = rWDC.getCart();
				System.out.println("買物かご [" + rc.getId() + "]");
				System.out.println("└ 合計金額: " + rc.getTotalPrice());
				for (Item ri: rc.getItems()) {
					System.out.println("└ 商品 [" + ri.getId() + "]");
					System.out.println("  └ 商品名: " + ri.getName());
					System.out.println("  └ 価格: " + ri.getPrice());
				}
			}
			/* 今回のルールではメッセージ無し
			Messages rMs = ResE.getCorticonResponse().getMessages();
			for (Message rM: rMs.getMessage()) {
				System.out.println("実行メッセージ: " + rM.getText());
			}
			*/
		} catch  (Exception e) {
			System.out.println(e.getMessage());
		}
		return;
	}
}

ソースコードのおおまかな処理内容はコメントに記述しています。
ディシジョン・サービスへ入力するデータ構造体(Javaオブジェクト)は、親オブジェクト「Cart」に子オブジェクト「Item」の配列が含まれるシンプルなツリー構造になっており、それぞれのオブジェクトの属性の値はCorticon Studioでのテスト<入力を階層型にした場合のテスト結果>と同じ値にしています。
作成したデータ構造体(Javaオブジェクト)は、StubとAxis2のライブラリによってCorticon ServerへのリクエストXMLに変換されます。
参考までに、このプログラムを実行した際に、Corticon ServerにPOSTされるリクエストXMLは以下です。(※)

  • リクエストXMLやレスポンスXMLの内容は、Corticon Serverのログの設定もしくはクライアント側のlog4jの設定を変更すると、確認することができます 。
<?xml version="1.0" encoding="UTF-8"?> 
<ns1:CorticonRequest xmlns:ns1="urn:Corticon" decisionServiceName="FlatTest">
  <ns1:WorkDocuments>
    <ns1:Cart>
      <ns1:Items>
        <ns1:name>バナナ</ns1:name>
        <ns1:price>100</ns1:price>
      </ns1:Items>
      <ns1:Items>
        <ns1:name>りんご</ns1:name>
        <ns1:price>200</ns1:price>
      </ns1:Items>
    </ns1:Cart>
    <ns1:Cart>
      <ns1:Items>
        <ns1:name>バナナ</ns1:name>
        <ns1:price>100</ns1:price>
      </ns1:Items>
      <ns1:Items>
        <ns1:name>りんご</ns1:name>
        <ns1:price>200</ns1:price>
      </ns1:Items>
    </ns1:Cart>
  </ns1:WorkDocuments>
</ns1:CorticonRequest>

データ構造体(Javaオブジェクト)が正しい階層型のリクエストXMLに変換されていることが確認できます。
Corticon Serverは、このリクエストXMLがPOSTされると自動的に階層型であることを検知し、階層型のデータとしてルール処理し、階層型のレスポンスXMLを返します。

このJavaプログラムでは、最終的にCorticon Serverからの階層型レスポンスXMLをJavaオブジェクトに変換し内容を表示します。結果の表示は以下になります。

画像7

結果を確認すると、Corticon Studioでのテスト<入力を階層型にした場合のテスト結果>と同じ内容であることが確認できます。
なお、結果に表示している各エンティティのIDは、入力データで何も値をセットしていなければ、Corticon Server側で処理される際に自動的に付与されます。入力データでエンティティIDに値をセットしていれば、その値がそのまま使用されます。次のフラット型XML用のJavaクライアントではエンティティIDに値をセットしていますので、そちらを参考にしてください。


フラット型XMLのJavaクライアントの実装

次にフラット型XMLを送受信するJavaクライアントを実装します。手順は以下です。

1.Deployment Consoleで「フラット」型XML用のWSDLを作成する。
 ①Deployment Consoleを起動し、デプロイしたルールフローを指定して、「デシジョンサービス名」 を「FlatTest」にする。
 ②「サービスコントラクト仕様」を設定する。
   ►「デシジョンサービス レベル」/「語彙レベル」のうち、
    「語彙レベル」を選択する必要がある。
   ►型: 「WSDL」を選択する。
   ►XMLメッセージングスタイル: 「フラット」を選択する。
   ►SOAPサーバURL: Corticon ServerのURLを指定する。
 ③「サービスコントラクトの生成」ボタンを押す。

画像8

2.Axis2のwsdl2javaを使用して、作成したWSDLからStubプログラムを生成する。
●実行するStub生成コマンドは以下。
    <Axis2Folder>\bin\wsdl2java.bat -uri <WSDLfilePath> -o <OutputFolder>
3.作成したStubとAxis2のライブラリを基に、SOAP通信を実行するJavaプログラムを実装する。


<ソースコード>


package sample ;

import org.apache.log4j.*;
import org.apache.axis2.databinding.types.*;

import corticonservice.FlatTestデシジョンサービスStub;
import corticonservice.FlatTestデシジョンサービスStub.*;

public class FlatTestMain {

	public static void main(String[] args) {
		PropertyConfigurator.configure("conf/log4j.properties");
		Logger.getRootLogger().setLevel(Level.OFF);
		// Logger.getRootLogger().setLevel(Level.DEBUG);

		// ルールに入力するデータ構造体を生成
		Item i1 = new Item();
		i1.setId(new Id("Item_1"));
		i1.setName("バナナ");
		i1.setPrice(100);
		WorkDocumentsChoice choice1 = new WorkDocumentsChoice();
		choice1.setItem(i1);

		Item i2 = new Item();
		i2.setId(new Id("Item_2"));
		i2.setName("りんご");
		i2.setPrice(200);
		WorkDocumentsChoice choice2 = new WorkDocumentsChoice();
		choice2.setItem(i2);

		Cart c1 = new Cart();
		c1.setId(new Id("Cart_1"));
		c1.addItems(getExtURI("#Item_1"));
		c1.addItems(getExtURI("#Item_2"));
		WorkDocumentsChoice choice3 = new WorkDocumentsChoice();
		choice3.setCart(c1);

		Cart c2 = new Cart();
		c2.setId(new Id("Cart_2"));
		c2.addItems(getExtURI("#Item_1"));
		c2.addItems(getExtURI("#Item_2"));
		WorkDocumentsChoice choice4 = new WorkDocumentsChoice();
		choice4.setCart(c2);

		// データ構造体をWorkDocumentsオブジェクトに追加する
		WorkDocuments WDs = new WorkDocuments();
		WDs.addWorkDocumentsChoice(choice1);
		WDs.addWorkDocumentsChoice(choice2);
		WDs.addWorkDocumentsChoice(choice3);
		WDs.addWorkDocumentsChoice(choice4);

		// リクエストXMLの基となる最もシンプルな設定
		CorticonRequest Req = new CorticonRequest();
		Req.setDecisionServiceName("FlatTest");
		WDs.setMessageType("FLAT");
		Req.setWorkDocuments(WDs);

		try {
			// Soapリクエスト実行
			CorticonRequestE ReqE = new CorticonRequestE();
			ReqE.setCorticonRequest(Req);
			CorticonResponseE ResE = new CorticonResponseE();
			FlatTestデシジョンサービスStub stub = new FlatTestデシジョンサービスStub();
			ResE = stub.processRequest(ReqE);

			// 結果の表示
			WorkDocuments rWDs = ResE.getCorticonResponse().getWorkDocuments();
			for (WorkDocumentsChoice rWDC: rWDs.getWorkDocumentsChoice()) {
				Cart rc = rWDC.getCart();
				if (rc != null) {
					System.out.println("買物かご [" + rc.getId() + "]");
					System.out.println("└ 合計金額: " + rc.getTotalPrice());
				}
				Item ri = rWDC.getItem();
				if (ri != null) {
					System.out.println("商品 [" + ri.getId() + "]");
					System.out.println("└ 商品名: " + ri.getName());
					System.out.println("└ 価格: " + ri.getPrice());
				}
			}
			/* 今回のルールではメッセージ無し
			Messages rMs = ResE.getCorticonResponse().getMessages();
			for (Message rM: rMs.getMessage()) {
				System.out.println("実行メッセージ: " + rM.getText());
			}
			*/
		} catch  (Exception e) {
			System.out.println(e.getMessage());
		}
		return;
	}
	private static ExtURIType getExtURI(String EntityId) {
		try {
			URI oURI = new URI(EntityId);
			ExtURIType oExtURI = new ExtURIType();
			oExtURI.setHref(oURI);
			return oExtURI;
		} catch  (Exception e) {
			System.out.println(e.getMessage());
			return null;
		}
	}
}

階層型との手順の違いはDeployment ConsoleでWSDLを作成する際のオプションと、Javaプログラムのソースコードの中身です。

フラット型XMLを扱うクライアントを作成するためのStubを生成するには、WSDL作成時のオプション設定として、「語彙レベル」を選択し、XMLメッセージングスタイルを「フラット」にする必要があります。前者の「語彙レベル」選択の設定は見落としやすいので注意が必要です、もし「語彙レベル」ではなく「デシジョンサービスレベル」でWSDLとStubを作成すると、例示したJavaプログラムと同じメソッドは利用できず、結果として多対多のフラット型XMLをPOSTできません。

Javaプログラムのソースコードのおおまかな処理内容はコメントに記述しています。
プログラム中に日本語のクラス名「FlatTestデシジョンサービスStub 」が出てきていますが、これは作成したWSDL内に「FlatTestデシジョンサービス」という日本語が含まれているためです。Javaではクラス名に日本語を使用しても特に問題ありませんが、日本語クラス名での実装はあまり一般的ではありません。もし英語に変更する場合は、テキストエディタでWSDL内の「FlatTestデシジョンサービス」を「FlatTestDecisionService」等に置換してから、Stubを再作成してください。

階層型のプログラムと比較すると、ディシジョン・サービスへの入力となるデータ構造体(Javaオブジェクト)が、親オブジェクト「Cart」に子オブジェクト「Item」の配列が含まれる、シンプルなツリー構造ではありません。「Cart」オブジェクトと「Item」オブジェクトを4個全て並列に生成し、「Item」オブジェクトにセットしたエンティティIDをURIに変換した属性を「Cart」オブジェクト内に追加しています。このようにすることで、Corticon Studioでのテスト<入力をフラット型にした場合のテスト結果>と同じ多対多の関連性を持ったフラット型の入力データになります。

参考までに、このプログラムを実行した際に、Corticon ServerにPOSTされるリクエストXMLは以下です。

<?xml version="1.0" encoding="UTF-8"?> 
<ns1:CorticonRequest xmlns:ns1="urn:Corticon" decisionServiceName="FlatTest">
  <ns1:WorkDocuments messageType="FLAT">
    <ns1:Item id="Item_1">
      <ns1:name>バナナ</ns1:name>
      <ns1:price>100</ns1:price>
    </ns1:Item>
    <ns1:Item id="Item_2">
      <ns1:name>りんご</ns1:name>
      <ns1:price>200</ns1:price>
    </ns1:Item>
    <ns1:Cart id="Cart_1">
      <ns1:Items href="#Item_1" />
      <ns1:Items href="#Item_2" />
    </ns1:Cart>
    <ns1:Cart id="Cart_2">
      <ns1:Items href="#Item_1" />
      <ns1:Items href="#Item_2" />
    </ns1:Cart>
  </ns1:WorkDocuments>
</ns1:CorticonRequest>

データ構造体(Javaオブジェクト)が正しいフラット型のリクエストXMLに変換されていることが確認できます。また、プログラムでセットした各エンティティIDが正しくセットされていることと、リンク(href)によって多対多の関連性が表現されていることを確認することができます。
Corticon Serverは、自動的にこのリクエストXMLがフラット型であることを検知し、フラット型のデータとしてルール処理し、フラット型のレスポンスXMLを返します。

このJavaプログラムでは、最終的にCorticon Serverからのフラット型レスポンスXMLをJavaオブジェクトに変換し内容を表示します。
結果の表示は以下になります。

画像9

結果を確認すると、Corticon Studioでのテスト<入力をフラット型にした場合のテスト結果>と同じ内容であること、また入力でセットした各エンティティIDがそのまま引き継がれて処理されたことがわかります。

まとめ

今回は、Corticon Serverに多対多の関連性を持つフラット型XMLを想定したルールをデプロイし、Javaクライアントプログラムからフラット型XMLを送受信してディシジョン・サービスを実行する方法をご紹介しました。

階層型XMLと比較して、フラット型XMLのメリットとデメリットは前回の記事「柔軟なデータ構造を扱えるCorticon 」のまとめにも書きましたが、以下のようになります。

●フラット型XMLのメリット
 ►階層型XMLでは表現できない多対多の関連性を表現できる。
 ►多対多の関連性にした結果、階層型XMLに比べてデータ量が少なくなる傾向がある。

●フラット型XMLのデメリット
 ►Corticon Studioでデータを作成する際に少し難しい操作が必要。
 ►XMLの視認性が低く、XMLパースプログラムでの取り扱いも少し難しい。

Corticonでは階層型XMLを利用するのが一般的ですが、フラット型XMLには階層型XMLにないメリットがありますので、もしもフラット型XMLの入出力を行うディジョン・サービスとクライアントプログラムを実装する際には今回の記事の内容を参考にしてください。

著者紹介

谷列樹さん

情報基盤技術統括部 プログレス推進部

以前は、Linux系のプログラマ兼SEとして、受託請負開発などに従事していた。
また、IT系雑誌や書籍の記事執筆などにも携わった経験をもつ。
現在は、BRMS Progress Corticonの技術サポート、研修などを行う。