この付録では、Oracle Business Rulesに関するよくある質問を紹介します。
付録C.1「JavaオブジェクトをファクトとしてアサートしてからModifyアクションを使用せずに変更すると、ルールが起動しないのはなぜですか。」
付録C.6「Oracle Business Rulesでデシジョン・サービスを使用する場合にどのような制限がありますか。」
Javaオブジェクトをアサートした後でModifyアクションを使用せずに変更した場合は、オブジェクトをRules Engineに再アサートする必要があります。 したがって、変更したJavaオブジェクトに関連付けられたルールが起動しない場合は、Rules Engineでルール条件が再評価されておらず、ルールもアクティブ化されていないことを意味します。 このため、Modifyアクションを使用せずにJavaオブジェクトを変更した場合には、オブジェクトをRules Engineに再アサートする必要があります。
Oracle Business Rules RL LanguageとJavaの相違の詳細は、『Oracle Fusion Middleware Oracle Business Rulesランゲージ・リファレンス・ガイド』の付録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つのスレッドの実行は、例C-1に示すように進むことがあります。
例C-1 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);
例C-1では、Thread-1がアサートした2つのファクトの両方が、run
へのコール中にRuleSession
に存在することはありません。また、1つのスレッドのみがrun
メソッドをコールすることに注意してください。 共有RuleSession
で複数のスレッドからrun
をコールできるデザインを使用すると、非常に見つけにくい不具合が作成される可能性があり、パフォーマンス上の利点はありません。
共有RuleSession
オブジェクトに対するすべてのアクセスを同期化して、意図した動作を保証する必要があります。ただし、RuleSession
インスタンスが例外をスローして回復不能になることがあるため、このオブジェクトを同期化オブジェクトとしては使用しないでください。かわりに、別の共有オブジェクトを同期点として使用します。
RuleSession
で使用する共有サーバー・プロセスのプロデューサ/コンシューマ・モデルを想定できます。このモデルでは、複数のスレッドが共有RuleSession
に対してファクトをアサートし、1つのスレッドが定期的にrun
をコールし、結果を読み取ってそれらを出力します。これにより、スレッドの競合が生じる可能性はなくなります。これは、2つのコード・セグメントを順次実行する必要があり、混在させることができないためです。 たとえば、例C-2、例C-3および例C-4にあるような、共有オブジェクトに関するコード、プロデューサ・コードおよびコンシューマ・コードです。
例C-3 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-4 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のランタイム動作で極端な結果が生成されることがあります。
例C-5のOracle RLコードについて考えます。
例C-5 ファクトFを使用した自己結合
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();
例C-5で行がいくつ出力されるか考えます。 f1
とf2
の両方については、同じファクト・インスタンスが一致できるため、答えは4行です。
したがって、例C-5では次の出力が生成されます。
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-6に示すように、テスト内に追加語句が必要になります。
例C-6 ファクトFのすべての組合せの検索
rule r1 { if (fact F F1 && fact F F2 && F1 != F2) { println("Results: " + F1.i + ", " + F2.i); } }
例C-6のコードでは、次の出力が生成されます。
Results: 2, 1 Results: 1, 2
ファクトのすべての組合せを検出する最も簡単な方法は、最速ではなく順序も不同ですが、例C-7に示したコードを使用するものです。
例C-7 ファクトFの組合せの検索
rule r1 { if (fact F F1 && fact F F2 && id(F1) < id(F2)) { println("Results: " + F1.i + ", " + F2.i); } }
例C-7に示した関数id()
をテスト・パターンで実行すると直接比較よりも時間がかかるため、各オブジェクトで一意の値に対してテストするのが最も速い方法になります。たとえば、整数値プロパティoidを、クラスの各インスタンスの固有値を割り当てられたクラスに追加できます。
例C-8は、oid値を使用する同じルールを示しています。
例C-8 高速の完全比較
rule r1 { if (fact F F1 && fact F F2 && F1.oid < F2.oid) { println("Results: " + F1.i + ", " + F2.i); } }
この問題は、例C-9に示した関数を使用して、Oracle Rules Engineからすべての重複するファクトを削除しようとした場合にも生じることがあります。
例C-9 重複するファクトを取り消す不正な例
rule rRemoveDups { if (fact F F1 && fact F F2 && F1.i == F2.i) { retract(F2); } }
ただし、このルールは重複のみでなくタイプF
のすべてのファクトを削除します。これは、F1
およびF2
が同じファクト・インスタンスである場合があるためです。 例C-10は、このルールの正しいバージョンを示しています。
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ファクト・タイプのみです。
他の制限は、付録D「デシジョン・サービスの入力と出力はどのように制限されますか。」を参照してください。
XMLファクト・タイプの参照可能オプションの設定の詳細は、第3.2項「XMLファクトの使用」を参照してください。
ディクショナリ名またはディクショナリ・パッケージ名を変更するには、Jdeveloperのオプション「ファイル」→「名前の変更」を使用します。「リファクタ」→「名前の変更」は使用しないでください。「リファクタ」オプションは、ディクショナリやディクショナリ・パッケージには適用されません。
詳細は、第2.2.5項「ディクショナリまたはディクショナリ・パッケージの名前の変更方法」および付録D.3「ディクショナリまたはディクショナリ・パッケージの名前の変更」を参照してください。
実際、Javaコードをルールに挿入することはありません。 ただし、ルール条件またはアクションからJavaメソッドを起動できます。
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 Fusion Middleware Oracle SOA Suite管理者ガイド』を参照してください。
Oracle Business Rulesでは、バージョニングが次の2つの方法でサポートされています。
デザインタイムには、ディクショナリはXMLファイルとしてJDeveloperプロジェクトに格納されます。ディクショナリは、他のソース・ファイルと同じ方法を使用して、ソース・コントロール・システム内でバージョニングできます。
実行時には、ディクショナリはMDSに格納されます。MDSがデータベースでバックアップされる場合、バージョニングはMDSを使用してサポートされます。
注意: サーバー・アプリケーションは、ディクショナリ変更がMDS内で参照可能になると、その変更にレスポンスできます。ルール・サービス・エンジン(デシジョン・サービス)では、これが自動的に実行されます。非SCAアプリケーションの場合、これはRuleRepositoryインタフェースを使用して実行できます。この時点では、ドラフト・バージョンをサポートするには、MDSのサンドボックス機能を使用します。Oracle Business RulesのRuleRepositoryインタフェースでは、これがサポートされています。
優先度は最上位から最下位の順序で、優先度の高いルールまたはルールセットから先に実行されます。たとえば、優先度1から4のルールを作成すると、各ルールは4、3、2、1という実行優先度の順に実行されます。
ただし、ルールの純粋な宣言モデルが損なわれるため、できるだけ優先度の使用を避ける必要があることに注意してください。 多数の優先度を使用していることに気づいた場合の最善策は、通常、ルール間の依存性を表すguard句を使用するためルールを試行して再構築し、特定の順序で実行することを意図する場合はルールを複数のルールセットに分割することです。詳細は、第4.5.5項「ルールの優先度の設定方法」を参照してください。
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クラスをプログラムでコールし、Rules Designerで公開しない場合にのみ可能です。
SCA-INF/classesディレクトリでコンパイルされたクラスはインポートしないでください。 このディレクトリのクラスを変更すると、データ・モデルにリロードできなくなります。
次のRules SDKコードを使用すると、null値を含めることができます。
SimpleTest test = pattern.getSimpleTestTable().add(); test.getLeft().setValue(attr); test.setOperator(Util.TESTOP_NE); test.getRight().setValue("null");