2013年

8月

12日

【iDempiere/Pentaho Lab】iDempiereからETLを呼び出す

 iDempiereから、PentahoのETLツールであるKettle(ケトル)のSpoon(スプーン)で作成した、トランスフォーメーションやジョブを、引数を渡して実行する方法を調査&研究したいと思います。これが、ちゃんと実行できるとリアルタイムで他のアプリケーションとのデータのやりとりもかなり楽に実現できるのではないかと思います。

 

調査&研究シナリオ

 iDempiereの受注伝票(Sales Order)のデータを保存する時に、PentahoのPanで実行する事ができるトランスショーメーションのBatファイルを呼び出して、別のテーブル(C_Order2)に受注伝票のヘッダー情報(C_Orderテーブルの情報)を書き込むというシナリオで調査&研究したいと思います。

 このシナリオの検証ポイントは下記になるかなと思います。

  • JavaからトランスフォーメーションをキックするBatファイルの実行。
  • Batファイルに引数として、受注伝票ID(C_Order_ID)を渡して、それをさらにETLに渡す。
  • 結果を受け取って、失敗した際にはiDempiere側でも失敗にする必要がある。

といったところでしょうか。

環境は、とりあえずローカルのWindoiws7で行いたいと思います。

Step1:トランスフォーメーションの作成

Spoon
Spoon

 受注伝票のヘッダー情報を格納しているC_OrderテーブルをそのままコピーしたC_Order2テーブルを作成し、C_Orderテーブルの情報をC_Order2テーブルにインサートもしくはアップデートするように設定します。

 Get System Infoステップを使用して、Windowsのコマンドプロンプトに入力した引数を受け取れるように準備しておきます。

Get System Info
Get System Info

 コマンドプロンプトで入力した引数はGet System InfoステップでC_Order_IDというフィールド名で受け取っているので、それを"?"を使用して、Table inputステップに渡します。

Table input
Table input

 Table input ステップで取得したデータを、Insert/Update ステップを使用してC_Order2テーブルにインサートもしくはアップデートして行きます。

Insert / Update
Insert / Update

Step2:Panで実行できるBatファイルの作成

 次のようなバッチファイルを作成します。ここではバッチファイルの名前は"From_Order_To_Order2.bat"として、C:\pentaho-ce\data-integration\wsにおいておきます。

 

../Pan.bat /file "C:\pentaho-ce\ws\From_Order_to_Order2.ktr"  %1 /level:Basic > C:\pentaho-ce\ktr.log

 

Sample Batファイル
上記の"From_Order_To_Order.bat"のSampleファイルです。拡張子".txt"を削除して、必要な所を適宜修正してお試し下さい。
Sample.bat.txt
テキスト文書 100 Bytes

 

【バッチファイルの構成】

  • ..\Pan.bat ⇒ 実行するバッチファイルの指定。
  • /file "C:\pentaho-ce\ws\From_Order_to_Order2.ktr" %1 ⇒ 実行するトランスフォーメーションのファイルの指定と、引数の設定。%1でコマンドプロンプトで入力した引数を取得する。
  • /level:Basic > C:\pentaho-ce\ktr.log ⇒ ログファイルの指定。ログ出力は、Erroe / Notihg / Minimal / Basic / Detailed / Debug / Rowlevel が指定できます。 

 

 この状態でコマンドプロンプトより下記のように、batファイルと引数(1000033)を入力して実行してみます。

 

 

C:\pentaho-ce\data-integration\ws>From_Order_To_Order2.bat 1000033

 

 

 そうすると、次のようなエラーがログファイルに出力されました。

ERROR: operator does not exist: numeric = character varying  ヒント: No operator matches the given name and argument type(s). You might need to add explicit type casts.  ポジション: 1397

 

どうやら、キャストするように言われているようです。そこで、select Valuesのステップを1つ追加します。

 

Select values
Select values

 C_Order_IDのフィールドは、文字列としてコマンドプロンプトよりデータを受け取っているので、それを数値としてキャストします。

 

 これで、C_Order_ID=1000033のレコードがC_Order2テーブルにインサート(もしくはアップデート)された事が確認できると思います。

 

 

Step3:Javaでコーディング

 Javaでコーディングする前に、検証を簡単にするために今まで相対パスで指定していた所は絶対パスに修正しておきます。またログファイルのリダイレクト設定は外しておきます。

 

"C:\pentaho-ce\data-integration\Pan.bat" /file "C:\pentaho-ce\ws\From_Order_to_Order2.ktr" %1 /level:Basic

 

 Javaから、batファイルを実行するには以下のように記述するようです。

