Oracle® Fusion Middleware Oracle Business Process Managementによるビジネス・ルールの設計 12c (12.2.1) E72523-01 |
|
![]() 前 |
![]() 次 |
この付録では、Oracle Business Rulesに関するよくある質問を紹介します。
Javaオブジェクトをアサートした後でModifyアクションを使用せずに変更した場合は、オブジェクトをRules Engineに再アサートする必要があります。
したがって、変更したJavaオブジェクトに関連付けられたルールが起動しない場合は、Rules Engineでルール条件が再評価されておらず、ルールもアクティブ化されていないことを意味します。このため、Modifyアクションを使用せずにJavaオブジェクトを変更した場合には、オブジェクトをRules Engineに再アサートする必要があります。
Oracle Business Rules RL LanguageとJavaとの間には多くの相違があります。
Oracle Business Rules RL LanguageとJavaの相違の詳細は、『Oracle Business Process Managementルール言語リファレンス』の付録Aを参照してください。
Oracle Business Rules RuleSessionオブジェクトに対するメソッド・コールは、複数のスレッドによるコールによって、RuleSessionレベルでの例外が発生しないようにスレッド・セーフになっています。ただし、メソッドの実行に排他性やトランザクション保証はありません。Rules Engineの最低レベルのrun
メソッドは同期化されるため、共有RuleSession
を持つ2つのスレッドは同時にrun
を実行できません。run
への一方のコールは、もう一方のコールが終了するまで待機する必要があります。
Oracle Business Rules関数は、デフォルトでは同期化されません。Javaメソッド同様、Oracle Business Rules関数は並行して実行可能であり、プログラマは同期化されたブロックを使用して共有データ(たとえば、結果データを含むHashMap
)へのアクセスを保護する必要があります。
トランザクションのような形式で実行する必要があるアクション・セットでは、共有オブジェクトに関する同期化が必要になります。RuleSession
オブジェクトに関する同期化はできません。RuleSession
メソッドのコール時にスローされる例外で、RuleSession
オブジェクトの破棄が必要になる場合があるからです。
RuleSession
オブジェクトをOracle Business Rulesで使用するほとんどの場合、各スレッドまたはサーブレット・インスタンスは、ローカルのRuleSession
オブジェクトを作成して使用する必要があります。この使用パターンは、この方法でJDBC接続を使用した場合とおおよそ似ています。
次の例は、共有RuleSession
オブジェクトを使用する方法を示しています。
Thread-1が次のものを含む場合:
ruleSession.callFunctionWithArgument("assert", singleFact1); ruleSession.callFunctionWithArgument("assert", singleFact2);
Thread-2が次のものを含む場合:
ruleSession.callFunction("run"); ruleSession.callFunction("clear");
このケースでは、2つのスレッドの実行が次のコード例のように進み、Oracle Business Rulesの共有rulesession
オブジェクトを示すことがあります。
Thread-1: ruleSession.callFunctionWithArgument("assert", singleFact1); Thread-2: ruleSession.callFunction("run"); Thread-2: ruleSession.callFunction("clear"); Thread-1: ruleSession.callFunctionWithArgument("assert", singleFact2);
前の例では、Thread-1がアサートした2つのファクトの両方が、run
へのコール中にRuleSession
に存在することはありません。また、1つのスレッドのみがrun
メソッドをコールすることに注意してください。共有RuleSession
で複数のスレッドからrun
をコールできるデザインを使用すると、非常に見つけにくい不具合が作成される可能性があり、パフォーマンス上の利点はありません。
共有RuleSession
オブジェクトに対するすべてのアクセスを同期化して、意図した動作を保証する必要があります。ただし、RuleSession
インスタンスが例外をスローして回復不能になることがあるため、このオブジェクトを同期化オブジェクトとしては使用しないでください。かわりに、別の共有オブジェクトを同期点として使用します。
候補としては、共有サーバー・プロセス・プロデューサが考えられます。RuleSession
用のコンシューマ・モデルです。このモデルでは、複数のスレッドが共有のRuleSession
にファクトをアサートし、1つのスレッドが定期的にrun
をコールし、結果を読み取って出力します。これにより、2つのコード・セグメントが必ずシリアルに実行され、混在できなくなるので、スレッドの競合が発生しなくなります。例C-1 、例C-2 、例C-3 の共有オブジェクト、プロデューサ・コード、コンシューマ・コードを使用したコード例を参照してください。
例C-1 に共有オブジェクトを含むコードを示します。
例C-1 RuleSessionの共有オブジェクト
RuleSession ruleSession; Object ruleSessionLock = new Object();
例C-2 にプロデューサ・コードを示します。
例C-2 RuleSessionのプロデューサ・コード
public String addFacts(FactTypeA fa, FactTypeB fb, FactTypeC fc){ String status = ""; synchronized(ruleSessionLock){ try { ruleSession.callFunctionWithArgument("assert", fa); ruleSession.callFunctionWithArgument("assert", fb); status = "success"; } catch (Exception e) { // a method that creates a new RuleSession loads it with rules initializeRuleSession(); status = "failure"; } return status; }
例C-3 にコンシューマ・コードを示します。
例C-3 RuleSessionのコンシューマ・コード
public List exec(){ synchronized(ruleSessionLock){ try { ruleSession.callFunction("run"); List results = (List)ruleSession.callFunction("getResults"); ruleSession.callFunction("clearResults"); return results; } catch (Exception e) { // a method that creates a new RuleSession loads it with rules initializeRuleSession(); return null; } } }
注意:
複数のスレッドがRuleSession
オブジェクトを共有している場合、複数のスレッドがrun
メソッドをコールすると、非常に見つけにくい不具合が作成される可能性があり、パフォーマンス上の利点はありません。
ファクトを使用した場合、Oracle RLのランタイム動作で極端な結果が生成されることがあります。
次の自己結合例のOracle RLコードについて考えてみます。
class F {int i; }; rule r1 { if (fact F f1 && fact F f2) { println("Results: " + f1.i + ", " + f2.i); } } assert(new F(i:1)); assert(new F(i:2)); run();
前の例で出力される行数はいくつでしょうか。f1
とf2
の両方については、同じファクト・インスタンスが一致できるため、答えは4行です。
つまり、この例では次の出力が生成されます。
Results: 2, 2 Results: 2, 1 Results: 1, 2 Results: 1, 1
3番目のF
のある同じ例を使用すると(たとえば、assert(new F(i:3));
)、9つの行が出力されます。また同時に、3番目の語句&& fact F F3
が追加されると、27の行が出力されます。
個別のファクトについてすべての組合せおよび順序を見つけようとしている場合は、例C-4に示すように、テスト内に追加語句が必要になります。
例C-4 ファクトFのすべての組合せの検索
rule r1 { if (fact F F1 && fact F F2 && F1 != F2) { println("Results: " + F1.i + ", " + F2.i); } }
上のコードにより、次の出力が生成されます。
Results: 2, 1 Results: 1, 2
ファクトのすべての組合せを検出する最も簡単な方法は、最速ではなく順序も不同ですが、例C-5に示したコードを使用するものです。
例C-5 ファクトFの組合せの検索
rule r1 { if (fact F F1 && fact F F2 && id(F1) < id(F2)) { println("Results: " + F1.i + ", " + F2.i); } }
例C-5に示した関数id()
をテスト・パターンで実行すると直接比較よりも時間がかかります。各オブジェクトで一意の値に対してテストするのが最も速い方法です。たとえば、整数値プロパティoidを、クラスの各インスタンスの固有値を割り当てられたクラスに追加できます。
例C-6は、oid値を使用する同じルールを示しています。
例C-6 高速の完全比較
rule r1 { if (fact F F1 && fact F F2 && F1.oid < F2.oid) { println("Results: " + F1.i + ", " + F2.i); } }
この問題は、次に示した関数を使用して、Oracle Rules Engineからすべての重複するファクトを削除しようとした場合にも生じることがあります。
rule rRemoveDups { if (fact F F1 && fact F F2 && F1.i == F2.i) { retract(F2); } }
ただしこのルールでは、重複するファクトだけでなく、タイプF
のすべてのファクトが削除されます。F1
とF2
が同じファクト・インスタンスの可能性があるからです。次の例に、このルールの正しいバージョンを示します。
rule rRemoveDups { if (fact F F1 && fact F F2 && F1 != F2 && F1.i == F2.i) { retract(F2); } }
Oracle Rules Engineは、Java PropertyChangeListener
設計パターンをサポートしています。これにより、PropertyChangeSupport
クラスを使用するJavaファクトのインスタンスは、プロパティ値が変更されたときに自動的にOracle Rules Engineに通知できます。Javaファクトは、Oracle Rules Engineで使用するこのパターンの実装に必須ではありません。
通常、Oracle Rules Engineに対して以前アサートされたJavaオブジェクトのプロパティ値が変更された場合は、新しいプロパティ値でルールを再評価するため、オブジェクトを再アサートする必要があります。PropertyChangeEvent
を起動するプロパティの場合、これらのプロパティの値を変更すると、値が変更されて、ファクトがOracle Rules Engineに対して再アサートされます。
PropertyChangeListener
設計パターンをクラスに実装する手順は次のとおりです。
このパッケージをクラスにインポートします。
import java.beans.PropertyChangeSupport;
プライベート・メンバー変数をクラスに追加します。
private PropertyChangeSupport m_pcs = null;
コンストラクタで、PropertyChangeSupport
オブジェクトを作成します。
m_pcs = new PropertyChangeSupport(this);
次の各setterに対して、firePropertyChange
へのコールを追加します。
public void setName( String name ){ String oldVal = m_name; m_name = name; m_pcs.firePropertyChange( "name", oldVal, m_name ); }
addPropertyChangeListener
メソッドを実装します(m_pcs
に委任します)。
public void addPropertyChangeListener(PropertyChangeListener pcl){ m_pcs.addPropertyChangeListener( pcl ); }
removePropertyChangeListener
メソッドを実装します(m_pcs
に委任します)。
public removePropertyChangeListener(PropertyChangeListener pcl){ m_pcs.removePropertyChangeListener( pcl ); }
アプリケーションの設計について、変更されたオブジェクトを常に明示的に再アサートするか、またはPropertyChangeListener
設計パターンを実装するかを決定する場合は、次の点を考慮してください。
変更されたオブジェクトを明示的に再アサートすると、ユーザーは、一部のプロパティ変更をグループ化して、これらを一度にルールに表示できます。これは、並行スレッドがルールを実行しているときに最も便利であり、ルールにはプロパティ変更の完全なグループのみが表示されます。
明示的なアサートによって、複数のプロパティが変更された場合に、ルール再評価の計算コストが削減されます。複数のプロパティが同時に変更されると、ファクト・タイプを参照するルール条件の複数の再評価が生じます。これは、各プロパティ変更イベントによってオブジェクトの再アサートが生じるためです。PropertyChangeListener
パターンのかわりに明示的なアサートを使用すると、この余分な計算コストを除去できます。
明示的なアサートは、その条件でもテストされるファクトをルールが変更する場合に必要ですが、ガード条件プロパティが設定される前にPropertyChangeListener
で起動される自動再アサートでは、ルールがそれ自体を無限に再起動します。
明示的なアサートは、Oracle RLファクトおよびXMLファクトを変更する場合に使用する必要があります。これは、PropertyChangeListener
設計パターンをサポートするように、これらを定義できないためです。
PropertyChangeListener
対応ファクトを使用すると、Javaアプリケーションは、アプリケーションで明示的アサートを実行するように変更しなくても、プロパティ変更をルール・エンジンに伝達できます。これはつまり、オブジェクトのプロパティを変更するコードが、範囲内のRuleSession
オブジェクトへの参照を持つ必要がないということでもあります。
PropertyChangeListener
サポートは、プロパティ変更後のファクトの再アサートを無視するという一般的なエラーを防止します。
BPELプロセスでBusiness Rulesを使用する場合、いくつかの制限があります。
次のような制限があります。
デシジョン・サービスへの入力として指定できるのは、参照可能なXMLファクト・タイプのみです。
デシジョン・サービスの出力として指定できるのは、参照可能なXMLファクト・タイプのみです。
他の制限は、「デシジョン・サービスの入力と出力の要素タイプはどのように制限されますか。」を参照してください。
XMLファクト・タイプの参照可能オプションの設定の詳細は、「XMLファクトの使用」を参照してください。
Oracle BPEL PMが起動できるのは、デシジョン・サービスとして公開されたデシジョン関数のみであるため、デシジョン関数の入力および出力はXMLファクト・タイプである必要があります。
入力XMLファクトをJavaファクトに変換する場合は、Javaファクト・タイプを使用する既存のルールセットまたはデシジョン関数を使用できます。たとえば、convertFromXML
というルールセットになんらかのルールを作成し、このルールセットをデシジョン関数ルールフロー内でJavaルールセットの前に挿入できます。同様に、Javaファクトを出力XMLファクトに変換するルールセットを作成し、このルールセットをデシジョン関数ルールフロー内でJavaルールセットの後に挿入できます。
また、ルールでJavaファクト・タイプのプロパティのみを使用し、メソッドやフィールドを使用しない場合は、次の手順でJavaファクト・タイプをXMLファクト・タイプで置換できます。
Javaファクト・タイプを削除します(最初にファクト・タイプおよびプロパティの別名を慎重にメモしてください)。
類似するXMLファクト・タイプをインポートし、そのファクト・タイプおよびプロパティの別名を、削除したJavaファクト・タイプおよびプロパティと同じになるように編集します。
BPELデシジョン・サービスに対するルールセット実行中のデバッグ出力を有効化するには、SOAルール・ロガーを有効化します。SOAルール・ロガーがTRACE
レベルに設定されていると、watchAll
の出力がSOA診断ログに記録されます。Fusion Middleware Controlコンソールを使用してロギング・レベルを変更した場合、指定のレベルを使用するようにアプリケーションを再デプロイする必要はありません。
SOA oracle.soa.service.rulesおよびoracle.soa.services.rules.obrtraceロガーの使用方法の詳細は、『Oracle SOA SuiteおよびOracle Business Process Management Suiteの管理』を参照してください。
Oracle Business Rulesでは、バージョニングがサポートされています。
次の2つの方法が考えられます。
デザインタイムには、ディクショナリはXMLファイルとしてJDeveloperプロジェクトに格納されます。ディクショナリは、他のソース・ファイルと同じ方法を使用して、ソース・コントロール・システム内でバージョニングできます。
実行時には、ディクショナリはMDSに格納されます。MDSがデータベースでバックアップされる場合、バージョニングはMDSを使用してサポートされます。
注意: サーバー・アプリケーションは、ディクショナリ変更がMDS内で参照可能になると、その変更にレスポンスできます。ルール・サービス・エンジン(デシジョン・サービス)では、これが自動的に実行されます。非SCAアプリケーションの場合、これはRuleRepositoryインタフェースを使用して実行できます。この時点では、ドラフト・バージョンをサポートするには、MDSのサンドボックス機能を使用します。Oracle Business RulesのRuleRepositoryインタフェースでは、これがサポートされています。
ルールおよびデシジョン表の優先度は最高から最低までで、優先度の高いルールまたはデシジョン表が先に実行されます。たとえば、優先度1から4のルールを作成すると、各ルールは4、3、2、1という実行優先度の順に実行されます。
ルール・デザイナを使用して、事前定義済の名前が付いた優先度のリストから選択するか、正または負の整数を入力して独自の優先度レベルを指定できます。デフォルトの優先度はmedium
(整数値0)です。詳細は、「ルールの優先度の設定方法」を参照してください。
ただし、ルールの純粋な宣言モデルが損なわれるため、できるだけ優先度の使用を避ける必要があることに注意してください。多数の優先度を使用していることに気づいた場合の最善策は、通常、競合を避けるようにルールおよびテストを再構築するか、特定の順序で実行することを意図する場合はルールフローを使用してルールを複数のルールセットに分割することです。競合は、ルールセット内の複数のルールが起動可能な場合に発生します。たとえば、gold customerルールでは$1000を超える支払がある顧客をgold customerとし、silver customerルールでは$500を超える支払がある顧客をsilver customerとする場合、顧客が$1100を支払うと競合が発生します。$500から$1000の間の支払の顧客をテストする場合、ルールに優先度を設定するよりも、silver customerルールを変更する方が明解です。こうした競合分析や競合回避は、デシジョン表を使用すると非常に簡単にできます。デシジョン表の詳細は、「デシジョン表の使用」を参照してください。
ルールセットの順序を変更するには、ルールフローを使用します(これはルールセット・スタックです)。ルールセット・スタックの使用の詳細は、『Oracle Business Process Managementルール言語リファレンス』を参照してください。
JAXB 2.0仕様によると、minOccurs="0"
およびnillable="true"
の属性がある要素のデフォルトの型マッピングは、JAXBElement<
T>
です。このTは、その要素に対して定義された型のデフォルト・マッピングです。たとえば、xsd:string
はJAXBElement<String>
に、xsd:int
はJAXBElement<Integer>
に、xsd:integer
はJAXBElement<BigInteger>
にマップされます。
これは、nillable="true"
が、ドキュメント内でminOccurs=0
を使用して定義されていない(定義する必要はない)要素と、定義されているがnil="true"
という属性を持つ要素の間のセマンティクスの相違が定義済であることを意味しているからです。これはわずかな相違で、不明な値と「値なし」として認識される値との相違を定義するためによく使用されます。
ルールでJAXBElementタイプのプロパティを使用するには、そのプロパティが非NULLかどうかを最初にチェックする必要があります。その後、"value"プロパティまたはgetValue()
メソッドを使用して、基礎となるタイプの値を取得できます。
fact FactType1 && FactType1.prop1 != null && FactType1.prop1.value == "abc"
または、nillable要素がタイプJAXBElement<
T>
ではなくタイプTにマップされるように、カスタマイズしたJAXBバインディングを定義できます。ただし、これでは存在しない要素およびnil要素の違いを判別できなくなるため、無駄な変換です。この場合、nillable属性はあまり役立ちませんが、JavaでObjectタイプのフィールドがデフォルトでNULLに初期化されたり、明示的にNULLに初期化できるのと同様に、文書では要素をnilとして明示的に定義できます。
複合属性は複数の方法で指定できます。いずれの場合も、これらの属性を最上位レベルのxsd:schema
要素の開始タグに追加します。
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" jaxb:version="2.0"
すべてのプロパティでバインディングを使用するように指定するには、これをxsd:schemaの開始タグ内の先頭に追加します。
<xsd:annotation> <xsd:appinfo> <jaxb:globalBindings generateElementProperty="false"/> </xsd:appinfo> </xsd:annotation>
特定のプロパティでのみバインディングを使用するように指定するには、必要な各要素に次のような注釈を追加します。
<xsd:element name="stringElement2" type="xsd:string" minOccurs="0" nillable="true"> <xsd:annotation> <xsd:appinfo> <jaxb:property generateElementProperty="false" /> </xsd:appinfo> </xsd:annotation> </xsd:element>
外部カスタマイズ・ファイルに定義を追加し、スキーマをデータ・モデルに追加する際に引数として渡します。この方法は、SchemaBrowserクラスをプログラムでコールし、ルール・デザイナで公開しない場合にのみ可能です。
SCA-INF/classesディレクトリでコンパイルされたクラスはインポートしないでください。
このディレクトリのクラスを変更すると、データ・モデルにリロードできなくなります。
Rules SDKコードを使用すると、null値を含めることができます。
次のRules SDKコードを使用できます。
SimpleTest test = pattern.getSimpleTestTable().add();
test.getLeft().setValue(attr); test.setOperator(Util.TESTOP_NE); test.getRight().setValue("null");
Oracle Fusion Middleware 11gリリース1 (11.1.1)のOracle Business Rulesでは、ディクショナリを格納するためにWebDAV (Web Distributed Authoring and Versioning)リポジトリはサポートされていません。Oracle Business Rulesでは、MDS(ファイルまたはデータベースを使用)リポジトリを使用したディクショナリの格納がサポートされています。
ルール・デザイナと、CVSまたはSubversionなどのソース制御システムを使用する場合には、特別な注意事項があります。ソース・コード制御システムとルール・デザイナを使用する場合には、プロジェクト内のルール・ディクショナリ・ファイルがテキスト・ファイルではなく、バイナリ・ファイルとして認識されるように指定する必要があります。ルール・ディクショナリ・ファイルはXML文書で、デフォルトで、ソース・コード制御システムはこれらのファイルをテキスト・ファイルとして処理します。
しかし、ルール・ディクショナリ・ファイルには、セマンティク構造が含まれているので、これらのファイルをマージすることはできません。ルール・ディクショナリ・ファイルがテキスト・ファイルとして処理され、変更された場合、ソース制御システムはトリビアル・マージによってこのファイルのマージを試行します。トリビアル・マージを使用すると、セマンティクスの無効なディクショナリ・ファイルが作成され、RuleDictionaryオブジェクトにアンマーシャリングできなくなります。
したがって、ソース・コード制御システムとルール・ディクショナリ・ファイル(.rulesファイル)を使用する場合には、必ずソース・コード制御システムがこれらのファイルをバイナリ・ファイルとして処理するように指定する必要があります。このシステムがディクショナリ・ファイルをバイナリ・ファイルとして処理するように指定するには、いくつかの構成オプションを設定する必要があります。たとえば、Subversionソース・コード制御システムでは、svn:mime-type
ファイル・プロパティを使用してMIMEタイプを設定できます。詳細は、次の章を参照してください。
バイナリ・ファイル・タイプを指定するためのソース・コード制御システム・オプションを設定すると、ソース・コード制御システム(tortoiseSVNなど)が、ルール・ディクショナリ・ファイルをバイナリ・ファイルとして正しく処理できるようになります。