| Oracle® Fusion Middleware Oracle Application Development FrameworkによるFusion Webアプリケーションの開発 12c (12.2.1.1.0) E77397-02 |
|
![]() 前 |
![]() 次 |
この章の内容は次のとおりです。
フレームワーク・ベースの開発の強力な機能の1つは、基本フレームワークを拡張して、組込み機能の動作を変更したり、すべてのアプリケーションが使用できる新機能を追加したりできることです。
ADFビジネス・コンポーネント・フレームワークのベース・クラスを拡張して、フレームワーク内のすべてのタイプのコンポーネントにカスタム・コードを組み込んだり、ADFビジネス・コンポーネント・フレームワークの動作を拡張できます。
カスタマイズを行わずに使用した場合、ビジネス・コンポーネントは、XMLドキュメントで完全に定義されており、コンポーネントのカスタムJavaコードやJavaクラス・ファイルをまったく必要とせず、十分な機能を備えています。ADFビジネス・コンポーネントのコンポーネントの組込み機能を拡張する必要がない場合、また、組込みイベントを処理するカスタム・コードを記述する必要がない場合、このXMLのみの方法でコンポーネントを使用できます。ただし、ADFビジネス・コンポーネント・フレームワークを拡張する場合も、JDeveloperでXMLドキュメントを操作できます。
フレームワーク拡張クラスを作成した後は、ベース・クラスではなくカスタマイズしたフレームワーク・クラスに基づいて、新しいビジネス・コンポーネントを作成できます。また、いつでも既存のコンポーネントの定義を更新して、新しいフレームワーク拡張クラスを使用するようにできます。
ADFビジネス・コンポーネント・フレームワークを使用する前に、他のOracle ADF機能について理解しておくと役立つ場合があります。次に、関連する他の機能へのリンクを示します。
再利用可能なライブラリを作成してフレームワーク拡張レイヤー・クラスのパッケージ化を容易にする場合の詳細は、「アプリケーション・コンポーネントの再利用」を参照してください。
Oracle Metadata Services (MDS)のカスタマイズ機能を使用して顧客によるカスタマイズとその後のデプロイが可能なアプリケーションを作成する場合の詳細は、「MDSによるアプリケーションのカスタマイズ」を参照してください。
カスタム・クラスでよく記述、使用またはオーバーライドするコードのクイック・リファレンスは、「ADFビジネス・コンポーネントのよく使用されるメソッド」を参照してください。
oracle.jboパッケージに関連するAPIのドキュメントについては、次のJavadocリファレンス・ドキュメントを参照してください。
Oracle ADFモデルJava APIリファレンス
ADFビジネス・コンポーネント・フレームワーク拡張クラスは、次のことを目的としてユーザーが作成する、フレームワークの基本クラスの1つを拡張するJavaクラスです。
追加の汎用的な機能で組込み機能を補強する
組込み機能の動作方法を変更する
検出された不具合を一般的な方法で回避する
アプリケーション固有のビジネス・コンポーネントの開発を始める前に、フレームワーク拡張クラスの完全なレイヤーを作成し、そのレイヤーをデフォルトで使用するようプロジェクト・レベルで設定することを検討することをお薦めします。当初はこれらのフレームワーク拡張クラスにカスタム・コードを追加することを想定していないかもしれませんが、このような設定は実際にカスタマイズが必要になった場合に役に立ちます。
これにより、新しい汎用機能や、組込み機能の補強や、不具合の一般的な回避といったことが、プロジェクトの途中ですべてのエンティティ・オブジェクトで必要になった場合でも、大きな不都合が起きることを回避できます。
注意:
この章の例を試すには、「SummitADF_Examplesワークスペースからのスタンドアロン・サンプルの実行」の説明に従って、SummitADF_Examplesワークスペースを使用します。Summit ADFスタンドアロン・サンプル・アプリケーションを取得およびインストールする方法の詳細は、「Oracle ADF用Summitサンプル・アプリケーションの設定」を参照してください。
カスタム・コードを追加してADFビジネス・コンポーネント・フレームワークの基本機能を拡張する必要がある場合は、作成するビジネス・コンポーネントの任意の主要タイプに対し、カスタムJavaクラスを有効にできます。コンポーネントのカスタム・クラスの生成は、JDeveloperの対応する概要エディタの「Java」ページで有効にします。このオプションを有効にすると、JDeveloperにより、構成可能な命名の標準に準拠したコンポーネントに関連するカスタム・クラスのJavaソース・ファイルが作成されます。このクラスの名前は、コンポーネントのXMLドキュメントに記録され、コンポーネントで必要とされるカスタムJavaコードは、このクラス内に記述できます。
フレームワーク拡張クラスを作成するには:
「OK」をクリックすると、ユーザーが選択したパッケージ名に対応するプロジェクトのソース・パスのディレクトリに、カスタム・フレームワーク拡張クラスが作成されます。
注意:
一部のADFビジネス・コンポーネント・クラスは、サーバー側とリモート・クライアント・バージョンの両方に存在します。たとえば、JDeveloperの「クラス・ブラウザ」を使用して、「検索」タブの「クラス名を一致」フィールドにApplicationModuleImplと入力すると、結果のリストでは、oracle.jbo.serverパッケージとoracle.jbo.client.remoteパッケージに2つのApplicationModuleImplクラスが表示されます。フレームワーク拡張クラスを作成するときは、oracle.jbo.serverパッケージのベースADFビジネス・コンポーネント・クラスを使用します。
新しいフレームワーク拡張クラスを作成しても、アプリケーションはそれを自動的には使用しません。フレームワーク拡張クラスを使用するプロジェクトのコンポーネントを指定する必要があります。次の項では、独自のフレームワーク拡張クラスに基づいてビジネス・コンポーネントを作成する方法について説明します。
フレームワーク拡張レイヤー・クラスを再利用可能なライブラリとしてパッケージしやすくするため、これらを使用するプロジェクトとは別のプロジェクトでこれらを作成します。
ユーザーが選択したパッケージ名(com.yourcompany.fwkextなど。それぞれがoracle.jbo.server.*パッケージをインポート)のカスタマイズされたフレームワーク・ベース・クラスの共通セットは、次のクラスで構成されます。
public class CustomEntityImpl extends EntityImpl
public class CustomEntityDefImpl extends EntityDefImpl
public class CustomViewObjectImpl extends ViewObjectImpl
public class CustomViewRowImpl extends ViewRowImpl
public class CustomApplicationModuleImpl extends ApplicationModuleImpl
public class CustomDBTransactionImpl extends DBTransactionImpl2
public class CustomDatabaseTransactionFactoryImpl extends DatabaseTransactionFactory
カスタムのDBTransactionImpl2およびDatabaseTransactionFactoryクラスの使用の詳細は、「カスタム・データベース・トランザクション・クラスを使用するためのアプリケーション・モジュールの構成」を参照してください。
完璧を期すため、次のクラスについてもカスタマイズしたフレームワーク・クラスを作成する場合もありますが、これらのクラスの内容をオーバーライドすることはほとんど必要がないことに注意してください。
public class CustomViewDefImpl extends ViewDefImpl
public class CustomEntityCache extends EntityCache
public class CustomApplicationModuleDefImpl extends ApplicationModuleDefImpl
任意のADFビジネス・コンポーネント・ウィザードまたはエディタの「Java」ページを使用して、ビジネス・コンポーネントのベース・クラスを設定できます。
始める前に:
「フレームワーク拡張クラスの作成方法」の説明に従って、フレームワーク拡張クラスを作成します。
フレームワーク拡張クラスを別のプロジェクトに作成した場合は、ビジネス・コンポーネントを含むプロジェクトの「プロジェクト・プロパティ」ダイアログの「依存性」ページで、「ビルド出力」を選択して、フレームワーク拡張プロジェクトをプロジェクト依存関係として追加します。
フレームワーク拡張クラスをJavaアーカイブ(JAR)ファイルにパッケージする場合は、JARファイルを参照するための名前付きライブラリ定義を作成し、ビジネス・コンポーネントを含むプロジェクトのライブラリ・リストにもそのライブラリを加えます。ライブラリがない場合に作成するには、「ツール」→「ライブラリの管理」メイン・メニュー項目から開くことのできる「ライブラリの管理」ダイアログを使用します。プロジェクトのライブラリ・リストを確認または変更するには、「プロジェクト・プロパティ」ダイアログの「ライブラリ」ページを使用します。
フレームワーク・クラスを参照できることを確認した後、ビジネス・コンポーネントを作成できます。すべてのADFビジネス・コンポーネント・ウィザードおよびエディタの「Java」ページには同じ「クラスの拡張」ボタンが表示されるので、この方法を使用して、新規コンポーネントと既存コンポーネントの両方に対して目的のフレームワーク拡張ベース・クラスを選択できます。
作成するフレームワーク拡張クラスのレベルの数には固定の制限はありません。たとえば、会社で作成するすべてのFusion Webアプリケーションのすべてのアプリケーション・モジュールで使用する会社レベルのCustomAppModuleImplを作成した後、一部のプロジェクト・チームでそのフレームワーク拡張クラスをさらにカスタマイズすることが必要になったとします。そのようなチームでは、CustomAppModuleImplを拡張するSomeProjectCustomAppModuleImplクラスを作成し、プロジェクト固有のカスタム・アプリケーション・モジュールのコードをそこに含めることができます(次の例を参照)。
public class SomeProjectCustomAppModuleImpl
extends CustomAppModuleImpl {
/*
* Custom application module code specific to the
* "SomeProject" project goes here.
*/
}
その後、この特定のプロジェクトの実装の一部として作成されるアプリケーション・モジュールでは、CustomAppModuleImplのかわりにSomeProjectCustomAppModuleImplをベース・クラスとして使用できます。
フレームワーク拡張クラスに基づいてビジネス・コンポーネントを作成するには:
フレームワーク拡張クラスの特定のセットを任意のプロジェクトの標準として使用する場合、「プロジェクト・プロパティ」ダイアログを使用して各コンポーネント・タイプの優先ベース・クラスを定義できます。ベース・クラスに対するこれらの優先指定を設定しても、プロジェクト内の既存コンポーネントには影響ありませんが、コンポーネント・ウィザードが作成する新しいコンポーネントにはこの設定が使用されます。
フレームワーク拡張クラスのプロジェクト・レベルのプリファレンスを定義するには:
JDeveloperで作成するそれぞれの新しいプロジェクトに同じベース・クラス設定を適用する場合、「プリファレンス」ダイアログを使用してグローバル・レベルで設定を定義できます。グローバル・レベルで指定するベース・クラスは、ビジネス・コンポーネントを含む既存のプロジェクトを変更しません。
フレームワーク拡張クラスのグローバル・プリファレンスを定義するには:
作成するビジネス・コンポーネントがカスタムADFビジネス・コンポーネント・フレームワーク拡張クラスを拡張するときは、選択したカスタム・クラス名を反映するように、JDeveloperがXMLドキュメントの定義を更新します。
たとえば、CustomAppModuleImplをカスタム・アプリケーション・モジュール・ベース・クラスにして、com.yourcompany.yourappパッケージにYourServiceアプリケーション・モジュールを作成したものとします。コンポーネントをカスタムJavaファイルのないXMLのみのコンポーネントにした場合、そのXMLドキュメント(YourService.xml)は、次の例のようになります。実行時にAppModuleタグのComponentClass属性の値が読み取られて、コンポーネントを表すために使用するJavaクラスが識別されます。
<AppModule Name="YourService" ComponentClass="com.yourcompany.fwkext.CustomAppModuleImpl" > <!-- etc. --> </AppModule>
図17-4は、XMLのみのYourServiceアプリケーション・モジュールとカスタム拡張クラスの関係を示したものです。実行時には、ApplicationModuleImplクラスから基本動作を継承するCustomAppModuleImplクラスを使用します。
図17-4 拡張フレームワーク・ベース・クラスを参照するXMLのみのコンポーネント