try{

String C_Order_id = new Integer( getC_Order_ID() ).toString();

String[] command = new String[]{"C:\\pentaho-ce\\data-integration\\ws\\From_Order_To_Order2.bat", C_Order_id };

ProcessBuilder pb = new ProcessBuilder(command);

Process process = pb.start();

InputStream is = process.getInputStream();

BufferedReader br  = new BufferedReader(new InputStreamReader(is));

String stringLine = null;

StringBuffer stringBuffer = new StringBuffer();

while ((stringLine  = br.readLine())!=null)

{

System.out.println("pentaho>"+stringLine );

stringBuffer =stringBuffer.append(stringLine).append(System.getProperty("line.separator"));

}

is.close();int ret = process.waitFor();


} catch ( Throwable t ){


}

 

 これで、いちおうiDempiereからPentahoのETLツールKettleのSpoonで作成したトランスフォーメーションを、呼び出して実行し結果を取得する事ができます。結果を取得できるので、それでハンドリングすれば、エラー処理もできるのではないかと思います。

 

確認及びテスト

 ここまで試してみて、この処理には大きな欠陥がある事に気が付きました。iDempiereでは、Compiereとモデルクラスのコミット処理が大きく異なっていたので、受注伝票の処理を行っているMOrderクラスのafterSave()メソッドでデータベースからデータを抽出するのでは、C_OrderテーブルとC_Order2テーブルのデータは同期できないという事です。

 iDempiereでは、afterSave()メソッドの処理も問題なく完了した時点でコミットされるようなので、aftesrSave()メソッドでのデータを抽出してしまうと、C_Order2のテーブルにはC_Orderテーブルの処理中のレコードのコミット前の状態のデータがインサートもしくはアップデートされる事になってしまうのです。

 

修正

 ここで、Step1に戻って再度トランスフォーメーションから作り直したいと思います。

 今度作成するトランスフォーメーションは、DBからのデータ抽出ステップは排除して、Get System Info ステップで、C_Order2テーブルにインサートする値を引数として渡してもらう処理にしようと思います。

 ただ、このステップでは、コマンドライン引数として渡せる数は10個に限られるのが弱点ですね。無制限に取得できる方法があると良いのですが…。iDempiereの複数の値を区切り文字を付加して1つの引数にまとめて、その引数をETL側で区切り文字をもとに分割して戻すという処理が、簡単に思いつく方法でしょうか…。

 コマンドプロンプトから渡される情報は、文字情報なのでキャストする設定を行います。

 

そして、そのままデータをインサートもしくはアップデートします。

バッチファイルも引数を複数取得できるように修正します。

 

"C:\pentaho-ce\data-integration\Pan.bat" /file "C:\pentaho-ce\ws\From_Order_to_Order2.ktr" %1 %2 %3 %4・・・ /level:Basic

 

 Javaのプログラムも複数引数を渡すように修正します。

String C_Order_ID = new Integer( getC_Order_ID() ).toString();

String AD_Client_ID = new Integer(getAD_Client_ID() ).toString();

String AD_Org_ID = new  Integer(getAD_Org_ID() ).toString();

String C_BPartner_ID =  new  Integer(getC_BPartner_ID() ).toString();

String[] command = new String[]{"C:\\pentaho-ce\\data-integration\\ws\\From_Order_To_Order2.bat", C_Order_ID,AD_Client_ID,AD_Org_ID,C_BPartner_ID,…};

 これで、C_OrderテーブルとC_Order2テーブルの内容をリアルタイムで同期させる事ができると思います。

 もし、ETL側で何らかのエラーが発生しデータが正しく登録されなかった場合は、iDempiereはそれを判断し、afterSave()メソッドでretrun falseをすればエラー処理も2つのテーブル間での同期を崩すことなく正しく処理できるのではないかと思います。

追記:pentaho側のロールバック処理について

 pentaho側でエラーが発生した場合はロールバックして、iDempiereにエラーを返す必要があります。ここでは、pentahoのロールバック処理について追記しておきます。

 Spoonのキャンパス上で、右クリックして表示されるメニューの中から、"データ変換設定"を選択します。

 その他タブを選択し、"固有の接続を使用する"をONにします。"固有の接続を使用する"というのは適切な訳では無いそうで、このフラグをONにする事により、pentaho側の処理が正常終了でない場合はロールバックがされるそうです。

今後の課題

次の事については今後の課題としたいと思います。

  • エラー処理の複雑さの緩和…エラー処理のハンドリングがこのままでは、正規表現などでデータを抜き取って判断する必要があるため複雑になってしまう。もっと簡素化したいなと思います。
  • afterSave()メソッドで実装するのではなく、ワークフロー上でプロセスを実行する事で上手く動作させる事ができないかな?->ワークフロー上でプロセスを実行して、エラーをハンドリングして、データの整合性が保てれば、既存ソースコードを修正する事無くできるので理想的。

 

 

参考サイト