Oracle® Fusion Middleware Oracle Application Development FrameworkによるFusion Webアプリケーションの開発 12c (12.2.1.2.0) E82918-03 |
|
前 |
次 |
この章の内容は次のとおりです。
ADFビジネス・コンポーネントのベース・クラスを拡張してカスタム・コードを組み込みます。
フレームワーク・ベースの開発の強力な機能の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ビジネス・コンポーネント拡張クラスを使用すると、組込み機能の動作を補強または変更することも、不具合に対して一般的な回避策を提供することもできます。
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 フレームワーク拡張レイヤーの新しいユーザー定義ライブラリ
ADFビジネス・コンポーネントのフレームワーク拡張コードは、特定のタイプのコンポーネントすべてで利用されます。汎用機能を記述するには、ADFビジネス・コンポーネントのAPIを使用して実行時にコード内でコンポーネント・メタデータにアクセスします。
フレームワーク拡張クラスで行う共通のタスクの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以上の |
|
単一の |
ADFビジネス・コンポーネントを使用すると、すべてのコンポーネントで実装できるカスタム・インタフェースを作成できます。たとえば、アプリケーション・モジュールからメソッドを公開する場合に便利です。
フレームワーク拡張クラスの作成に加えて、すべてのコンポーネントがデフォルトで実装できるカスタム・インタフェースも作成できます。クライアント・インタフェースは、たとえば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
拡張クラスのメソッドを公開する場合も、基本的な手順は同じです。
ADFビジネス・コンポーネントのカスタムJavaクラスにカスタム・コードを記述してデータベースのストアド・プロシージャとストアド・ファンクションを呼び出します。
ビジネス・コンポーネント用のカスタム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接続オブジェクトにアクセスします。
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ビジネス・コンポーネントのカスタム・メッセージ・バンドルを使用して、エラー・コードの代替メッセージ文字列を指定します。このようにして、組込みエラー・メッセージをカスタマイズできます。
カスタム・メッセージ・バンドルでエラー・コードに対してかわりのメッセージ文字列を提供することにより、組み込まれている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
この構成を使用してアプリケーションを実行すると、カスタム・トランザクション・クラスが使用されます。
ADFビジネス・コンポーネントでは、継承をサポートします。そのため、既存のビジネス・コンポーネントを拡張することで新しいビジネス・コンポーネントを作成してオリジナルのプロパティをすべて継承し、そこから新しいビジネス・コンポーネントをカスタマイズできます。
新しいビジネス・コンポーネントを作成するたびに、必要に応じて、既存のものを拡張してオリジナルのカスタマイズ・バージョンを作成できます。図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ビジネス・コンポーネントでは、アプリケーションのソース・コードにアクセスする必要のないアプリケーションのカスタマイズをサポートします。これにより、カスタマイズに関係するメンテナンス費用およびヒューマン・エラーが完全になくなります。
ソリューションの各クライアントがオンサイトでカスタマイズする必要のあるパッケージ化されたアプリケーションを配布する場合のために、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
に設定されるよう定義し、「拡張コンポーネントの親のクラスとインタフェース」で説明するものとまったく同じテスト用のクライアント・クラスを実行すると、サンプルの出力は、置換されたコンポーネントの使用を反映して変化します。