コンポーネントでJavaクラスが必要な場合は、前の項で示したように、コンポーネント・エディタの「Java」ページを開き、適切なチェック・ボックスを選択して有効にします。たとえば、YourServerアプリケーション・モジュールに対してカスタム・アプリケーション・モジュール・クラスを有効にすると、適切なYourServiceImpl.javaクラスが作成されます。次の例に示すように、コンポーネントのXMLドキュメントも更新されて、カスタム・コンポーネント・クラスの名前が反映されます。
<AppModule Name="YourService" ComponentClass="com.yourcompany.yourapp.YourServiceImpl" > <!-- etc. --> </AppModule>
また、次の例に示すように、コンポーネントのカスタムJavaクラスのextends句も変更されて、新しいカスタム・フレームワーク・ベース・クラスが反映されます。
package com.yourcompany.yourapp;
import com.yourcompany.fwkext.CustomAppModuleImpl;
// ---------------------------------------------------------------------
// --- File generated by Oracle ADF Business Components Design Time.
// --- Custom code may be added to this class.
// --- Warning: Do not modify method signatures of generated methods.
// ---------------------------------------------------------------------
public class YourServiceImpl extends CustomAppModuleImpl {
/**This is the default constructor (do not remove) */
public YourServiceImpl() {}
// etc.
}
図17-5は、カスタムYourServiceImplクラスを含むYourServiceアプリケーション・モジュールとフレームワーク拡張クラスの関係を示したものです。実行時には、ベースApplicationModuleImplクラスを拡張するCustomAppModuleImplフレームワーク拡張クラスから基本動作を継承するYourServiceImplクラスを使用します。
図17-5 カスタマイズされたフレームワーク・ベース・クラスを拡張するカスタムJavaのあるコンポーネント

カスタムJavaクラスを含むビジネス・コンポーネントがあり、後でコンポーネントをADFビジネス・コンポーネント・フレームワーク拡張クラスに基づくように変更する場合は、「Javaオプションの選択」ダイアログの「クラスの拡張」ボタンを使用して、コンポーネントのベース・クラスを変更してください。このダイアログは、コンポーネントの概要エディタの「Java」ページから開けます。これを行うと、コンポーネントのXMLドキュメントが新しいベース・クラスを反映して更新され、さらにコンポーネントのカスタムJavaクラスのextends句が変更されます。
注意:
コンポーネント・エディタを使用せずに手動でextends句を更新すると、コンポーネントのXMLドキュメントに新しい継承が反映されず、次にエディタを開いたときに、手動で変更したextends句は、コンポーネント・エディタが正しいコンポーネント・ベース・クラスであるとする値で上書きされます。
フレームワーク拡張レイヤーのクラスを含むJARファイルを作成するには、「デプロイメント・プロファイルの作成 -- JARファイル」ダイアログを使用します。これは、「新規ギャラリ」の「一般」→「デプロイメント・プロファイル」カテゴリで使用できます。
デプロイメント・プロファイルにFrameworkExtensionsのような名前を設定し、「OK」をクリックします。デフォルトでは、JARファイルにはプロジェクト内のすべてのクラス・ファイルが格納されます。これが求める結果であるため、「JARデプロイメント・プロファイルのプロパティ」ダイアログが表示されたら、「OK」をクリックして終了してかまいません。
注意:
ADFライブラリJARアーカイブ・タイプを使用してフレームワーク拡張機能レイヤーをパッケージ化しないでください。再使用可能な構成要素をパッケージ化してJDeveloperリソース・カタログで共有する場合は、ADFライブラリJARファイルを作成します。ビジネス・コンポーネントおよびADFライブラリJARアーカイブ・タイプでの作業の詳細は、「ADFライブラリへの再利用可能なADFコンポーネントのパッケージ化」を参照してください。
最後に、JARファイルを作成するには、「アプリケーション」ウィンドウでプロジェクト・ノードを右クリックし、ポップアップ・メニューから「デプロイ」→YourProfileName→「JARファイルへ」を選択します。JDeveloperの「ログ・ウィンドウ」の「デプロイ」タブに、次のようなフィードバックが表示されます。
---- Deployment started. ---- Feb 14, 2013 1:42:39 PM Running dependency analysis... Wrote JAR file to ...\FrameworkExtensions\deploy\FrameworkExtensions.jar Elapsed time for deployment: 2 seconds ---- Deployment finished. ---- Reb 14, 2013 1:42:41 PM
JDeveloperは、再利用可能なコンポーネント・ライブラリで構成されるJARファイルを編成するための簡便な方法として、名前付きのライブラリを使用します。
フレームワーク拡張JARファイルのライブラリを定義するには:
終了すると、図17-6に示すように、新しいユーザー定義ライブラリFramework Extension Layerが表示されます。この後は、ビジネス・サービスを作成するプロジェクトのライブラリ・リストにこのライブラリを追加することで、優先使用するコンポーネント・ベース・クラスとしてカスタム・フレームワーク拡張クラスが参照されるようになります。
図17-6 フレームワーク拡張レイヤーの新しいユーザー定義ライブラリ

