この章では、すべての種類のADF Business Componentsに適用される高度な手法について説明します。
この章の内容は次のとおりです。
注意: この章の例の稼働バージョンを試すには、サンプルのダウンロード・ページ(http://otn.oracle.com/documentation/jdev/b25947_01 )からAdvancedExamples ワークスペースをダウンロードしてください。 |
フレームワーク・ベースの開発の強力な機能の1つは、基本フレームワークを拡張して、組込み機能の動作を変更したり、すべてのアプリケーションが使用できる新機能を追加したりできることです。ここでは、次のことについて説明します。
フレームワーク拡張クラス
拡張クラスおよびその上に作成する基本ADFコンポーネントの作成方法
コンポーネントまたは特定のプロジェクトに対するフレームワーク拡張クラスのカスタム・レイヤー全体を使用するベスト・プラクティスの採用方法
ADF Business Componentsフレームワーク拡張クラスは、次のことを目的としてユーザーが作成する、フレームワークの基本クラスの1つを拡張するJavaクラスです。
追加の汎用的な機能で組込み機能の動作を補強する
組込み機能の動作方法を変更する
検出された不具合を一般的な方法で回避する
フレームワーク拡張クラスを作成した後は、基本クラスではなくカスタマイズしたフレームワーク・クラスに基づいて、新しいADFコンポーネントを作成できます。もちろん、既存のコンポーネントの定義を更新し、新しいフレームワーク拡張クラスを使用するようにすることもできます。
フレームワーク拡張クラスを作成するには、次の手順に従います。
フレームワーク拡張クラスを組み込むプロジェクトを識別します。
あるプロジェクトのコンポーネントのみで使用されることが明らかな場合は、ビジネス・サービス・コンポーネントと同じプロジェクトに作成できます。あるいは、複数のADFアプリケーションでフレームワーク拡張クラスを再利用する場合は、FrameworkExtensions
プロジェクト(SRDemoアプリケーションで示されているような)を別に作成して、フレームワーク拡張クラスを格納することもできます。
BC4Jランタイム・ライブラリがプロジェクトのライブラリ・リストに含まれることを確認します。
「プロジェクト・プロパティ」ダイアログの「ライブラリ」ページを使用してこれを検証し、ない場合はライブラリを追加します。
「Javaクラスの作成」ダイアログを使用して、新しいクラスを作成します。
このダイアログは、「新規ギャラリ」の「General」カテゴリで使用できます。
拡張フィールドのoracle.jbo.server
パッケージから、適切なフレームワーク・ベース・クラスを指定します。
図25-1は、com.yourcompany.fwkext
パッケージにCustomAppModuleImpl
という名前のカスタム・フレームワーク拡張クラスを作成して基本アプリケーション・モジュール・コンポーネントの機能をカスタマイズする様子を示しています。目的のベース・クラスをすばやく見つけるには、拡張フィールドの隣の「参照」ボタンを使用して、JDeveloperの「クラス・ブラウザ」を起動します。「検索」タブを使用し、クラス名の一部(ワイルドカードとして*
を使用できます)を入力してクラスの一覧をすばやく絞り込み、目的のクラスを見つけることができます。
「OK」をクリックすると、ユーザーが選択したパッケージ名に対応するプロジェクトのソース・パスのディレクトリに、カスタム・フレームワーク拡張クラスが作成されます。
注意: 一部のADF Business Componentsコンポーネント・クラスは、サーバー側とリモート・クライアント・バージョンの両方に存在します。たとえば、JDeveloperの「クラス・ブラウザ」を使用して、「検索」タブの「クラス名を一致」フィールドにApplicationModuleImpl と入力すると、結果のリストでは、oracle.jbo.server パッケージとoracle.jbo.client.remote パッケージに2つのApplicationModuleImplクラスが表示されます。フレームワーク拡張クラスを作成するときは、oracle.jbo.server パッケージのベースADFクラスを使用します。 |
新しいフレームワーク拡張クラスを作成しても、アプリケーションはそれを自動的には使用しません。フレームワーク拡張クラスを使用するプロジェクトのコンポーネントを指定する必要があります。次では、独自のフレームワーク拡張クラスに基づいてADFコンポーネントを作成する方法について説明します。
任意のADF Business Componentsウィザードまたはエディタの「Java」ページを使用して、ADFコンポーネントのベース・クラスを設定できます。実際に行う前に、次のチェックリストを確認してください。
フレームワーク拡張クラスを別のプロジェクトに作成する場合は、ビジネス・コンポーネントを含むプロジェクトの「プロジェクト・プロパティ」ダイアログの「依存性」ページで、FrameworkExtension
プロジェクトをプロジェクト依存関係としてマークします。
フレームワーク拡張クラスをJavaアーカイブ(JAR)ファイルにパッケージする場合は、JARファイルを参照するための名前付きライブラリ定義を作成し、ビジネス・コンポーネントを含むプロジェクトのライブラリ・リストにもそのライブラリを加えます。ライブラリがない場合に作成するには、「ツール」→「ライブラリの管理」メイン・メニュー項目から開くことのできる「ライブラリの管理」ダイアログを使用します。プロジェクトのライブラリ・リストを確認または変更するには、「プロジェクト・プロパティ」ダイアログの「ライブラリ」ページを使用します。
図25-2は、フレームワーク・クラスを参照できることを確認した後、新しいアプリケーション・モジュールのベース・クラスとしてCustomAppModuleImpl
クラスを使用する方法を示しています。ウィザードの「Java」ページの「クラスの拡張」ボタンをクリックすると表示される拡張ダイアログでは、フレームワーク拡張クラスの完全修飾名を入力できます(または、「参照」ボタンをクリックし、JDeveloperの「クラス・ブラウザ」を使用して検索します)。
すべてのADF Business Componentsウィザードおよびエディタの「Java」ページには同じ「クラスの拡張」ボタンが表示されるので、この方法を使用して、新規コンポーネントと既存コンポーネントの両方に対して目的のフレームワーク拡張ベース・クラスを選択できます。
注意: ADF Business Componentsのウィザードまたはエディタの拡張ダイアログでJDeveloperの「クラス・ブラウザ」を使用してコンポーネントのカスタム・ベース・クラスを選択するときは、使用可能なクラスのリストが自動的にフィルタ処理され、適切なクラスのみが表示されます。たとえば、図25-2で「参照」をクリックしてアプリケーション・モジュールの「オブジェクト」のベース・クラスを選択するときは、oracle.jbo.server.ApplicationModule クラスを直接または間接に拡張する現在のプロジェクトのライブラリ・リストで使用できるクラスのみがリストに表示されます。探しているクラスが表示されない場合は、正しくないベース・クラスを拡張しているか、または誤ったコンポーネント・クラス名をオーバーライドするように選択しています。 |
作成するADFコンポーネントがカスタム・フレームワーク拡張クラスを拡張するときは、選択したカスタム・クラス名を反映するように、JDeveloperがXMLコンポーネントの定義を更新します。
たとえば、CustomAppModuleImpl
をカスタム・アプリケーション・モジュール・ベース・クラスにして、com.yourcompany.yourapp
パッケージにYourService
アプリケーション・モジュールを作成したものとします。コンポーネントをカスタムJavaファイルのないXMLのみのコンポーネントにした場合、そのXMLコンポーネント定義(YourService.xml)は、例25-1のようになります。実行時にAppModuleタグのComponentClass
属性の値が読み取られて、コンポーネントを表すために使用するJavaクラスが識別されます。
例25-1 XMLコンポーネント定義に記録されるカスタム・ベース・クラス名
<AppModule Name="YourService" ComponentClass="com.yourcompany.fwkext.CustomAppModuleImpl" > <!-- etc. --> </AppModule>
図25-3は、XMLのみのYourService
アプリケーション・モジュールとカスタム拡張クラスの関係を示したものです。実行時には、ApplicationModuleImpl
クラスから基本動作を継承するCustomAppModuleImpl
クラスを使用します。
コンポーネントでJavaクラスが必要な場合は、前の項で示したように、コンポーネント・エディタの「Java」ページを開き、適切なチェック・ボックスを選択して有効にします。たとえば、YourServer
アプリケーション・モジュールに対してカスタム・アプリケーション・モジュール・クラスを有効にすると、適切なYourServiceImpl.java
クラスが作成されます。例25-2で示したように、コンポーネントのXMLコンポーネント定義も更新されて、カスタム・コンポーネント・クラスの名前が反映されます。
例25-2 XMLコンポーネント定義に記録されたカスタム・コンポーネント・クラス
<AppModule Name="YourService" ComponentClass="com.yourcompany.yourapp.YourServiceImpl" > <!-- etc. --> </AppModule>
また、例25-3に示すように、コンポーネントのカスタムJavaクラスのextends
句も変更されて、新しいカスタム・フレームワーク・ベース・クラスが反映されます。
例25-3 新しいベース・クラスを反映するためのコンポーネントのカスタムJavaクラスの更新
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. }
図25-4は、カスタムYourServiceImpl
クラスを含むYourService
アプリケーション・モジュールとフレームワーク拡張クラスの関係を示したものです。実行時には、ベースApplicationModuleImpl
クラスを拡張するCustomAppModuleImpl
フレームワーク拡張クラスから基本動作を継承するYourServiceImpl
クラスを使用します。
カスタムJavaクラスを含むADFコンポーネントがあり、後でコンポーネントをフレームワーク拡張クラスに基づくように変更する場合は、コンポーネント・エディタの「Java」ページの「クラスの拡張」ボタンを使用して、コンポーネントのベース・クラスを変更してください。これを行うと、コンポーネントのXMLコンポーネント定義が新しいベース・クラスを反映するように更新され、さらにコンポーネントのカスタムJavaクラスのextends
句が変更されます。コンポーネント・エディタを使用せずに手動でextends
句を更新すると、コンポーネントのXMLコンポーネント定義に新しい継承が反映されず、次にエディタを開いたときに、手動で変更したextends
句は、コンポーネント・エディタが正しいコンポーネント・ベース・クラスであるとする値で上書きされます。
前に示した例では、ベースApplicationModuleImpl
クラスを拡張する単一のCustomAppModuleImpl
クラスが示されていました。ただし、作成するフレームワーク拡張クラスのレベルの数には固定の制限はありません。たとえば、会社で作成するすべてのADFアプリケーションのすべてのアプリケーション・モジュールで使用する会社レベルのCustomAppModuleImpl
を作成した後、一部のプロジェクト・チームでそのフレームワーク拡張クラスをさらにカスタマイズすることが必要になったものとします。そのようなチームでは、CustomAppModuleImpl
を拡張するSomeProjectCustomAppModuleImpl
クラスを作成し、プロジェクト固有のカスタム・アプリケーション・モジュールのコードをそこに含めることができます。
public class SomeProjectCustomAppModuleImpl extends CustomAppModuleImpl { /* * Custom application module code specific to the * "SomeProject" project goes here. */ }
その後、この特定のプロジェクトの実装の一部として作成されるアプリケーション・モジュールでは、CustomAppModuleImpl
のかわりにSomeProjectCustomAppModuleImpl
をベース・クラスとして使用できます。
フレームワーク拡張クラスの特定のセットをプロジェクトの標準として使用する場合は、「プロジェクト・プロパティ」ダイアログの「ビジネス・コンポーネント: ベース・クラス」ページ(図25-5を参照)を開き、各コンポーネント・タイプに対して優先的に使用するベース・クラスを定義できます。たとえば、プロジェクトで作成される新しいアプリケーション・モジュールがデフォルトでCustomAppModuleImpl
クラスを使用するよう指定するには、図で示されているように、「アプリケーション・モジュール」の「オブジェクト」クラス名フィールドに、そのクラスの完全修飾名を入力します。ベース・クラスに対するこれらの優先指定を設定しても、プロジェクト内の既存コンポーネントには影響ありませんが、コンポーネント・ウィザードが作成する新しいコンポーネントにはこの設定が使用されます。
アプリケーション固有のビジネス・コンポーネントの開発を始める前に、フレームワーク拡張クラスの完全なレイヤーを作成し、そのレイヤーをデフォルトで使用するようプロジェクト・レベルで設定することを検討することをお薦めします。これらのフレームワーク拡張クラスの一部(または全部)にカスタム・コードを追加することはまだ考えていない場合でも、次のようなことが必要になることがあります。
会社のすべてのアプリケーション・モジュールで必要な一般機能を追加する
汎用的なカスタム処理で組込み機能を補強する
検出された不具合を一般的な方法で回避する
この推奨事項に従うことをお薦めします。最初にこれらの設定をしておかないと、新しい汎用機能や、組込み機能の補強や、不具合の一般的な回避といったことが、プロジェクトの途中ですべてのエンティティ・オブジェクトで必要になった場合に、チームにとって大きな不都合となります。プロジェクトの開始時にJDeveloperが自動的に使用するようにフレームワーク・クラスの完全なレイヤーを設定しておくことは、このような不都合と、プロジェクトの途中でそれに対処する場合の無駄な時間に対する保険となります。
ユーザーが選択したパッケージ名(com.yourcompany.adfextensions
など。それぞれが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 CustomDatabaseTransactionFactory extends DatabaseTransactionFactory
フレームワーク拡張レイヤー・クラスを再利用可能なライブラリとしてパッケージしやすくするため、これらを使用するプロジェクトとは別のプロジェクトでこれらを作成することをお薦めします。SRDemoアプリケーションでは、FrameworkExtensions
プロジェクトに、アプリケーションが使用するすべてのフレームワーク拡張クラスが収められています。
注意: 便宜のため、AdvancedExamples ワークスペースのFrameworkExtensions プロジェクトに、これらのクラスのセットが収められています。アプリケーション・ナビゲータでcom.yourcompany.adfextensions パッケージを選択し、ポップアップ・メニューから「リファクタ」→「名前の変更」オプションを選択して、すべてのクラスが入っているパッケージの名前を自由に変更できます。 |
完璧を期すため、次のクラスについてもカスタマイズしたフレームワーク・クラスを作成する場合もありますが、これらのクラスの内容をオーバーライドすることはほとんど必要がないことに注意してください。
public class CustomViewDefImpl extends ViewDefImpl
public class CustomEntityCache extends EntityCache
public class CustomApplicationModuleDefImpl extends ApplicationModuleDefImpl
フレームワーク拡張レイヤーのクラスを含むJARファイルを作成するには、「デプロイメント・プロファイルの作成 -- JARファイル」ダイアログを使用します。このダイアログは、「新規ギャラリ」の「General」→「Deployment Files」カテゴリで使用できます。
注意: 「新規ギャラリ」に「Deployment Profiles」カテゴリが表示されない場合は、ダイアログの上部にある「フィルタ方法」ドロップダウン・リストで「すべてのテクノロジ」を選択すると、表示されるようになります。 |
デプロイメント・プロファイルにFrameworkExtensions
のような名前を設定し、「OK」をクリックします。デフォルトでは、JARファイルにはプロジェクト内のすべてのクラス・ファイルが格納されます。これが求める結果であるため、「JARデプロイメント・プロファイルのプロパティ」ダイアログが表示されたら、「OK」をクリックして終了してかまいません。
最後に、JARファイルを作成するには、アプリケーション・ナビゲータの「リソース」フォルダでFrameworkExtensions.deploy
ノードを選択し、ポップアップ・メニューで「JARファイルにデプロイ」を選択します。JDeveloperの「ログ・ウィンドウ」の「デプロイ」タブに、次のようなフィードバックが表示されます。
---- Deployment started. ---- Apr 28, 2006 1:42:39 PM Running dependency analysis... Wrote JAR file to ...\FrameworkExtensions\deploy\FrameworkExtensions.jar Elapsed time for deployment: less than one second ---- Deployment finished. ---- Apr 28, 2006 1:42:39 PM
JDeveloperは、再利用可能なコンポーネント・ライブラリで構成されるJARファイルを編成するための簡便な方法として、名前付きのライブラリを使用します。フレームワーク拡張JARファイルのライブラリを定義するには、次の操作を実行します。
JDeveloperのメイン・メニューから、「ツール」→「ライブラリの管理」を選択します。
「ライブラリの管理」ダイアログで「ライブラリ」タブを選択します。
ツリーで「User」フォルダを選択し、「新規」ボタンをクリックします。
表示される「ライブラリの作成」ダイアログで、ライブラリにFramework Extension Layerという名前を設定し、「クラス・パス」ノードを選択して、「エントリの追加」をクリックします。
表示される「パス・エントリの選択」ダイアログで、フレームワーク拡張コンポーネントのクラス・ファイルを含むFrameworkExtensions.jar
ファイルを選択し、「選択」をクリックします。
「ソース・パス」ノードを選択し、「エントリの追加」をクリックします。
表示される「パス・エントリの選択」ダイアログで、フレームワーク拡張クラスのソース・ファイルが存在する..\FrameworkExtensions\src
ディレクトリを選択し、「選択」をクリックします。
「OK」をクリックして「ライブラリの作成」ダイアログを閉じ、新しいライブラリを定義します。
終了すると、図25-6で示すように、新しいユーザー定義ライブラリFramework Extension Layerが表示されます。この後は、ビジネス・サービスを作成するプロジェクトのライブラリ・リストにこのライブラリを追加することで、優先使用するコンポーネント・ベース・クラスとしてカスタム・フレームワーク拡張クラスが参照されるようになります。
フレームワーク拡張クラスで行う共通のタスクの1つは、カスタム・アプリケーション機能の実装です。フレームワーク拡張コードは特定の種類の全コンポーネントで使用されるように記述するので、これらのクラスで記述するコードでは、通常、一般的な方法でコンポーネントの属性を処理する必要があります。これに対応するため、ADFでは、実行時にコンポーネントのメタデータにアクセスできるAPIが提供されています。また、カスタム・メタデータのプロパティとコンポーネントまたは属性を関連付ける機能も提供されています。作成する汎用的なフレームワーク拡張コードでは、ランタイム・メタデータとカスタム・プロパティを使用して、必要な場合には特定のカスタム・プロパティが存在する場合にのみ使用される、一般的な機能を作成できます。
図25-7は、ビュー・オブジェクトおよびエンティティ・オブジェクトに関するランタイム・メタデータにアクセスするためにADFが提供する3つの主要なインタフェースを示しています。ViewObject
インタフェースは、StructureDef
インタフェースを拡張します。エンティティ定義を表すクラス(EntityDefImpl
)も、このインタフェースを実装します。名前が示すように、StructureDef
は構造とコンポーネントを定義しており、ビュー・オブジェクト行またはエンティティ行の各属性に関するランタイム・メタデータを提供するAttributeDef
オブジェクトのコレクションへのアクセスを提供します。AttributeDef
を使用することで、それに付随するAttributeHints
オブジェクトにアクセスし、表示ラベル、フォーマット・マスク、ツールチップなどのヒントを参照できます。
7.9.3項「読取り専用ビュー・オブジェクトに対するビュー・オブジェクト・キー管理の有効化について」で説明したように、読取り専用のビューの場合、findByKey()
メソッドおよびsetCurrentRowWithKey
組込み操作は、setManageRowsByKey(true)
を呼び出すようにビュー・オブジェクトのcreate()
メソッドをオーバーライドした場合にのみ動作します。多くの読取り専用ビュー・オブジェクトを作成する場合、このような詳細を記憶しておくのは大変であるため、ビュー・オブジェクトのフレームワーク拡張クラスで自動化する機能の有力な候補となります。
SRDemoアプリケーションに含まれるFrameworkExtensions
プロジェクトのSRViewObjectImpl
クラスは、アプリケーション内の全ビュー・オブジェクトに対するベース・クラスです。ビュー・オブジェクトに対するこのフレームワーク拡張クラスは、例25-4で示されているように、ViewObjectImpl
ベース・クラスを拡張し、create()
メソッドをオーバーライドして、このタスクを自動化します。実行時にビュー・オブジェクト・インスタンスを作成するとき、super.create()
を呼び出してデフォルトのフレームワーク機能を実行した後、このコードは、ビュー・オブジェクトが少なくとも1つの属性がキー属性としてマークされている読取り専用のビュー・オブジェクトかどうかを検査します。そうである場合は、setManageRowsByKey(true)
を呼び出します。
isReadOnlyNonEntitySQLViewWithAtLeastOneKeyAttribute()
ヘルパー・メソッドは、次の条件の組合せを検査することで、ビュー・オブジェクトが読取り専用かどうかを判定します。
isFullSql()
がtrue
このメソッドは、ビュー・オブジェクトの問合せが、含まれるエンティティ・オブジェクトの慣用名に基づいて選択リストを自動的に生成するのではなく、開発者によって完全に指定されている場合は、trueを返します。
getEntityDefs()
がnull
このメソッドは、ビュー・オブジェクトのエンティティ・オブジェクト慣用名を表すEntityDefImpl
オブジェクトの配列を返します。null
を返す場合は、ビュー・オブジェクトにはエンティティ・オブジェクトの慣用名がありません。
次に、getAttributeDefs()
メソッドが返すAttributeDef
配列をループして、ビュー・オブジェクトにキー属性があるかどうかを判定します。リストのいずれかの属性定義に対してisPrimaryKey()
メソッドがtrueを返す場合、そのビュー・オブジェクトにはキーがあります。
例25-4 キーによる管理行の設定の自動化
public class SRViewObjectImpl 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. }
JDeveloperでは、アプリケーション・モジュール、ビュー・オブジェクト、およびエンティティ・オブジェクトを作成するときに、エディタの「カスタム・プロパティ」タブで、任意のコンポーネントに対するカスタム・メタデータ・プロパティを定義できます。カスタム・メタデータ・プロパティは名前と値のペアであり、これを使用することで、コンポーネントに関する追加の宣言情報を、フレームワーク拡張クラスで作成する汎用コードに伝えることができます。コードでは、getProperty()
メソッドを使用することで、特定のカスタム・メタデータ・プロパティの存在または値に基づいて、条件付きで汎用機能を実行できます。
たとえば、SRDemoアプリケーションのSRViewObjectImpl
フレームワーク拡張クラスでは、ビュー・オブジェクトのinsertRow()
メソッドをオーバーライドし、条件に基づいて行を挿入し、行セットの最後の行として表示しています。このフレームワーク拡張クラスを拡張するビュー・オブジェクトでInsertNewRowsAtEnd
という名前のカスタム・メタデータ・プロパティが定義されている場合、この汎用コードは末尾への新しい行の挿入を実行します。ビュー・オブジェクトでこのプロパティが定義されていない場合は、insertRow()
のデフォルトの動作が実行されます。SRDemoアプリケーションでは、ServiceHistories
ビュー・オブジェクトでこのカスタム・メタデータ・プロパティが定義されているので、追加された新規行は最後に挿入されます。
例25-5 ビュー・オブジェクトのデフォルト行セットの末尾への条件付きでの新規行の挿入
public class SRViewObjectImpl 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()
メソッドを使用することで、属性が表す種類を判定することができます。このメソッドは、表25-1で示されているAttributeDef
インタフェースのパブリック定数のいずれかに対応するbyte
値を返します。
表25-1 エンティティ・オブジェクトとビュー・オブジェクトの属性の種類
パブリックAttributeDef 定数 |
属性の種類の説明 |
---|---|
|
永続的属性 |
|
一時的属性 |
|
エンティティ・レベルの一時的属性にマップされるビュー・オブジェクト属性 |
|
SQL計算属性 |
|
動的属性 |
|
0以上の |
|
単一の |
カスタム・プロパティに依存するフレームワーク拡張クラスを作成すると、適切なコンポーネント・エディタの「カスタム・プロパティ」タブのドロップダウン・リストにカスタム・プロパティ名が表示されるように、JDeveloperを設定できます。定義済のカスタム・プロパティ名を設定するには、JDeveloperのメイン・メニューから「ツール」→「設定」を選択し、「設定」ダイアログの「ビジネス・コンポーネント」→「プロパティ名」タブを開きます。
実行時にカスタム・プロパティの値をプログラムで設定すると便利な場合があります。この機能を実行するためのsetProperty()
APIは、設計上、oracle.jbo
パッケージのViewObject
、ApplicationModule
またはAttributeDef
インタフェースのクライアントでは利用できませんが、ADFコンポーネントのカスタムJavaクラスで記述したコードでは使用できます。
注意: AdvancedExamples ワークスペースのProgrammaticallySetProperties プロジェクトを使用して、この手法の例を試すことができます。ダウンロード方法については、この章の最初にある注意を参照してください。 |
フレームワーク拡張クラスの作成に加えて、すべてのコンポーネントがデフォルトで実装できるカスタム・インタフェースも作成できます。ここではアプリケーション・モジュールの例について説明しますが、カスタム拡張ビュー・オブジェクトおよびビュー行インタフェースに対しても同じ機能を作成できます。
注意: この項の例では、AdvancedExamples ワークスペースのCustomizedExtensionInterface プロジェクトを参照します。ダウンロード方法については、この章の最初にある注意を参照してください。 |
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ウィザードは、コンパイルが正常に終了したインタフェースのみを参照します。
次に、グローバル拡張インタフェースCustomApplicationModule
を公開し、CustomApplicationModuleImpl
フレームワーク拡張クラスに基づく新しいProductModule
アプリケーション・モジュールを作成するには、次の手順を実行します。
アプリケーション・モジュールにカスタムJavaクラスがあることを確認します。
ない場合は、アプリケーション・モジュール・エディタの「Java」ページで「適用」ボタンをクリックして有効にできます。
アプリケーション・モジュールのベース・クラスとしてCustomApplicationModuleImpl
を選択します。
そのためには、再びアプリケーション・モジュール・エディタの「Java」タブを使用し、「クラスの拡張」ボタンをクリックして拡張ダイアログを開きます。
クライアントがコンポーネントで使用できるカスタム・インタフェースの1つとして、CustomApplicationModule
インタフェースを指定します。
そのためには、「クライアント・インタフェース」ページを開きます。「インタフェース」ボタンをクリックします。CustomApplicationModule
インタフェースを「使用可能」リストから「選択済」リストに移動し、「OK」をクリックします。
「クライアント・インタフェース」ページの「選択済」リストに、少なくとも1つのメソッドが表示されることを確認します。
注意: グローバル拡張インタフェースのメソッドの1つを重複して選択することになるとしても、「クライアント・インタフェース」ページの「選択済」リストでは少なくとも1つのメソッドを選択する必要があります。JDeveloperでカスタムProductModule インタフェースを生成するには、どのメソッドでもかまいません。 |
アプリケーション・モジュール・エディタを終了すると、JDeveloperがアプリケーション・モジュールのカスタム・インタフェースProductModule
を生成します。このインタフェースは、次のように、ApplicationModule
インタフェースとCustomApplicationModule
拡張インタフェースの両方を自動的に拡張します。
package devguide.advanced.customintf.common; import devguide.advanced.customintf.fwkext.CustomApplicationModule; import oracle.jbo.ApplicationModule; // --------------------------------------------------------------------- // --- File generated by Oracle 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 devguidepkg 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 devguidepkg;
以降の項では、このパッケージのプロシージャおよびファンクションの各例を呼び出す方法について説明します。
注意: この項の例では、AdvancedExamples ワークスペースのStoredProcedureInvocation プロジェクトを参照します。ダウンロード方法については、この章の最初にある注意を参照してください。 |
引数を受け取らないストアド・プロシージャを呼び出す必要がある場合は、DBTransaction
インタフェース(例25-6で示されているoracle.jbo.server
パッケージ内)のexecuteCommand()
メソッドを使用できます。
IN
モード引数のみを受け取るストアド・プロシージャ(何も指定しない場合のデフォルトのPL/SQLパラメータ・モード)を呼び出すには、JDBC PreparedStatement
オブジェクトを使用する必要があります。DBTransaction
インタフェースでは、現在のデータベース接続のコンテキストでこのオブジェクトを作成するためのcreatePreparedStatement()
メソッドが提供されています。例25-7で示されているようなヘルパー・メソッドを使用することで、PreparedStatement
を使用してこの種のストアド・プロシージャを呼び出す処理が簡単になります。重要なこととして、ヘルパー・メソッドを使用することにより、実行後にJDBC PreparedStatement
を閉じるコードをカプセル化できます。このコードが実行する基本的な処理は次のとおりです。
渡される文のためのJDBC PreparedStatement
を作成し、PL/SQLのbegin
...end
ブロック内にラップします。
バインド変数が渡された場合は、値のループ処理を行います。
各バインド変数の値を文に設定します。
JDBCのバインド変数APIは1から始まる番号付けを使用するため、0から始まるforループのインデックス変数にコードで1を加えていることに注意してください。
文を実行します。
文を閉じます。
例25-7 IN引数のみのストアド・プロシージャの呼出しを簡単にするヘルパー・メソッド
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("devguidepkg.proc_with_three_args(?,?,?)", new Object[]{n,d,v}); }
ファンクションに渡される引数のJDBCバインド変数のプレースホルダとして疑問符が使用されていることに注意してください。JDBCは名前付きのバインド変数の使用もサポートしますが、ヘルパー・メソッドは位置でバインド変数の値を設定するのみであるため、このように単純な位置によるバインド変数を使用しても問題ありません。
IN
モードの引数のみを受け取るストアド・ファンクションの呼出しでは、文を実行した後でファンクションの結果の値にアクセスするために、JDBC CallableStatement
オブジェクトを使用する必要があります。DBTransaction
インタフェースでは、現在のデータベース接続のコンテキストでこのオブジェクトを作成するためのcreateCallableStatement()
メソッドが提供されています。例25-8で示されているようなヘルパー・メソッドを使用することで、CallableStatement
を使用してこの種のストアド・ファンクションを呼び出す処理が簡単になります。前に示したように、ヘルパー・メソッドは、使用するJDBC文の作成とクリーン・アップの両方をカプセル化します。
このコードが実行する基本的な処理は次のとおりです。
渡される文のためのJDBC CallableStatement
を作成し、PL/SQLのbegin
...end
ブロック内にラップします。
最初のバインド変数をファンクションの戻り値に登録します。
バインド変数が渡された場合は、値のループ処理を行います。
ユーザーが提供した各バインド変数の値を文に設定します。
JDBCのバインド変数APIは1から始まる番号付けを使用し、ファンクションの戻り値がすでに文の最初のバインド変数になっているため、0から始まるforループのインデックス変数にコードで2を加えていることに注意してください。
文を実行します。
最初のバインド変数の値を返します。
文を閉じます。
例25-8 IN引数のみのストアド・ファンクションの呼出しを簡単にするヘルパー・メソッド
// 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 StoredProcTestModuleImpl.java public String callFuncWithThreeArgs(Number n, Date d, String v) { return (String)callStoredFunction(VARCHAR2, "devguidepkg.func_with_three_args(?,?,?)", new Object[]{n,d,v}); }
ファンクションに渡される引数のJDBCバインド変数のプレースホルダとして疑問符が使用されていることに注意してください。JDBCは名前付きのバインド変数の使用もサポートしますが、ヘルパー・メソッドは位置でバインド変数の値を設定するのみであるため、このように単純な位置によるバインド変数を使用しても問題ありません。
OUT
モードまたはIN OUT
モードの引数を含むdevguidepkg.proc_with_out_args
のようなストアド・プロシージャまたはストアド・ファンクションの呼出しでは、前の項の場合と同じようにCallableStatement
を使用する必要がありますが、ヘルパー・メソッドへの一般化は少し難しくなります。例25-9は、devguidepkg.proc_with_out_args
プロシージャを呼び出すために必要なJDBCコードを示しています。
このコードが実行する基本的な処理は次のとおりです。
文が呼び出すPL/SQLブロックを定義します。
PL/SQLブロックに対するCallableStatement
を作成します。
OUT
パラメータの位置と型を登録します。
IN
パラメータのバインド変数を設定します。
文を実行します。
複数の戻り値を保持するためのJavaBeanを作成します。
DateAndStringBean
クラスには、dateVal
およびstringVal
という名前のBeanプロパティが含まれます。
最初のOUT
パラメータを使用して、dateVal
プロパティの値を設定します。
2番目のOUT
パラメータを使用して、stringVal
プロパティの値を設定します。
結果を返します。
JDBC CallableStatement
を閉じます。
例25-9 複数のOUT引数があるストアド・プロシージャの呼出し
public Date callProcWithOutArgs(Number n, String v) { CallableStatement st = null; try { // 1. Define the PL/SQL block for the statement to invoke String stmt = "begin devguidepkg.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) {} } } }
例25-9で使用されているDateAndString
Beanは、次のような、2つのBeanプロパティを持つ簡単なJavaBeanです。
package devguide.advanced.storedproc; 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 Business Componentsのコンポーネントは下位レベルのデータベース・プログラミングの詳細をすべて抽象化しているので、通常は、JDBCのConnection
オブジェクトに直接アクセスする必要はありません。28.3.1項「サポートされる解放レベル」で説明されている予約されたリリース・モードを使用しているのでない場合は、実行時に、異なるWebページ・リクエストの間で、まったく同じアプリケーション・モジュール・インスタンスまたはJDBC Connection
インスタンスをアプリケーションが使用することは保証されません。この種のプールされたサービス環境でJDBC Connectionオブジェクトへの参照を誤って保持すると、実行時に想定外の動作が発生する可能性があるため、設計として、ADF Business ComponentsレイヤーにはJDBC Connection
を直接取得するAPIはありません。これは、JDBC Connectionの直接的な使用および不注意による誤用を防ぐために意図的に行われていることです。
ただし、サード・パーティのコードをADF Business Componentsと統合しようとするときにはこれができると便利な場合があるので、例25-10で示すようなヘルパー・メソッドを使用するとConnectionにアクセスできます。
例25-10 現在のJDBC Connectionにアクセスするためのヘルパー・メソッド
/** * Put this method in your XXXXImpl.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 Connectionを、独自のコード内のどこにもキャッシュしないことをお薦めします。かわりに、必要になるたびにヘルパー・メソッドを呼び出すようにすることで、JDBC Connectionに対する参照を誤って保持し、それが後で別のユーザーによる別のリクエストで、ADFランタイム環境のプールされたサービスに対して使用されるのを防ぐようにします。 |
他のJavaコンポーネントと同様に、再利用可能なADFコンポーネントの1つ以上のパッケージを含むJARファイルを作成できます。その後、他のプロジェクトでこのコンポーネント・ライブラリからコンポーネントのパッケージをインポートし、新しいアプリケーションで参照できます。
注意: この項の例では、AdvancedExamples ワークスペースのReusableComponents 、ProjectImportingReusableComponents 、およびOtherProjectWithComponents の各プロジェクトを参照します。ダウンロード方法については、この章の最初にある注意を参照してください。 |
ビジネス・コンポーネントのライブラリを構成するJavaクラスおよびXMLコンポーネント定義を含むJARファイルを作成するには、「ビジネス・コンポーネントのアーカイブ・プロファイルの作成」ダイアログを使用します。このダイアログは、「新規ギャラリ」の「General」→「Deployment Files」カテゴリで使用できます。
注意: 「新規ギャラリ」に「Deployment Profiles」カテゴリが表示されない場合は、ダイアログの上部にある「フィルタ方法」ドロップダウン・リストで「すべてのテクノロジ」を選択すると、表示されるようになります。 |
デプロイメント・プロファイルにReusableComponents.bcdeploy
のような名前を設定し、「OK」をクリックします。図25-8で示されているように、ReusableComponents.bcdeploy
ビジネス・コンポーネント・デプロイメント・アーカイブ・プロファイルには、ネストされた2つのJARデプロイメント・プロファイルが含まれます。
ReusableComponentsMiddleTier.deploy
ReusableComponentsCommon.deploy
これら2つのネストされたプロファイルは、次のものをバンドルするために事前に構成されている標準のJARデプロイメント・プロファイルです。
すべてのビジネス・コンポーネント・カスタムJavaクラスおよびXMLコンポーネント定義をReusableComponentsCSMT.jar
アーカイブにバンドル
すべてのクライアント・インタフェース、メッセージ・バンドル・クラス、およびカスタム・ドメインをReusableComponentsCSCommon.jar
にバンドル
これらは、ADF Business Componentsベースのアプリケーションを簡単にデプロイできるよう、このように分割されています。*CSMT.jar
は、中間層のアプリケーション・サーバーにのみデプロイされるよう設計されているコンポーネントのアーカイブです。*CSCommon.jar
は、アプリケーション・モジュールと通信するクライアントがそのアプリケーション・モジュールとは異なる物理サーバーで実行しているデプロイ・シナリオで、アプリケーション・サーバーとリモート・クライアント層の両方に共通のものです。
JARファイルを作成するには、アプリケーション・ナビゲータの「リソース」フォルダでReusableComponents.bcdeploy
ノードを選択し、ポップアップ・メニューで「デプロイ」を選択します。JDeveloperの「ログ・ウィンドウ」の「デプロイ」タブに、次のようなフィードバックが表示されます。
---- Deployment started. ---- Apr 28, 2006 7:04:02 PM Running dependency analysis... Wrote JAR file to ...\ReuseableComponents\deploy\ReuseableComponentsCSMT.jar Running dependency analysis... Wrote JAR file to ...\ReuseableComponents\deploy\ReuseableComponentsCSCommon.jar Elapsed time for deployment: less than one second ---- Deployment finished. ---- Apr 28, 2006 7:04:02 PM
ビジネス・コンポーネントの再利用可能なライブラリを作成すると、そのライブラリからコンポーネントのパッケージを他のプロジェクトにインポートして参照することができます。ライブラリからビジネス・コンポーネントのパッケージをインポートすると、ADF Business Componentsコンポーネントのウィザードおよびエディタの様々な「使用可能」リストでそのパッケージのコンポーネントを使用できるようになりますが、アプリケーション・ナビゲータには表示されません。アプリケーション・ナビゲータに表示されるのは、現在のプロジェクトのソース・パスに存在するコンポーネントのみです。
ライブラリからビジネス・コンポーネントのパッケージをインポートするには、次の手順を実行します。
インポートを行うプロジェクトの「プロジェクト・プロパティ」ダイアログの「ライブラリ」タブで、JARファイルのライブラリを定義します。
ライブラリは、プロジェクト・レベルまたはユーザー・レベルとして定義できます。ライブラリ定義のクラス・パスに、*CSMT.jar
と*CSCommon.jar
の両方を必ず含めます。
インポートを行うプロジェクトのライブラリ・リストに、新しいライブラリを追加します。
アプリケーション・ナビゲータでインポートを行うプロジェクトを選択し、JDeveloperのメイン・メニューから「ファイル」→「インポート」を選択します。
表示される「インポート」ダイアログで、リストから「ビジネス・コンポーネント」を選択します。
ファイルを開くダイアログを使用して、ディレクトリの場合と同じようにライブラリの*CSMT.jar
ファイルの中に移動し、インポートするコンポーネントのパッケージ内のコンポーネントから、XMLコンポーネント定義を選択します。
パッケージのインポートが成功したことを示すアラートを確認します。
インポートするコンポーネントのパッケージごとに、手順3〜6を繰り返します。
インポートしたコンポーネントのパッケージにProduct
のようなエンティティ・オブジェクトがあったとすると、エンティティ・オブジェクトの慣用名として、インポートしたProduct
コンポーネントを使用し、インポートを行っているプロジェクト内に新しいビュー・オブジェクトを作成できます。これはほんの一例です。インポートした任意のコンポーネントを、プロジェクトのソース・パスにある場合と同じように参照できます。唯一の違いは、インポートしたコンポーネントは編集できないことです。実際、再利用可能なコンポーネント・ライブラリのJARファイルには、コンポーネントのXMLコンポーネント定義ファイルとJava *.class
ファイルのみが含まれ、ソース・コードは含まれない場合があります。
コンポーネントのパッケージをYourImportingProjectName
という名前のプロジェクトにインポートすると、インポートしているプロジェクトのソース・パスのルート・ディレクトリにあるYourImportingProjectName.jpx
ファイルに、そのパッケージへの参照が追加されます。このエントリの一部として、_LocationURL
という名前の設計時プロジェクトが含まれ、その値はインポートされたコンポーネントが存在するJARファイルを指します。
アプリケーション・ナビゲータは、プロジェクトのソース・パスにあるすべてのビジネス・コンポーネントを表示します。現在はプロジェクトのソース・パスの一部ではないディレクトリからビジネス・コンポーネントを追加する場合は、「プロジェクト・プロパティ」ダイアログの「プロジェクト・コンテンツ」ページを開き、追加するコンポーネントの親ディレクトリを、「Javaコンテンツ」リストのディレクトリの1つとして追加します。インポートしたコンポーネントのパッケージとは異なり、このようにしてプロジェクトのソース・パスに追加したコンポーネントは、完全に編集可能であり、アプリケーション・ナビゲータに表示されます。
インポートしたコンポーネントを変更し、それを格納しているJARファイルを更新した場合、変更を反映するには、インポートしているプロジェクトを閉じてから開きなおす必要があります。このとき、JDeveloperを終了する必要はありません。アプリケーション・ナビゲータでインポートしているプロジェクトを選択し、メイン・メニューから「ファイル」→「閉じる」を選択した後、プロジェクトのノードを再び展開し、プロジェクトを閉じて開きなおします。この手順を実行すると、インポートされているJARファイルの更新バージョンからコンポーネントが再度読み込まれます。
コンポーネントのパッケージを誤ってインポートした場合、またはインポートしたコンポーネントのパッケージで使用していないものを削除する場合、JDeveloperには対話式にこれを行う方法はありません。パッケージのインポートを取り消すには、次の手順を実行する必要があります。
該当するワークスペースをアプリケーション・ナビゲータから削除します。
テキスト・エディタを使用して、YourImportingProjectName.jpx
ファイルを編集します。
削除するインポート済パッケージを表すContainee要素を、このファイルから削除します。
このとき、削除するパッケージに対する適切なContaineeタグから対応する/Containeeタグまでの間のすべての行(両方のタグの行を含みます)を、ファイルから削除します。
JDeveloperでワークスペースを再び開きます。
注意: プロジェクト内の他のコンポーネントからまだ参照されているインポート済パッケージは、削除しないでください。このようなパッケージを削除すると、プロジェクトを開いたときに例外が発生するか、またはアプリケーションの動作が予測できなくなる可能性があります。インポートしたパッケージのエントリを*.jpx ファイルから手動で削除する場合は、最初に、そのパッケージ内のコンポーネントを参照しているものがないことを確認してください。 |
注意: この項の例では、AdvancedExamples ワークスペースのCustomizedErrorMessages プロジェクトを参照します。ダウンロード方法については、この章の最初にある注意を参照してください。 |
カスタム・メッセージ・バンドルでエラー・コードに対してかわりのメッセージ文字列を提供することにより、組み込まれているADF Business Componentsのエラー・メッセージをカスタマイズできます。次の組込みエラー・メッセージを変更するものとします。
JBO-27014: Attribute Name is Product is required
Oracle Worldwide Supportから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}
は属性の名前に置き換えられます。
カスタム・メッセージのバンドル・ファイルを作成するには、次の手順を実行します。
ビジネス・コンポーネントが含まれるプロジェクトの「プロジェクト・プロパティ」ダイアログで、「ビジネス・コンポーネント」の「オプション」ページを開きます。
ダイアログの下部にある「このプロジェクトで使用するカスタム・メッセージ・バンドル」リストに注目します。
「新規」をクリックします。
「MessageBundleクラスの作成」ダイアログでカスタム・メッセージ・バンドルの名前とパッケージを入力し、「OK」をクリックします。
注意: カスタム・メッセージ・バンドル・ファイルの完全修飾名が「このプロジェクトで使用するカスタム・メッセージ・バンドル」リストに表示されない場合は、「削除」ボタンをクリックした後、「追加」ボタンをクリックして、作成した新しいメッセージ・バンドル・ファイルを追加してください。カスタム・メッセージ・バンドル・ファイルが正しく登録されると、その完全修飾クラス名がリストに表示されます。 |
「OK」をクリックして「プロジェクト・プロパティ」ダイアログを閉じ、新しいカスタム・メッセージ・バンドル・クラスをソース・エディタで開きます。
カスタム・メッセージ・バンドル・クラスの2次元のString
配列を編集し、使用するカスタマイズしたメッセージを入力します。
例25-11は、前記の例で使用したJBO-27014
エラーに対するエラー・メッセージ文字列をオーバーライドするカスタム・メッセージ・バンドル・クラスを示しています。
例25-11 カスタムADF Business Componentsメッセージ・バンドル
package devguide.advanced.customerrs; import java.util.ListResourceBundle; public class CustomMessageBundle extends ListResourceBundle { private static final Object[][] sMessageStrings = new String[][] { {"27014","You must provide a value for {2}"} }; protected Object[][] getContents() { return sMessageStrings; } }
このメッセージをカスタム・メッセージ・バンドル・ファイルに追加した後、ビジネス・コンポーネント・ブラウザを使用してアプリケーションをテストし、必須属性の値を空白にすると、デフォルトのメッセージのかわりにカスタム・エラー・メッセージが表示されます。
JBO-27014: You must provide a value for Name
必要なだけいくつでも、メッセージ・バンドルにメッセージを追加できます。エラー・コード・キーが組込みエラー・メッセージ・コードのいずれかと一致するメッセージは、実行時に、oracle.jbo.CSMessageBundle
メッセージ・バンドルのデフォルト・メッセージのかわりに使用されます。
データベースに制約を設定している場合、制約の違反が発生したときに、ADFアプリケーションでカスタム・エラー・メッセージをエンド・ユーザーに対して提供する場合があります。たとえば、次のDDL文を使用して、SRDemoアプリケーションのPRODUCTS
表にNAME_CANNOT_BEGIN_WITH_X
という名前の制約を追加するものとします。
alter table products add ( constraint name_cannot_begin_with_x check (upper(substr(name,1,1)) != 'X') );
アプリケーションでカスタム・エラー・メッセージを定義するには、制約名をメッセージ・キーとして、カスタム・メッセージ・バンドルにメッセージを追加するだけです。たとえば、前の項で作成したものと同じCustomMessageBundle.java
クラスを使用すると、例25-12は、上で定義したデータベース制約の名前と一致するキーNAME_CANNOT_BEGIN_WITH_X
を持つメッセージを定義した状態を示します。
例25-12 データベース制約違反のエラー・メッセージのカスタマイズ
package devguide.advanced.customerrs; import java.util.ListResourceBundle; public class CustomMessageBundle extends ListResourceBundle { private static final Object[][] sMessageStrings = new String[][] { {"27014","You must provide a value for {2}"}, {"NAME_CANNOT_BEGIN_WITH_X", "The name cannot begin with the letter x!"} }; protected Object[][] getContents() { return sMessageStrings; } }
カスタム・メッセージをデータベース制約違反に割り当てるデフォルト機能が要件を満たさない場合は、独自のカスタム制約エラー処理ルーチンを実装できます。そのためには、ADFトランザクション・クラスに対するカスタム・フレームワーク拡張クラスを作成し、実行時にそれを使用するようアプリケーション・モジュールを構成する必要があります。
ADFトランザクション用のカスタム・フレームワーク拡張クラスを記述するには、例25-13で示すCustomDBTransactionImpl
のようなクラスを作成します。この例では、スローされるDMLConstraintException
エラーに対するカスタム処理を実行するため、トランザクション・オブジェクトのpostChanges()
メソッドをオーバーライドして、super.postChanges()
の呼び出しをtry
/catch
ブロックでラップしています。この簡単な例で実行しているカスタム処理は、ex.setExceptions(null)
を呼び出して、DMLConstraintException
が保持している可能性のあるネストされた詳細な例外をクリアすることのみです。このような処理のかわりに、アプリケーションで必要な他の種類のカスタム例外処理を実行できます。たとえば、カスタム例外がJboException
を直接または間接に拡張している場合は、カスタム例外をスローできます。
例25-13 カスタム・データベース・トランザクション・フレームワーク拡張クラス
package devguide.advanced.customerrs; import oracle.jbo.DMLConstraintException; import oracle.jbo.JboException; import oracle.jbo.common.StringManager; 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()
メソッドをオーバーライドするDatatabaseTransactionFactory
クラスのカスタム実装を提供します。
TransactionFactory
プロパティの値に、このカスタム・トランザクション・ファクトリ・クラスの完全修飾名を構成します。
例25-14は、これを行うカスタム・データベース・トランザクション・ファクトリ・クラスを示しています。フレームワークがデータベース・トランザクション・ファクトリでcreate()
メソッドを呼び出すと、このクラスはCustomDBTransactionImpl
クラスの新しいインスタンスを返します。
例25-14 カスタム・データベース・トランザクション・ファクトリ・クラス
package devguide.advanced.customerrs; import oracle.jbo.server.DBTransactionImpl2; import oracle.jbo.server.DatabaseTransactionFactory; public class CustomDatabaseTransactionFactory extends DatabaseTransactionFactory { public CustomDatabaseTransactionFactory() { } /** * Return an instance of our custom ToyStoreDBTransactionImpl class * instead of the default implementation. * * @return instance of custom CustomDBTransactionImpl implementation. */ public DBTransactionImpl2 create() { return new CustomDBTransactionImpl(); } }
最後の作業として、構成エディタの「プロパティ」タブを使用し、TransactionFactory
プロパティに値devguide.advanced.customerrs.CustomDatabaseTransactionFactory
を割り当てます。この構成を使用してアプリケーションを実行すると、カスタム・トランザクション・クラスが使用されます。
新しいビジネス・コンポーネントを作成するたびに、必要に応じて、既存のものを拡張してオリジナルのカスタマイズ・バージョンを作成できます。たとえば、SRDemoアプリケーションの場合は、図25-9で示されているように、ServiceRequestsByStatus
ビュー・オブジェクトはServiceRequests
ビュー・オブジェクトを拡張して、TheStatus
という名前の名前付きバインド変数が追加され、このバインド変数を参照するようにWHERE
句がカスタマイズされています。
図ではビュー・オブジェクトの例が示されていますが、このコンポーネント継承機能はすべての種類のコンポーネントで使用できます。あるコンポーネントが別のコンポーネントを拡張する場合、拡張されたコンポーネントは親からすべてのメタデータと動作を継承します。拡張されたコンポーネントでは、メタデータとJavaコードの両方を使用して、新しい機能を追加したり、親コンポーネントの既存機能をカスタマイズしたりできます。
注意: この項の例では、AdvancedExamples ワークスペースのBaseProject プロジェクトを参照します。ダウンロード方法については、この章の最初にある注意を参照してください。 |
拡張コンポーネントを作成するには、作成するコンポーネントの種類に対する「新規ギャラリ」のコンポーネント・ウィザードを使用します。たとえば、拡張ビュー・オブジェクトを作成するには、ビュー・オブジェクトの作成ウィザードを使用します。ウィザードの「名前」ページで、新しいコンポーネントの名前とパッケージを指定することに加えて、拡張するコンポーネントの完全修飾名を拡張フィールドで指定します。リストからコンポーネント名を選択するには、拡張フィールドの隣の「参照」ボタンを使用します。その後は、ウィザードの残りのパネルを使用して、通常の方法で拡張コンポーネントの作成を続けます。
これまでに学習したように、作成するADF Business Componentsは、XMLコンポーネント定義とオプションのJavaクラスで構成されます。別のコンポーネントを拡張するコンポーネントを作成すると、JDeveloperは、拡張されたコンポーネントのXMLコンポーネント定義と生成されるJavaコードの両方に、このコンポーネント継承を反映します。
JDeveloperは、新しいコンポーネントのXMLコンポーネント定義のルート・コンポーネント要素にExtends
属性を追加することで、親コンポーネントの名前を記録します。追加された新しい宣言機能、または親コンポーネントの定義のオーバーライドされた部分は、拡張コンポーネントのXMLコンポーネント定義に記述されます。これに対し、親コンポーネントから純粋に継承されたメタデータは、拡張コンポーネントでは繰り返し記述されません。
例25-15は、ServiceRequstsByStatus
ビュー・オブジェクトに対するServiceRequstsByStatus.xml
XMLコンポーネント定義を示しています。ViewObject要素のExtends
属性、拡張されたビュー・オブジェクトで追加されたバインド変数に関連するVariable要素、およびStatusCode
バインド変数を参照するように変更されたWHERE句のWhere
属性のオーバーライドされた値に注意してください。
例25-15 XMLディスクリプタに親が反映された拡張コンポーネント
<ViewObject Name="ServiceRequestsByStatus" Extends="oracle.srdemo.model.queries.ServiceRequests" Where="((ServiceRequest.CREATED_BY = CreatedByUser.USER_ID) AND (ServiceRequest.ASSIGNED_TO = AssignedToUser.USER_ID(+))) AND (ServiceRequest.PROD_ID = Product.PROD_ID) AND STATUS LIKE NVL(:StatusCode,'%')" OrderBy="REQUEST_DATE DESC" BindingStyle="OracleName" CustomQuery="false" ComponentClass="oracle.srdemo.model.queries.ServiceRequestsByStatusImpl" FetchMode="FETCH_AS_NEEDED" UseGlueCode="false" > <Variable Name="StatusCode" Kind="where" Type="java.lang.String" DefaultValue="%" > </Variable> </ViewObject>
拡張コンポーネントでカスタムJavaコードを有効にすると、親コンポーネントの各Javaクラスを拡張するためのJavaクラスが自動的に生成されます。これにより、拡張コンポーネントでは、必要に応じて、親コンポーネントのプログラム的な動作のすべての面をオーバーライドできます。親コンポーネントが、独自のカスタムJavaクラスを持たないXMLのみのコンポーネントである場合は、拡張コンポーネントのJavaクラスは、親が実行時に使用するベースJavaクラスを拡張します。これは、oracle.jbo.server
パッケージのデフォルトのADF Business Componentsフレームワーク・クラス、または親コンポーネントの拡張ダイアログで指定されている場合は独自のフレームワーク拡張クラスなどになります。
さらに、拡張コンポーネントがアプリケーション・モジュールまたはビュー・オブジェクトで、クライアント・インタフェースを有効にしてある場合は、拡張コンポーネントのクライアント・インタフェースが自動的に生成され、親コンポーネントの各クライアント・インタフェースが拡張されます。親コンポーネントのクライアント・インタフェースが存在しない場合は、拡張コンポーネントのクライアント・インタフェースは、oracle.jbo
パッケージの適切なベースADF Business Componentsインタフェースを直接拡張します。
拡張コンポーネントは親のカスタマイズ・バージョンであるため、親コンポーネントのJavaクラスまたはクライアント・インタフェースで動作するように作成したコードは、親コンポーネントまたはそのカスタマイズ・バージョンで問題なく動作します。
たとえば、次のようなカスタムJavaクラスとクライアント・インタフェースを含むベースProducts
ビュー・オブジェクトがあるものとします。
クラスProductsImpl
行クラスProductsRowImpl
インタフェースProducts
行インタフェースProductsRow
Products
を拡張するProductsByName
ビュー・オブジェクトを作成した場合、ベース・コンポーネントのクラスとインタフェースを使用して、Products
とProductsByName
の両方を操作できます。
例25-16は、Products
、ProductsRow
、ProductsByName
およびProductsByNameRow
の各クライアント・インタフェースで動作するテスト用のクライアント・プログラムです。この例については、次のことに注意してください。
親のProducts
インタフェースを、それを拡張するProductsByName
ビュー・オブジェクトを処理するために使用できます。
別の方法として、ProductsByName
ビュー・オブジェクトのインスタンスを、独自のさらに限定的なProductsByName
クライアント・インタフェースにキャストすることもできます。
キャストしてProductsByNameRow
インタフェースに固有のメソッドを呼び出す前に、本当に行ProductsRow
がより限定的なProductsByNameRow
のインスタンスであるかどうかをテストできます。
例25-16 親コンポーネントと拡張コンポーネントの使用
package devguide.advanced.extsub; /* imports omitted */ public class TestClient { public static void main(String[] args) { String amDef = "devguide.advanced.extsub.ProductModule"; String config = "ProductModuleLocal"; ApplicationModule am = Configuration.createRootApplicationModule(amDef,config); Products products = (Products)am.findViewObject("Products"); products.executeQuery(); ProductsRow product = (ProductsRow)products.first(); printAllAttributes(products,product); testSomethingOnProductsRow(product); // 1. You can use parent Products interface for ProductsByName products = (Products)am.findViewObject("ProductsById"); // 2. Or cast it to its more specific ProductsByName interface ProductsByName productsById = (ProductsByName)products; productsById.setProductName("Ice"); productsById.executeQuery(); product = (ProductsRow)productsById.first(); printAllAttributes(productsById,product); testSomethingOnProductsRow(product); am.getTransaction().rollback(); Configuration.releaseRootApplicationModule(am,true); } private static void testSomethingOnProductsRow(ProductsRow product) { try { // 3. Test if row is a ProductsByNameRow before casting if (product instanceof ProductsByNameRow) { ProductsByNameRow productByName = (ProductsByNameRow)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 'Products' ProdId=100 Name=Washing Machine W001 Checksum=I am the Product Class Setting the Name attribute to 'Q' succeeded. Printing attribute for a row in VO 'ProductsById' ProdId=119 Name=Ice Maker I012 Checksum=I am the Product Class SomeExtraAttr=SomeExtraAttrValue ## Called someExtraFeature of ProductsByNameRowImpl Setting the Name attribute to 'Q' succeeded.
注意: この例では、ProductsはProduct エンティティ・オブジェクトに基づくエンティティ・ベースのビュー・オブジェクトです。Product エンティティ・オブジェクトには、一時的属性Checksum が含まれ、I am the Product classという文字列を返します。これが含まれる理由の詳細は、25.10項「提供されるアプリケーションでの拡張コンポーネントの置換え」の例を参照してください。 |
拡張コンポーネントを作成するとき、拡張コンポーネントの「Java」ページの「クラスの拡張」ボタンは無効になります。これは、JDeveloperが親コンポーネントの適切なクラスを自動的に拡張するため、別のクラスをユーザーが選択できるようにしても意味がないためです。
拡張エンティティ・オブジェクトを作成するときは、新しい属性、新しいアソシエーション、新しいバリデータ、および新しいカスタム・コードを導入できます。既存の属性の特定の宣言的部分、および親コンポーネントのクラスのメソッドをオーバーライドできます。
拡張ビュー・オブジェクトを作成するときは、新しい属性、新しいビュー・リンク、新しいバインド変数、および新しいカスタム・コードを導入できます。既存の属性の特定の宣言的部分、および親コンポーネントのクラスのメソッドをオーバーライドできます。
拡張アプリケーション・モジュールを作成するときは、新しいビュー・オブジェクト・インスタンスまたは新しいネストされたアプリケーション・モジュール・インスタンス、および新しいカスタム・コードを導入できます。親コンポーネントのクラスのメソッドをオーバーライドすることもできます。
拡張エンティティ・オブジェクトまたはビュー・オブジェクトで新しい属性を追加する場合、属性索引番号は親コンポーネントを規準にして計算されます。たとえば、前記のProducts
ビュー・オブジェクトについて考えます。カスタム・ビュー行クラスを有効にする場合、ProductsRowImpl.javaクラスで定義される次のような属性索引定数が含まれる場合があります。
public class ProductsRowImpl extends ViewRowImpl implements ProductsRow { public static final int PRODID = 0; public static final int NAME = 1; public static final int CHECKSUM = 2; //etc. }
ProductsByNameのような拡張ビュー・オブジェクトを作成するとき、そのビュー・オブジェクトでSomeExtraAttr
のような属性を追加し、カスタム・ビュー行クラスを有効にした場合、その属性定数は、親コンポーネントの属性定数の最大値を規準にして計算されます。
public class ProductsByNameRowImpl extends ProductsRowImpl implements ProductsByNameRow { public static final int MAXATTRCONST = ViewDefImpl.getMaxAttrConst("devguide.advanced.extsub.Products"); public static final int SOMEEXTRAATTR = MAXATTRCONST;
追加属性の索引値は、MAXATTRCONST+1
、MAXATTRCONST+2
などになります。
拡張コンポーネントを定義した後で、拡張コンポーネントが継承している親コンポーネントを変更できます。その場合、次の方法での変更が可能です。
アプリケーション・ナビゲータで拡張コンポーネントを選択
プロパティ・インスペクタを使用してExtends
プロパティを変更
ただし、現時点では、この手法を使用して、どの親からも継承しないように拡張コンポーネントを変更することはできません。この制限に対する例外はエンティティ・オブジェクトです。エンティティ・オブジェクトのコンポーネント・エディタの「名前」ページには拡張フィールドがあり、必要な場合には空白にできます。他のすべての拡張コンポーネントについては、親コンポーネントを拡張しないようにするには、削除してから作成しなおす必要があります。
ソリューションの各クライアントがオンサイトでカスタマイズする必要のあるパッケージ化されたアプリケーションを配布する場合のために、ADF Business Componentsでは、この作業を簡単にする便利な機能が提供されています。
注意: この項の例では、AdvancedExamples ワークスペースのBaseProject およびExtendAndSubstitute プロジェクトを参照します。ダウンロード方法については、この章の最初にある注意を参照してください。 |
通常、オンサイトでのアプリケーションのカスタマイズは、配布されたアプリケーションのソース・コードを直接変更することで行います。この方法の短所が明らかになるのは、元のアプリケーションのパッチや新機能リリースをクライアントに提供するときです。基になるアプリケーションのソース・コードに適用されていたカスタマイズは、パッチまたは更新を行った後のアプリケーションに、わざわざ適用しなおす必要があります。この方法では、アプリケーションのカスタマイズやメンテナンスに費用がかかるだけでなく、以前のカスタマイズを新しいリリースに適用しなおすときの人為的エラーにより不具合が発生する可能性があります。
ADF Business Componentsが提供する優れたコンポーネント・ベースの方法を使用してアプリケーションをカスタマイズすると、基になっているアプリケーションのソース・コードを変更する必要はなく、ソース・コードにアクセスする必要さえありません。提供されたアプリケーションのカスタマイズは、次の方法で行います。
基になるアプリケーションのコンポーネントのパッケージを、新しいプロジェクトにインポートします。
アプリケーションのカスタマイズを適用する新しいコンポーネントを作成し、必要に応じて基のアプリケーションの適切な親コンポーネントを拡張します。
グローバルなコンポーネント置換のリストを定義し、カスタマイズしたコンポーネントの名前を設定して、基になるアプリケーションの適切な親コンポーネントを置き換えます。
ユーザーが定義されたグローバル・コンポーネント置換リストを使用し、配布されたアプリケーションを実行すると、配布されたアプリケーションは、コードを変更することなく、カスタマイズされたアプリケーション・コンポーネントを使用します。元のアプリケーションのパッチまたは更新バージョンが提供されると、次にアプリケーションを再起動するときにコンポーネントのカスタマイズが更新バージョンに適用されるので、カスタマイズを適用しなおす必要はありません。
グローバルなコンポーネント置換を定義するには、基になるアプリケーションからインポートしたコンポーネントを基にして拡張コンポーネントを作成したプロジェクトの、「プロジェクト・プロパティ」ダイアログの「ビジネス・コンポーネント: 置換」ページを使用します。図25-10で示されているように、各コンポーネントの置換を定義するには、次のようにします。
「使用可能」リストで、基になるアプリケーションのコンポーネントを選択します。
「置換する対象」リストで、置き換えるカスタマイズした拡張コンポーネントを選択します。
「追加」をクリックします。
注意: 基になるアプリケーションのコンポーネントのうち、基のコンポーネントを直接または間接に継承する拡張コンポーネントがあるもののみを置換できます。 |
YourExtendsAndSubstitutesProject
という名前のプロジェクトでグローバル・コンポーネント置換リストを定義すると、置換リストは、ソース・パスのルート・ディレクトリにあるYourExtendsAndSubstitutesProject
.jpx
に保存されます。
例25-17で示されているように、このファイルには、置換されるコンポーネントごとに1つずつ、Substitute要素が含まれます。
例25-17 プロジェクトのJPXファイルに保存されるコンポーネント置換リスト
<JboProject Name="ExtendAndSubstitute" SeparateXMLFiles="true" PackageName="" > <Containee Name="anotherpkg" FullName="devguide.advanced.anotherpkg.anotherpkg" ObjectType="JboPackage" > </Containee> <Containee Name="extsub" FullName="devguide.advanced.extsub.extsub" ObjectType="JboPackage" > <DesignTime> <Attr Name="_LocationURL" Value="../../BaseProject/deploy/BaseProjectCSMT.jar" /> </DesignTime> </Containee> <Substitutes> <Substitute OldName="devguide.advanced.extsub.Product" NewName="devguide.advanced.anotherpkg.CustomizedProduct" /> <Substitute OldName="devguide.advanced.extsub.Products" NewName="devguide.advanced.anotherpkg.CustomizedProducts" /> </Substitutes> </JboProject>
元のアプリケーションが置換されたコンポーネントを使用するよう指定するには、Javaシステム・プロパティFactory-Substitution-List
を定義し、その値に、置換リストが格納されている*.jpx
ファイルに対するプロジェクトの名前を設定します。値には、*.jpr
または*.jpx
拡張子を除いたプロジェクト名のみを設定します。
25.9.3.1項「親のクラスとインタフェースは拡張コンポーネントの操作にも使用できる」で説明されている、Product
エンティティ・オブジェクトとProducts
ビュー・オブジェクトをカスタマイズする簡単な例について考えます。カスタマイズのため、次のようにしてExtendsAndSubstitutes
という名前の新しいオブジェクトを作成するものとします。
ベース・コンポーネントを含むJARファイルに対するライブラリを定義します。
Product
およびProducts
を含むパッケージをインポートします。
CustomizedProduct
およびCustomizedProducts
という個別のパッケージ名で、新しい拡張コンポーネントを作成します。
拡張コンポーネントを使用するためのコンポーネント置換リストを定義します。
拡張コンポーネントを作成するときは次のようにします。
CustomizedProductsビュー・オブジェクトにExtraViewAttribute
という名前の新しいビュー属性を追加します。
新しい検証ルールをCustomizedProduct
エンティティ・オブジェクトに追加し、Q
という製品名は使用できないようにします。
CustomizedProduct.java
クラスのgetChecksum()
メソッドをオーバーライドし、I am the CustomizedProduct Classを返すようにします。
Factory-Substitution-List
Javaシステム・プロパティを定義し、値ExtendsAndSubstitutes
を設定して、例25-16とまったく同じテスト用のクライアント・クラスを実行すると、サンプルの出力は、置換されたコンポーネントの使用を反映して次のように変化します。
Printing attribute for a row in VO 'Products' ProdId=100 Name=Washing Machine W001 Checksum=I am the CustomizedProduct Class ExtraViewAttribute=Extra Attr Value The name cannot be Q! Printing attribute for a row in VO 'ProductsById' ProdId=119 Name=Ice Maker I012 Checksum=I am the CustomizedProduct Class SomeExtraAttr=SomeExtraAttrValue ## Called someExtraFeature of ProductsByNameRowImpl The name cannot be Q!
例25-16からの出力と比較すると、ファクトリ置換リストがあるため、元のテスト・プログラムのProducts
ビュー・オブジェクトにExtraViewAttribute
が追加され、Checksum
属性の値I am the CustomizedProduct Classが表示されるようになり、製品名として値Qを割り当てられなくなっています。これらのコンポーネント動作の変更は、提供されたコンポーネントの元のJavaまたはXMLソース・コードを変更することなく行われました。