フレームワーク拡張クラスで行う共通のタスクの1つは、カスタム・アプリケーション機能の実装です。フレームワーク拡張コードは特定の種類の全コンポーネントで使用されるように記述するので、これらのクラスで記述するコードでは、通常、一般的な方法でコンポーネントの属性を処理する必要があります。この要件に対処するために、ADFビジネス・コンポーネントではランタイムにコンポーネント・メタデータにアクセスできるようにするAPIを提供します。また、カスタム・メタデータのプロパティとコンポーネントまたは属性を関連付ける機能も提供されています。作成する汎用的なフレームワーク拡張コードでは、ランタイム・メタデータとカスタム・プロパティを使用して、必要な場合には特定のカスタム・プロパティが存在する場合にのみ使用される、一般的な機能を作成できます。
図17-7は、ビュー・オブジェクトおよびエンティティ・オブジェクトに関するランタイム・メタデータにアクセスするためにADFビジネス・コンポーネントで提供されている3つの主要なインタフェースを示しています。ViewObjectインタフェースは、StructureDefインタフェースを拡張します。エンティティ定義を表すクラス(EntityDefImpl)も、このインタフェースを実装します。名前が示すように、StructureDefは構造とコンポーネントを定義しており、ビュー・オブジェクト行またはエンティティ行の各属性に関するランタイム・メタデータを提供するAttributeDefオブジェクトのコレクションへのアクセスを提供します。AttributeDefを使用することで、それに付随するAttributeHintsオブジェクトにアクセスし、表示ラベル、フォーマット・マスク、ツールチップなどのヒントを参照できます。
図17-7 ビュー・オブジェクトおよびエンティティ・オブジェクトで使用できるランタイム・メタデータ

「ビュー・オブジェクトのデフォルト行セットを使用した操作のViewObjectインタフェース・メソッド」で説明したように、読取り専用のビューの場合、findByKey()メソッドおよびsetCurrentRowWithKey組込み操作は、setManageRowsByKey(true)をコールするようにビュー・オブジェクトのcreate()メソッドをオーバーライドした場合にのみ動作します。多くの読取り専用ビュー・オブジェクトを作成する場合、このような詳細を記憶しておくのは大変であるため、ビュー・オブジェクトのフレームワーク拡張クラスで自動化する機能の有力な候補となります。
FrameworkExtensionsプロジェクトには、アプリケーション内の全ビュー・オブジェクトに対するベース・クラスであるSummitViewObjectImplクラスが含まれるものとします。ビュー・オブジェクトに対するこのフレームワーク拡張クラスは、次の例に示すように、ViewObjectImplベース・クラスを拡張し、create()メソッドをオーバーライドして、このタスクを自動化します。実行時にビュー・オブジェクト・インスタンスを作成するとき、super.create()を呼び出してデフォルトのフレームワーク機能を実行した後、このコードは、ビュー・オブジェクトが少なくとも1つの属性がキー属性としてマークされている読取り専用のビュー・オブジェクトかどうかを検査します。そうである場合は、setManageRowsByKey(true)を呼び出します。
isReadOnlyNonEntitySQLViewWithAtLeastOneKeyAttribute()ヘルパー・メソッドは、次の条件の組合せを検査することで、ビュー・オブジェクトが読取り専用かどうかを判定します。
isFullSql()がtrue
このメソッドは、ビュー・オブジェクトの問合せが、含まれるエンティティ・オブジェクトの慣用名に基づいて選択リストを自動的に生成するのではなく、開発者によって完全に指定されている場合は、trueを返します。
getEntityDefs()がnull
このメソッドは、ビュー・オブジェクトのエンティティ・オブジェクト慣用名を表すEntityDefImplオブジェクトの配列を返します。nullを返す場合は、ビュー・オブジェクトにはエンティティ・オブジェクトの慣用名がありません。
次に、getAttributeDefs()メソッドが返すAttributeDef配列をループして、ビュー・オブジェクトにキー属性があるかどうかを判定します。リストのいずれかの属性定義に対してisPrimaryKey()メソッドがtrueを返す場合、そのビュー・オブジェクトにはキーがあります。
public class SummitViewObjectImpl extends ViewObjectImpl {
protected void create() {
super.create();
if (isReadOnlyNonEntitySQLViewWithAtLeastOneKeyAttribute()) {
setManageRowsByKey(true);
}
}
boolean isReadOnlyNonEntitySQLViewWithAtLeastOneKeyAttribute() {
if (getViewDef().isFullSql() && getEntityDefs() == null) {
for (AttributeDef attrDef : getAttributeDefs()) {
if (attrDef.isPrimaryKey()) {
return true;
}
}
}
return false;
}
// etc.
}
アプリケーション・モジュール、ビュー・オブジェクトおよびエンティティ・オブジェクトを作成するときに、これらのビジネス・コンポーネントの概要エディタの「一般」ナビゲーション・タブを選択し、「カスタム・プロパティ」セクションを開くと、任意のコンポーネントのカスタム・メタデータ・プロパティを定義できます。カスタム・メタデータ・プロパティは名前と値のペアであり、これを使用することで、コンポーネントに関する追加の宣言情報を、フレームワーク拡張クラスで作成する汎用コードに伝えることができます。コードでは、getProperty()メソッドを使用することで、特定のカスタム・メタデータ・プロパティの存在または値に基づいて、条件付きで汎用機能を実行できます。
たとえば、SummitViewObjectImplフレームワーク拡張クラスでは、ビュー・オブジェクトのinsertRow()メソッド(次の例を参照)をオーバーライドし、条件付きで、行セットの最後の行として表示される行を挿入しています。このフレームワーク拡張クラスを拡張するビュー・オブジェクトでInsertNewRowsAtEndという名前のカスタム・メタデータ・プロパティが定義されている場合、この汎用コードは末尾への新しい行の挿入を実行します。ビュー・オブジェクトでこのプロパティが定義されていない場合は、insertRow()のデフォルトの動作が実行されます。
public class SummitViewObjectImpl extends ViewObjectImpl {
private static final String INSERT_NEW_ROWS_AT_END = "InsertNewRowsAtEnd";
public void insertRow(Row row) {
super.insertRow(row);
if (getProperty(INSERT_NEW_ROWS_AT_END) != null) {
row.removeAndRetain();
last();
next();
getDefaultRowSet().insertRow(row);
}
}
// etc.
}
コンポーネント・レベルでカスタム・プロパティを定義するだけでなく、ビュー・オブジェクト属性、エンティティ・オブジェクト属性、およびドメインでも、プロパティを定義できます。実行時にこれらのプロパティにアクセスするには、特定の属性に対するAttributeDefインタフェースのgetProperty()メソッドを使用します。
属性の名前、Java型、SQL型、および他の多くの有用な情報を提供するだけでなく、AttributeDefインタフェースのgetAttributeKind()メソッドを使用することで、属性が表す種類を判定することができます。このメソッドは、表17-1で示されているAttributeDefインタフェースのパブリック定数のいずれかに対応するbyte値を返します。
表17-1 エンティティ・オブジェクトとビュー・オブジェクトの属性の種類
| パブリックAttributeDef定数 | 属性の種類の説明 |
|---|---|
|
永続的属性 |
|
一時的属性 |
|
エンティティ・レベルの一時的属性にマップされるビュー・オブジェクト属性 |
|
SQL計算属性 |
|
動的属性 |
|
0以上の |
|
単一の |
フレームワーク拡張クラスの作成に加えて、すべてのコンポーネントがデフォルトで実装できるカスタム・インタフェースも作成できます。クライアント・インタフェースは、たとえばUIクライアントから起動されるアプリケーション・モジュールからメソッドを公開する際に、非常に便利です。ここではアプリケーション・モジュールの例について説明しますが、カスタム拡張ビュー・オブジェクトおよびビュー行インタフェースに対しても同じ機能を作成できます。クライアント・インタフェースの詳細は、「UIクライアントへのカスタム・サービス・メソッドの公開」および「アプリケーション・モジュールのクライアント・インタフェースのプログラム的操作」も参照してください。
ApplicationModuleImplを拡張するCustomApplicationModuleImplクラスがあり、次のような2つのカスタム・メソッドを公開するものとします。
public void doFeatureOne(String arg); public int anotherFeature(String arg);
カスタム拡張インタフェースCustomApplicationModuleを作成し、CustomApplicationModuleImplクラスでそれを実装するには、次の手順を実行します。
アプリケーション・モジュール・コンポーネントでグローバルに公開するメソッドを含むカスタム・インタフェースを作成します。このシナリオでは、このインタフェースは次のようになります。
package devguide.advanced.customintf.fwkext;
/**
* NOTE: This does not extend the
* ==== oracle.jbo.ApplicationModule interface.
*/
public interface CustomApplicationModule {
public void doFeatureOne(String arg);
public int anotherFeature(String arg);
}
このインタフェースがoracle.jbo.ApplicationModuleインタフェースを拡張していないことに注意してください。
CustomApplicationModuleImplアプリケーション・モジュール・フレームワーク拡張クラスを変更し、この新しいCustomApplicationModuleインタフェースを実装します。
package devguide.advanced.customintf.fwkext;
import oracle.jbo.server.ApplicationModuleImpl;
public class CustomApplicationModuleImpl
extends ApplicationModuleImpl
implements CustomApplicationModule {
public void doFeatureOne(String arg) {
System.out.println(arg);
}
public int anotherFeature(String arg) {
return arg == null ? 0 : arg.length();
}
}
プロジェクトを再ビルドします。
ADFビジネス・コンポーネントの概要エディタでは、インタフェースが正常にコンパイルされた後にのみインタフェースを認識できます。
CustomApplicationModuleImplクラスを実装すると、新しいアプリケーション・モジュールを作成できます。このモジュールはグローバル拡張インタフェースを公開し、カスタム・フレームワーク拡張クラスに基づくものです。この作成には、アプリケーション・モジュールの概要エディタを使用します。
カスタム・アプリケーション・モジュールを作成するには:
「クライアント・インタフェースの編集」ダイアログを終了してアプリケーション・モジュールの概要エディタに戻ると、JDeveloperによりアプリケーション・モジュール・カスタム・インタフェースが生成されます。たとえば、カスタム・インタフェースProductModuleにより、次のように、ApplicationModule基本インタフェースとCustomApplicationModule拡張インタフェースの両方が自動的に拡張されます。
package devguide.advanced.customintf.common;
import devguide.advanced.customintf.fwkext.CustomApplicationModule;
import oracle.jbo.ApplicationModule;
// ---------------------------------------------------------------------
// --- File generated by ADF Business Components Design Time.
// ---------------------------------------------------------------------
public interface ProductModule
extends CustomApplicationModule, ApplicationModule {
void doSomethingProductRelated();
}
これが済むと、クライアント・コードは、ProductModuleアプリケーション・モジュールをCustomApplicationModuleインタフェースにキャストし、それに含まれる汎用拡張メソッドを強く型付けされた方法で呼び出すことができます。
注意:
ViewObjectImplフレームワーク拡張クラスおよびViewRowImpl拡張クラスのメソッドを公開する場合も、基本的な手順は同じです。
ビジネス・コンポーネント用のカスタムJavaクラスにコードを記述し、データベースのストアド・プロシージャとストアド・ファンクションを呼び出すことができます。ここでは、PL/SQLパッケージのプロシージャとファンクションに基づく簡単な例をいくつか示しますが、同じ手法を使用して、パッケージの一部ではないプロシージャやファンクションを呼び出すこともできます。
次の例のPL/SQLパッケージについて考えてみます。
create or replace package invokestoredprocpkg as procedure proc_with_no_args; procedure proc_with_three_args(n number, d date, v varchar2); function func_with_three_args(n number, d date, v varchar2) return varchar2; procedure proc_with_out_args(n number, d out date, v in out varchar2); end invokestoredprocpkg;
以降の項では、このパッケージのプロシージャおよびファンクションの各例を呼び出す方法について説明します。
注意:
この項の例では、SummitADF_Examplesアプリケーション・ワークスペースのoracle.summit.model.invokingstoredprocedureパッケージを参照します。
引数を受け取らないストアド・プロシージャを起動する必要がある場合は、(次の例に示すようにoracle.jbo.serverパッケージ内の)DBTransactionインタフェースのexecuteCommand()メソッドを使用できます。
// In InvokingStoredProcAppModuleImpl.java
public void callProcWithNoArgs() {
getDBTransaction().executeCommand(
"begin invokestoredprocpkg.proc_with_no_args; end;");
}
INモード引数のみを受け取るストアド・プロシージャ(何も指定しない場合のデフォルトのPL/SQLパラメータ・モード)を呼び出すには、JDBC PreparedStatementオブジェクトを使用する必要があります。DBTransactionインタフェースでは、現在のデータベース接続のコンテキストでこのオブジェクトを作成するためのcreatePreparedStatement()メソッドが提供されています。次の例で示すようなヘルパー・メソッドを使用することで、PreparedStatementを使用してこの種のストアド・プロシージャを起動する処理が簡単になります。重要なこととして、ヘルパー・メソッドを使用することにより、実行後にJDBC PreparedStatementを閉じるコードをカプセル化できます。このコードが実行する基本的な処理は次のとおりです。
protected void callStoredProcedure(String stmt, Object[] bindVars) {
PreparedStatement st = null;
try {
// 1. Create a JDBC PreparedStatement for
st = getDBTransaction().createPreparedStatement("begin "+stmt+";end;",0);
if (bindVars != null) {
// 2. Loop over values for the bind variables passed in, if any
for (int z = 0; z < bindVars.length; z++) {
// 3. Set the value of each bind variable in the statement
st.setObject(z + 1, bindVars[z]);
}
}
// 4. Execute the statement
st.executeUpdate();
}
catch (SQLException e) {
throw new JboException(e);
}
finally {
if (st != null) {
try {
// 5. Close the statement
st.close();
}
catch (SQLException e) {}
}
}
}
このようなヘルパー・メソッドを使用した場合、前の例のproc_with_three_argsプロシージャのコールは次のようになります。
// In StoredProcTestModuleImpl.java
public void callProcWithThreeArgs(Number n, Date d, String v) {
callStoredProcedure("callStoredProcedure.proc_with_three_args(?,?,?)",
new Object[]{n,d,v});
}
ファンクションに渡される引数のJDBCバインド変数のプレースホルダとして疑問符が使用されていることに注意してください。JDBCは名前付きのバインド変数の使用もサポートしますが、ヘルパー・メソッドは位置でバインド変数の値を設定するのみであるため、このように単純な位置によるバインド変数を使用しても問題ありません。
INモードの引数のみを受け取るストアド・ファンクションの呼出しでは、文を実行した後でファンクションの結果の値にアクセスするために、JDBC CallableStatementオブジェクトを使用する必要があります。DBTransactionインタフェースでは、現在のデータベース接続のコンテキストでこのオブジェクトを作成するためのcreateCallableStatement()メソッドが提供されています。次の例に示すようにヘルパー・メソッドを使用することで、CallableStatementを使用してこの種のストアド・ファンクションを起動する処理が簡単になります。ヘルパー・メソッドは、使用するJDBC文の作成とクリーン・アップの両方をカプセル化します。
このコードが実行する基本的な処理は次のとおりです。
// Some constants
public static int NUMBER = Types.NUMERIC;
public static int DATE = Types.DATE;
public static int VARCHAR2 = Types.VARCHAR;
protected Object callStoredFunction(int sqlReturnType, String stmt,
Object[] bindVars) {
CallableStatement st = null;
try {
// 1. Create a JDBC CallabledStatement
st = getDBTransaction().createCallableStatement(
"begin ? := "+stmt+";end;",0);
// 2. Register the first bind variable for the return value
st.registerOutParameter(1, sqlReturnType);
if (bindVars != null) {
// 3. Loop over values for the bind variables passed in, if any
for (int z = 0; z < bindVars.length; z++) {
// 4. Set the value of user-supplied bind vars in the stmt
st.setObject(z + 2, bindVars[z]);
}
}
// 5. Set the value of user-supplied bind vars in the stmt
st.executeUpdate();
// 6. Return the value of the first bind variable
return st.getObject(1);
}
catch (SQLException e) {
throw new JboException(e);
}
finally {
if (st != null) {
try {
// 7. Close the statement
st.close();
}
catch (SQLException e) {}
}
}
}
このようなヘルパー・メソッドを使用した場合、前の例のfunc_with_three_argsプロシージャのコールは次のようになります。
// In InvokingStoredProcAppModuleImpl.java
public String callFuncWithThreeArgs(Number n, Date d, String v) {
return (String)callStoredFunction(VARCHAR2,
"invokestoredprocpkg.func_with_three_args(?,?,?)",
new Object[]{n,d,v});
}
ファンクションに渡される引数のJDBCバインド変数のプレースホルダとして疑問符が使用されていることに注意してください。JDBCは名前付きのバインド変数の使用もサポートしますが、ヘルパー・メソッドは位置でバインド変数の値を設定するのみであるため、このように単純な位置によるバインド変数を使用しても問題ありません。
OUTモードまたはIN OUTモードの引数を含むinvokestoredprocpkg.proc_with_out_argsのようなストアド・プロシージャまたはストアド・ファンクションの呼出しでは、前の項の場合と同じようにCallableStatementを使用する必要がありますが、ヘルパー・メソッドへの一般化は少し難しくなります。次の例は、invokestoredprocpkg.proc_with_out_argsプロシージャを起動するために必要なJDBCコードを示しています。
このコードが実行する基本的な処理は次のとおりです。
public DateAndStringBean callProcWithOutArgs(Number n, String v) {
CallableStatement st = null;
try {
// 1. Define the PL/SQL block for the statement to invoke
String stmt = "begin invokestoredprocpkg.proc_with_out_args(?,?,?); end;";
// 2. Create the CallableStatement for the PL/SQL block
st = getDBTransaction().createCallableStatement(stmt,0);
// 3. Register the positions and types of the OUT parameters
st.registerOutParameter(2,Types.DATE);
st.registerOutParameter(3,Types.VARCHAR);
// 4. Set the bind values of the IN parameters
st.setObject(1,n);
st.setObject(3,v);
// 5. Execute the statement
st.executeUpdate();
// 6. Create a bean to hold the multiple return values
DateAndStringBean result = new DateAndStringBean();
// 7. Set value of dateValue property using first OUT param
result.setDateVal(new Date(st.getDate(2)));
// 8. Set value of stringValue property using 2nd OUT param
result.setStringVal(st.getString(3));
// 9. Return the result
return result;
} catch (SQLException e) {
throw new JboException(e);
} finally {
if (st != null) {
try {
// 10. Close the JDBC CallableStatement
st.close();
}
catch (SQLException e) {}
}
}
}
前の例で使用されているDateAndString Beanは、次のような2つのBeanプロパティを持つ簡単なJavaBeanです。
package oracle.summit.model.invokingstoredprocedure;
import java.io.Serializable;
import oracle.jbo.domain.Date;
public class DateAndStringBean implements Serializable {
Date dateVal;
String stringVal;
public void setDateVal(Date dateVal) {this.dateVal=dateVal;}
public Date getDateVal() {return dateVal;}
public void setStringVal(String stringVal) {this.stringVal=stringVal;}
public String getStringVal() {return stringVal;}
}
注意:
カスタム・メソッドを、アプリケーション・モジュールのカスタム・サービス・インタフェースに組み込む正当な候補にできるようにするには(望ましい場合)、Beanがjava.io.Serializableインタフェースを実装する必要があります。これはマーカー・インタフェースであるため、implements Serializableキーワードを追加するのみでよく、インタフェースのメソッドの実装をコーディングする必要はありません。
ADFビジネス・コンポーネントは下位レベルのデータベース・プログラミングの詳細をすべて抽象化しているので、通常は、JDBCのConnectionオブジェクトに直接アクセスする必要はありません。実行時に、異なるWebページ・リクエスト間でまったく同じアプリケーション・モジュール・インスタンスまたはJDBC接続インスタンスをアプリケーションが使用することは保証されません。この種のプールされたサービス環境でJDBC Connectionオブジェクトへの参照を誤って保持すると、実行時に想定外の動作が発生する可能性があるため、設計として、ADFビジネス・コンポーネントにはJDBC Connectionを直接取得するAPIはありません。これは、その直接的な使用および不注意による誤用を防ぐために意図的に行われていることです。
ただし、サード・パーティのコードをADFビジネス・コンポーネントと統合する場合はこれが可能であると便利なため、次の例に示すようなヘルパー・メソッドを使用して接続にアクセスできます。
/**
* Put this method in your XXXImpl.java class where you need
* to access the current JDBC connection
*/
private Connection getCurrentConnection() throws SQLException {
/* Note that we never execute this statement, so no commit really happens */
PreparedStatement st = getDBTransaction().createPreparedStatement("commit",1);
Connection conn = st.getConnection();
st.close();
return conn;
}
注意:
上の例のヘルパー・メソッドを使用して取得したJDBC接続を、独自のコード内のどこにもキャッシュしないでください。かわりに、各接続に対してヘルパー・メソッドをコールすることで、JDBC接続に対する参照を誤って保持し、それが後で別のユーザーによる別のリクエストで使用されるのを防ぐようにします。Oracle ADFランタイム環境のプールされたサービスの性質上、保持している参照を閉じないでください。ただし、文は閉じるようにしてください。
カスタム・メッセージ・バンドルでエラー・コードに対してかわりのメッセージ文字列を提供することにより、組み込まれているADFビジネス・コンポーネントのエラー・メッセージをカスタマイズできます。
注意:
この項の例では、SummitADF_Examplesアプリケーション・ワークスペースのoracle.summit.model.custommessagesパッケージを参照します。
次の組込みエラー・メッセージをカスタマイズするとします。
JBO-27014: Attribute OrderFilled in SOrd is required
Oracle Worldwide SupportからOracle Application Development Framework (Oracle ADF)のソース・コードを入手している場合は、oracle.jboパッケージのCSMessageBundle.javaファイルを見ると、このエラー・メッセージが次の行の組合せと関連していることがわかります。
public class CSMessageBundle extends CheckedListResourceBundle {
// etc.
public static final String EXC_VAL_ATTR_MANDATORY = "27014";
// etc.
private static final Object[][] sMessageStrings = {
// etc.
{EXC_VAL_ATTR_MANDATORY, "Attribute {2} in {1} is required"},
// etc.
}
}
番号付きのトークン{2}および{1}は、エラー・メッセージのプレースホルダです。この例では、実行時に、{l}はエンティティ・オブジェクトの名前に置き換えられ、{2}は属性の名前に置き換えられます。
カスタムのメッセージ・バンドル・ファイルを作成するには、次のようにします。
このメッセージをカスタム・メッセージ・バンドル・ファイルに追加した後、Oracle ADFモデル・テスターを使用してアプリケーションをテストし、必須属性の値を空白にすると、デフォルトではなくカスタムのエラー・メッセージが表示されます。
JBO-27014: You must provide a value for Order Filled
必要なだけいくつでも、メッセージ・バンドルにメッセージを追加できます。エラー・コード・キーが組込みエラー・メッセージ・コードのいずれかと一致するメッセージは、実行時に、oracle.jbo.CSMessageBundleメッセージ・バンドルのデフォルト・メッセージのかわりに使用されます。
ADFビジネス・コンポーネントのエラー・メッセージをカスタマイズする場合、ネストされたエラー・メッセージの表示もカスタマイズする必要があります。そのためには、カスタム・エラー・ハンドラ・クラスを作成して登録する必要があります。
ビジネス・メソッドでエラーがスローされると、ADFモデル・データ・バインディング・レイヤーはエラーを遮断して、登録されたカスタム・エラー・ハンドラ・クラスを呼び出します。一般に、エラー・ハンドラ・クラスは例外を読取り可能になるように書式設定します。このプロセスで、デフォルト・エラー・ハンドラDCErrorHandlerImplは通常、最上位のJboExceptionをスキップします。このオブジェクトは他のビジネス例外のラッパーでビジネス上重要でないためです。
最上位の例外をスキップすることが、ADFビジネス・コンポーネント・エラーの場合の望ましい動作ですが、デフォルトの動作では、SQLExceptionを置換するために設定されたカスタム・メッセージがスキップされます。この状況を回避するために、ネストされた例外の各項目を表示する間、カスタム・エラー・ハンドラ・クラスはDCErrorHandlerImpl::skipException(Exception ex)をオーバーライドして、該当する例外を最終リストのユーザーに表示するかどうかを決定します。
始める前に:
アプリケーション・モジュールの知識があると役立ちます。詳細は、「ビジネス・コンポーネントのエラー・メッセージのカスタマイズ」を参照してください。
他のOracle ADF機能を使用して追加できる機能を理解しておくことも役立ちます。詳細は、「ビジネス・コンポーネントの拡張の追加機能」を参照してください。
次のタスクを完了する必要があります。
プロジェクトのSQLExceptionsのカスタム・メッセージを指定するには:
次の例は、ユーザーに表示されるエラー最終リストへのSQLIntegrityConstraintViolationExceptionの表示をスキップするエラー・ハンドラのカスタム実装を示しています。一意の制約違反や外部キー制約違反などのエラーで生成される、他のデータベース・レベルのエラー・メッセージをスキップすることもできます。
package view;
import java.sql.SQLIntegrityConstraintViolationException;
import oracle.adf.model.BindingContext;
import oracle.adf.model.RegionBinding;
import oracle.adf.model.binding.DCBindingContainer;
import oracle.adf.model.binding.DCErrorHandlerImpl;
import oracle.adf.model.binding.DCErrorMessage;
import oracle.jbo.DMLConstraintException;
import oracle.jbo.JboException;
public class CustomErrorHandler extends DCErrorHandlerImpl {
public CustomErrorHandler() {
super();
}
/**
* If an exception is a RowValException or a TxnValException
* and they have nested exceptions, then do not display
* it.
*/
@Override
protected boolean skipException(Exception ex) {
if (ex instanceof DMLConstraintException) {
return false;
} else if (ex instanceof SQLIntegrityConstraintViolationException) {
return true;
}
return super.skipException(ex);
}
)
データベースに制約を設定している場合、制約の違反が発生したときに、Fusion Webアプリケーションでカスタム・エラー・メッセージをエンド・ユーザーに対して提供する場合があります。たとえば、次の例に示す次のDDL文を使用して、アプリケーションのS_ORD表にS_ORD_ORDER_FILLED_CKという制約を追加するものとします。
alter table s_ord add (
constraint S_ORD_ORDER_FILLED_CK
check (ORDER_FILLED IN ('Y', 'N'))
);
アプリケーションでカスタム・エラー・メッセージを定義するには、制約名をメッセージ・キーとして、カスタム・メッセージ・バンドルにメッセージを追加します。次の例は、前の例で定義したデータベース制約名と一致するキーS_ORD_ORDER_FILLED_CKを持つメッセージを定義した場合のCustomMessageBundle.javaクラスを示しています。
package oracle.summit.model.custommessages;
import java.util.ListResourceBundle;
public class CustomMessageBundle extends ListResourceBundle {
private static final Object[][] sMessageStrings
= new String[][] {
{"27014","You must provide a value for {2}"},
{"S_ORD_ORDER_FILLED_CK", "The order filled value must be Y or N"}
};
protected Object[][] getContents() {
return sMessageStrings;
}
}
カスタム・メッセージをデータベース制約違反に割り当てるデフォルト機能が要件を満たさない場合は、独自のカスタム制約エラー処理ルーチンを実装できます。そのためには、ADFビジネス・コンポーネント・トランザクション・クラスに対するカスタム・フレームワーク拡張クラスを作成し、実行時にそれを使用するようアプリケーション・モジュールを構成する必要があります。
ADFビジネス・コンポーネント・トランザクション用のカスタム・フレームワーク拡張クラスを記述するには、次の例で示すCustomDBTransactionImplのようなクラスを作成します。この例では、スローされるDMLConstraintExceptionエラーに対するカスタム処理を実行するため、トランザクション・オブジェクトのpostChanges()メソッドをオーバーライドして、super.postChanges()の呼び出しをtry/catchブロックでラップしています。この簡単な例で実行しているカスタム処理は、ex.setExceptions(null)を呼び出して、DMLConstraintExceptionが保持している可能性のあるネストされた詳細な例外をクリアすることのみです。かわりに、アプリケーションで必要な他の種類のカスタム例外処理を実行できます。たとえば、カスタム例外がJboExceptionを直接または間接に拡張している場合は、カスタム例外をスローできます。
package oracle.summit.model.custommessages;
import oracle.jbo.DMLConstraintException;
import oracle.jbo.server.DBTransactionImpl2;
import oracle.jbo.server.TransactionEvent;
public class CustomDBTransactionImpl extends DBTransactionImpl2 {
public void postChanges(TransactionEvent te) {
try {
super.postChanges(te);
}
/*
* Catch the DML constraint exception
* and perform custom error handling here
*/
catch (DMLConstraintException ex) {
ex.setExceptions(null);
throw ex;
}
}
}
アプリケーション・モジュールで実行時にカスタム・データベース・トランザクション・クラスを使用するには、次の手順を実行する必要があります。
create()メソッドをオーバーライドするDatabaseTransactionFactoryクラスのカスタム実装を提供します。TransactionFactoryプロパティの値に、このカスタム・トランザクション・ファクトリ・クラスの完全修飾名を構成します。次の例は、これを行うカスタム・データベース・トランザクション・ファクトリ・クラスを示しています。フレームワークがデータベース・トランザクション・ファクトリでcreate()メソッドを呼び出すと、このクラスはCustomDBTransactionImplクラスの新しいインスタンスを返します。
package oracle.summit.model.custommessages;
import oracle.jbo.server.DBTransactionImpl2;
import oracle.jbo.server.DatabaseTransactionFactory;
public class CustomDatabaseTransactionFactory
extends DatabaseTransactionFactory {
public CustomDatabaseTransactionFactory() {
}
/**
* Return an instance of our custom CustomDBTransactionImpl class
* instead of the default implementation.
*
* @return instance of custom CustomDBTransactionImpl implementation.
*/
public DBTransactionImpl2 create() {
return new CustomDBTransactionImpl();
}
}
これを実行するには、アプリケーション・モジュール構成(bc4j.xcfgファイル)の概要エディタの「プロパティ」タブを使用して、TransactionFactoryプロパティに値oracle.summit.model.custommessages.CustomDatabaseTransactionFactoryを割り当てます。アプリケーション・モジュール構成の概要エディタを表示するには、「アプリケーション」ウィンドウでアプリケーション・モジュールをダブルクリックして、概要エディタの「構成」ナビゲーション・タブを選択します。次に、概要エディタの「構成」ページで構成を選択し、構成のハイパーリンクをクリックします。アプリケーション・モジュール構成の概要エディタで「プロパティ」タブを選択し、「プロパティの追加」をクリックして「プロパティの追加」ダイアログから次のプロパティを選択し、「OK」をクリックします。
TransactionFactory
次に、「プロパティ」リストで、TransactionFactoryプロパティの値を入力します。
TransactionFactory = oracle.summit.model.custommessages.CustomDatabaseTransactionFactory
この構成を使用してアプリケーションを実行すると、カスタム・トランザクション・クラスが使用されます。
新しいビジネス・コンポーネントを作成するたびに、必要に応じて、既存のものを拡張してオリジナルのカスタマイズ・バージョンを作成できます。図17-9に示すように、ProductViewExビュー・オブジェクトでは、ProductViewビュー・オブジェクトを拡張してbv_ProductNameという名前の名前付きバインド変数が追加され、このバインド変数を参照するようにWHERE句がカスタマイズされています。
図17-9 別のコンポーネントを拡張できるADFビジネス・コンポーネント

図ではビュー・オブジェクトの例が示されていますが、このコンポーネント継承機能はすべての種類のコンポーネントで使用できます。あるコンポーネントが別のコンポーネントを拡張する場合、拡張されたコンポーネントは親からすべてのメタデータと動作を継承します。拡張されたコンポーネントでは、メタデータとJavaコードの両方を使用して、新しい機能を追加したり、親コンポーネントの既存機能をカスタマイズしたりできます。
注意:
この項の例では、SummitADF_Examplesアプリケーション・ワークスペースのoracle.summit.model.extendパッケージを参照します。
拡張コンポーネントを作成するには、作成するコンポーネントの種類に対する「新規ギャラリ」のコンポーネント・ウィザードを使用します。たとえば、拡張ビュー・オブジェクトを作成するには、「ビュー・オブジェクトの作成」ウィザードを使用します。ウィザードの「名前」ページで、新しいコンポーネントの名前とパッケージを指定することに加えて、拡張するコンポーネントの完全修飾名を「拡張」フィールドで指定します。リストからコンポーネント名を選択するには、「拡張」フィールドの隣の「参照」ボタンを使用します。その後は、ウィザードの残りのパネルを使用して、通常の方法で拡張コンポーネントの作成を続けます。
拡張コンポーネントを定義した後で、拡張コンポーネントが継承している親コンポーネントを変更できます。コンポーネントの概要エディタをこの目的に使用できます。
作成後に親コンポーネントを変更するには:
「アプリケーション」ウィンドウで、コンポーネントをダブルクリックします。
概要エディタで、「一般」ナビゲーション・タブをクリックし、「拡張対象」フィールドの横にある「オブジェクト拡張対象をリファクタします。」ボタンをクリックします。
「親の選択」ダイアログで、パッケージ・リストから拡張する目的のコンポーネントを選択して、「OK」をクリックします。
どの親からも継承しないように拡張コンポーネントを変更するには、「親の選択」ダイアログの「なし」チェック・ボックスを選択します。これは、この目的のためのコンポーネント削除および再作成と同じ結果になります。
ADFビジネス・コンポーネント・データ・モデル・プロジェクトで作成するビジネス・コンポーネントは、XMLドキュメントとオプションのJavaクラスで構成されます。別のコンポーネントを拡張するコンポーネントを作成すると、JDeveloperは、拡張されたコンポーネントのXMLドキュメントと生成されるJavaコードの両方に、このコンポーネント継承を反映します。
拡張コンポーネントを作成すると、拡張コンポーネントは親コンポーネントから属性および属性プロパティを継承します。継承される属性は拡張コンポーネントに定義されておらず、親コンポーネントから渡されるだけであるため、この接続が維持されます。したがって、拡張コンポーネントの作成後に親コンポーネントの属性を変更した場合、拡張コンポーネントの属性はその変更を反映します。
たとえば、ProductViewビュー・オブジェクトを拡張するProductViewEx拡張ビュー・オブジェクトを作成するとします。次に、ProductView親ビュー・オブジェクトでShortDesc属性の「表示」UIヒントを「非表示」に設定することに決めます。この操作を行うと、ShortDesc属性がProductViewEx拡張ビュー・オブジェクトでも非表示になります。
ただし、拡張オブジェクトで属性をオーバーライドした場合、オーバーライドされた属性は拡張オブジェクトに再定義されるため、この接続は切断されます。これにより、親オブジェクトまたはその親オブジェクトから導出された他のオブジェクトに影響を与えずに、拡張オブジェクトに変更を加えることができます。オーバーライドは選択した属性にのみ適用され、他の継承された属性は影響を受けないことに注意してください。
たとえば、ProductViewビュー・オブジェクトと2つの拡張ビュー・オブジェクトProductViewEx1およびProductViewEx2があるとします。ProductView親ビュー・オブジェクトでShortDesc属性の「表示」UIヒントを「非表示」に設定した後、ProductViewEx1拡張ビュー・オブジェクトでこれを「表示」に設定することに決めます。ProductViewEx1でShortDescをオーバーライドし、「表示」UIヒントを「表示」に設定すると、変更はProductViewEx1拡張ビュー・オブジェクトにのみ反映され、ProductViewEx2拡張ビュー・オブジェクトまたはProductView親ビュー・オブジェクトには反映されません。
JDeveloperは、新しいコンポーネントのXMLドキュメントのルート・コンポーネント要素にExtends属性を追加することで、親コンポーネントの名前を記録します。追加された新しい宣言機能、または親コンポーネントの定義のオーバーライドされた部分は、拡張コンポーネントのXMLドキュメントに記述されます。これに対し、親コンポーネントから純粋に継承されたメタデータは、拡張コンポーネントでは繰り返し記述されません。
次の例は、ProductViewExビュー・オブジェクトのProductViewEx.xml XMLドキュメントを示しています。ViewObject要素のExtends属性、拡張されたビュー・オブジェクトで追加されたバインド変数に関連するVariable要素、およびtheProductNameバインド変数を参照するように変更されたWHERE句のWhere属性のオーバーライドされた値に注意してください。
<ViewObject xmlns="http://xmlns.oracle.com/bc4j" Name="ProductViewEx" InheritPersonalization="true" BindingStyle="OracleName" CustomQuery="false" ComponentClass="oracle.summit.model.extend.ProductViewExImpl" RowClass="oracle.summit.model.extend.ProductViewExRowImpl" RowInterface="oracle.summit.model.extend.common.ProductViewExRow" ClientRowProxyName="oracle.summit.model.extend.client.ProductViewExRowClient" ComponentInterface="oracle.summit.model.extend.common.ProductViewEx" ClientProxyName="oracle.summit.model.extend.client.ProductViewExClient" Extends="oracle.summit.model.extend.ProductView" Where="UPPER(PRODUCT_NAME) LIKE UPPER(:theProductName)||'%'" ... <Variable Name="bv_ProductName" Kind="where" Type="java.lang.String"/> ... </ViewObject>
拡張コンポーネントでカスタムJavaコードを有効にすると、親コンポーネントの各Javaクラスを拡張するためのJavaクラスが自動的に生成されます。これにより、拡張コンポーネントでは、必要に応じて、親コンポーネントのプログラム的な動作のすべての面をオーバーライドできます。親コンポーネントが、独自のカスタムJavaクラスを持たないXMLのみのコンポーネントである場合は、拡張コンポーネントのJavaクラスは、親が実行時に使用するベースJavaクラスを拡張します。これは、oracle.jbo.serverパッケージのデフォルトのADFビジネス・コンポーネント・フレームワーク・クラス、または親コンポーネントの「拡張」ダイアログで指定されている場合は独自のフレームワーク拡張クラスなどになります。
さらに、拡張コンポーネントがアプリケーション・モジュールまたはビュー・オブジェクトで、クライアント・インタフェースを有効にしてある場合は、拡張コンポーネントのクライアント・インタフェースが自動的に生成され、親コンポーネントの各クライアント・インタフェースが拡張されます。親コンポーネントのクライアント・インタフェースが存在しない場合は、拡張コンポーネントのクライアント・インタフェースは、oracle.jboパッケージの適切なベースADFビジネス・コンポーネント・インタフェースを直接拡張します。
拡張コンポーネントは親のカスタマイズ・バージョンであるため、親コンポーネントのJavaクラスまたはクライアント・インタフェースで動作するように作成したコードは、親コンポーネントまたはそのカスタマイズ・バージョンで問題なく動作します。
たとえば、次のようなカスタムJavaクラスとクライアント・インタフェースを含むベースProductViewビュー・オブジェクトがあるものとします。
クラスProductViewImpl
行クラスProductViewRowImpl
クライアント・インタフェースProductView
行クライアント・インタフェースProductViewRow
ProductViewを拡張するProductViewExビュー・オブジェクトを作成した場合、ベース・コンポーネントのクラスとインタフェースを使用して、ProductViewとProductViewExの両方を操作できます。
次の例は、ProductView、ProductViewRow、ProductViewExおよびProductViewExRowの各クライアント・インタフェースで動作するテスト用のクライアント・プログラムです。この例については、次のことに注意してください。
親のProductViewインタフェースを、それを拡張するProductViewExビュー・オブジェクトを処理するために使用できます。
別の方法として、ProductViewExビュー・オブジェクトのインスタンスを、独自のさらに限定的なProductViewExクライアント・インタフェースにキャストすることもできます。
キャストしてProductViewExRowインタフェースに固有のメソッドを呼び出す前に、本当に行ProductViewRowがより限定的なProductViewExRowのインスタンスであるかどうかをテストできます。
package oracle.summit.model.extend;
import oracle.jbo.ApplicationModule;
import oracle.jbo.AttributeDef;
import oracle.jbo.Row;
import oracle.jbo.StructureDef;
import oracle.jbo.ValidationException;
import oracle.jbo.ViewObject;
import oracle.jbo.client.Configuration;
import oracle.summit.model.extend.common.ProductView;
import oracle.summit.model.extend.common.ProductViewEx;
import oracle.summit.model.extend.common.ProductViewExRow;
import oracle.summit.model.extend.common.ProductViewRow;
public class TestClient {
public static void main(String[] args) {
String amDef = "oracle.summit.model.extend.AppModule";
String config = "AppModuleLocal";
ApplicationModule am =
Configuration.createRootApplicationModule(amDef,config);
ProductView products = (ProductView)am.findViewObject("ProductView1");
products.executeQuery();
ProductViewRow product = (ProductViewRow)products.first();
printAllAttributes(products,product);
testSomethingOnProductsRow(product);
products = (ProductView)am.findViewObject("ProductViewEx1");
ProductViewEx productsByName = (ProductViewEx)products;
productsByName.setbv_ProductName("bunny");
productsByName.executeQuery();
product = (ProductViewRow)productsByName.first();
printAllAttributes(productsByName,product);
testSomethingOnProductsRow(product);
am.getTransaction().rollback();
Configuration.releaseRootApplicationModule(am,true);
}
private static void testSomethingOnProductsRow(ProductViewRow product) {
try {
if (product instanceof ProductViewExRow) {
ProductViewExRow productByName = (ProductViewExRow)product;
productByName.someExtraFeature("Test");
}
product.setName("Q");
System.out.println("Setting the Name attribute to 'Q' succeeded.");
}
catch (ValidationException v) {
System.out.println(v.getLocalizedMessage());
}
}
private static void printAllAttributes(ViewObject vo, Row r) {
String viewObjName = vo.getName();
System.out.println("Printing attribute for a row in VO '"+
viewObjName+"'");
StructureDef def = r.getStructureDef();
StringBuilder sb = new StringBuilder();
int numAttrs = def.getAttributeCount();
AttributeDef[] attrDefs = def.getAttributeDefs();
for (int z = 0; z < numAttrs; z++) {
Object value = r.getAttribute(z);
sb.append(z > 0 ? " " : "")
.append(attrDefs[z].getName())
.append("=")
.append(value == null ? "<null>" : value)
.append(z < numAttrs - 1 ? "\n" : "");
}
System.out.println(sb.toString());
}
}
テスト・クライアントを実行すると、次のような結果が生成されます。
Printing attribute for a row in VO 'ProductView1' Id=10011 Name=Bunny Boot ShortDesc=Beginners ski boot LongtextId=518 ImageId=1001 SuggestedWhlslPrice=150 WhlslUnits=<null> SomeValue=I am from the Product Impl Class Setting the Name attribute to 'Q' succeeded.
注意:
この例では、ProductViewはProductエンティティ・オブジェクトに基づくエンティティ・ベースのビュー・オブジェクトです。Productエンティティ・オブジェクトには、一時的属性SomeValueが含まれ、I am the Product classという文字列を返します。この一時的属性が含まれる理由の詳細は、「提供されるアプリケーションでの拡張コンポーネントの置換え」の例を参照してください。
拡張コンポーネントを作成するとき、拡張コンポーネントのウィザードの「Java」ページにある「クラスの拡張」ボタンは無効になります。また、アプリケーション・モジュール・エディタの「Java」ページで、「Javaオプションの編集」ボタンをクリックすると、「Java」ダイアログの「クラスの拡張」ボタンは無効として表示されます。これは、JDeveloperが親コンポーネントの適切なクラスを自動的に拡張するため、別のクラスをユーザーが選択できるようにしても意味がないためです。
拡張エンティティ・オブジェクトを作成するときは、新しい属性、新しいアソシエーション、新しいバリデータ、および新しいカスタム・コードを導入できます。既存の属性の特定の宣言的部分、および親コンポーネントのクラスのメソッドをオーバーライドできます。拡張エンティティ・オブジェクトから、ベース・クラスが拡張エンティティ・オブジェクトから定義する属性を削除することはできません。
拡張ビュー・オブジェクトを作成するときは、新しい属性、新しいビュー・リンク、新しいバインド変数および新しいカスタム・コードを導入できます。既存の属性の特定の宣言的部分、および親コンポーネントのクラスのメソッドをオーバーライドできます。拡張ビュー・オブジェクトから、ベース・クラスが定義する属性を削除することはできません。
拡張アプリケーション・モジュールを作成するときは、新しいビュー・オブジェクト・インスタンスまたは新しいネストされたアプリケーション・モジュール・インスタンス、および新しいカスタム・コードを導入できます。拡張ビュー・オブジェクトから、親のコンポーネント・クラスのメソッドをオーバーライドすることもできます。
拡張エンティティ・オブジェクトまたはビュー・オブジェクトで新しい属性を追加する場合、属性索引番号は親コンポーネントを規準にして計算されます。たとえば、「拡張コンポーネントの親のクラスとインタフェース」で示されているProductViewビュー・オブジェクトについて考えます。カスタム・ビュー行クラスを有効にする場合、ProductViewRowImpl.javaクラスで定義される次のような属性索引定数が含まれる場合があります。
public class ProductViewRowImpl extends SummitViewRowImpl
implements ProductViewRow {
/**
* AttributesEnum: generated enum for identifying attributes and accessors.
* Do not modify.
*/
public enum AttributesEnum {...}
public static final int ID = AttributesEnum.Id.index();
public static final int NAME = AttributesEnum.Name.index();
public static final int SHORTDESC = AttributesEnum.ShortDesc.index();
public static final int LONGTEXTID = AttributesEnum.LongtextId.index();
public static final int IMAGEID = AttributesEnum.ImageId.index();
public static final int SUGGESTEDWHLSLPRICE =
AttributesEnum.SuggestedWhlslPrice.index();
public static final int WHLSLUNITS = AttributesEnum.WhlslUnits.index();
public static final int SOMEVALUE = AttributesEnum.SomeValue.index();
//etc.
}
ProductViewExのような拡張ビュー・オブジェクトを作成するとき、そのビュー・オブジェクトでSomeExtraAttrのような属性を追加し、カスタム・ビュー行クラスを有効にした場合、その属性定数は、親コンポーネントの属性定数の最大値を規準にして計算されます。
public class ProductViewExRowImpl extends ProductViewRowImpl
implements ProductViewExRow {
public static final int MAXUSAGECONST = 1;
public enum AttributesEnum {
SomeExtraAttr {
public Object get(ProductViewExRowImpl obj) {
return obj.getSomeExtraAttr();
}
public void put(ProductViewExRowImpl obj, Object value) {
obj.setAttributeInternal(index(), value);
}
}
private static AttributesEnum[] vals = null;
private static int firstIndex =
ViewDefImpl.getMaxAttrConst("oracle.summit.model.extend.ProductView");
public abstract Object get(ProductViewExRowImpl object);
public abstract void put(ProductViewExRowImpl object, Object value);
public int index() {
return AttributesEnum.firstIndex() + ordinal();
}
public static int firstIndex() {
return firstIndex;
}
public static int count() {
return AttributesEnum.firstIndex() +
AttributesEnum.staticValues().length;
}
public static AttributesEnum[] staticValues() {
if (vals == null) {
vals = AttributesEnum.values();
}
return vals;
}
}
public static final int SOMEEXTRAATTR = AttributesEnum.SomeExtraAttr.index();
...
}
ソリューションの各クライアントがオンサイトでカスタマイズする必要のあるパッケージ化されたアプリケーションを配布する場合のために、ADFビジネス・コンポーネントでは、この作業を簡単にする便利な機能が提供されています。
注意:
この項の例では、SummitADF_Examplesアプリケーション・ワークスペースのExtendedProjectプロジェクトを参照します。
通常、オンサイトでのアプリケーションのカスタマイズは、配布されたアプリケーションのソース・コードを直接変更することで行います。この方法の短所が明らかになるのは、元のアプリケーションのパッチや新機能リリースをクライアントに提供するときです。基になるアプリケーションのソース・コードに適用されていたカスタマイズは、パッチまたは更新を行った後のアプリケーションに、わざわざ適用しなおす必要があります。この方法では、アプリケーションのカスタマイズやメンテナンスに費用がかかるだけでなく、以前のカスタマイズを新しいリリースに適用しなおすときの人為的エラーにより不具合が発生する可能性があります。
ADFビジネス・コンポーネントが提供する優れたコンポーネント・ベースの方法を使用してアプリケーションをカスタマイズすると、基になっているアプリケーションのソース・コードを変更する必要はなく、ソース・コードにアクセスする必要さえありません。提供されたアプリケーションのカスタマイズは、次の方法で行います。
基になるアプリケーションのコンポーネントのパッケージを、新しいプロジェクトにインポートします。
アプリケーションのカスタマイズを適用する新しいコンポーネントを作成し、必要に応じて基のアプリケーションの適切な親コンポーネントを拡張します。
グローバルなコンポーネント置換のリストを定義し、カスタマイズしたコンポーネントの名前を設定して、基になるアプリケーションの適切な親コンポーネントを置き換えます。
ユーザーが定義されたグローバル・コンポーネント置換リストを使用し、配布されたアプリケーションを実行すると、配布されたアプリケーションは、コードを変更することなく、カスタマイズされたアプリケーション・コンポーネントを使用します。元のアプリケーションのパッチまたは更新バージョンが提供されると、次にアプリケーションを再起動するときにコンポーネントのカスタマイズが更新バージョンに適用されるので、カスタマイズを適用しなおす必要はありません。
グローバルなコンポーネント置換を定義するには、基になるアプリケーションからインポートしたコンポーネントに基づいて拡張コンポーネントを作成したプロジェクトの、「プロジェクト・プロパティ」ダイアログを使用します。
注意:
基になるアプリケーションのコンポーネントのうち、基のコンポーネントを直接または間接に継承する拡張コンポーネントがあるもののみを置換できます。
拡張コンポーネントを置換するには:
ExtendedProjectという名前のプロジェクトでグローバル・コンポーネント置換リストを定義すると、置換リストは、ソース・パスのルート・ディレクトリにあるExtendedProject.jpxに保存されます。
次の例に示すように、このファイルには、置換されるコンポーネントごとに1つずつ、Substitute要素が含まれます。
<JboProject Name="ExtendedProject" SeparateXMLFiles="true" PackageName="oracle.summit.model.extended" > <Containee Name="custompackage" FullName="oracle.summit.model.custompackage" ObjectType="JboPackage" > </Containee> <Containee Name="extended" FullName="oracle.summit.model.extended" ObjectType="JboPackage" > </Containee> <AppContainee Name="Model" FullName="oracle.summit.model.Model" ObjectType="JboProject"> <Substitutes> <Substitute OldName="oracle.summit.model.extended.ProductEx" NewName="oracle.summit.model.custompackage.CustomProduct" /> </Substitutes> </JboProject>
置換されたコンポーネントを元のアプリケーションが使用するよう指定するには、Javaシステム・プロパティFactory-Substitution-Listを定義し、その値を、置換リストが格納されている*.jpxファイルを持つプロジェクトの名前に設定します。値には、*.jprまたは*.jpx拡張子を除いたプロジェクト名のみを設定します。
たとえば、「拡張コンポーネントの親のクラスとインタフェース」で説明されている、Productエンティティ・オブジェクトとProductViewビュー・オブジェクトをカスタマイズする簡単な例について考えます。カスタマイズのため、次のようにしてExtendedProjectという名前の新しいオブジェクトを作成するものとします。
ベース・コンポーネントを含むJARファイルに対するライブラリを定義します。
ProductおよびProductViewを含むパッケージをインポートします。
CustomizeProductおよびCustomizeProductViewという個別のパッケージ名で、新しい拡張コンポーネントを作成します。
拡張コンポーネントを使用するためのコンポーネント置換リストを定義します。
拡張コンポーネントを作成するときは次のようにします。
ProductViewExビュー・オブジェクトにSomeExtraAttributeという名前の新しいビュー属性を追加します。
新しい検証ルールをCustomizedProductエンティティ・オブジェクトに追加し、Qという製品名は使用できないようにします。
CustomizedProduct.javaクラスのgetChecksum()メソッドをオーバーライドし、I am the CustomizedProduct Classを返すようにします。
Factory-Substitution-List Javaシステム・プロパティを、値がExtendsAndSubstitutesに設定されるよう定義し、「拡張コンポーネントの親のクラスとインタフェース」で説明するものとまったく同じテスト用のクライアント・クラスを実行すると、サンプルの出力は、置換されたコンポーネントの使用を反映して変化します。