Oracle Fusion Middleware Oracle Application Development Framework Fusion開発者ガイド 11gリリース1 (11.1.1.7.0) B52028-05 |
|
前 |
次 |
この章では、すべてのタイプのADFビジネス・コンポーネントにカスタム・コードを組み込んだり、ADFビジネス・コンポーネント・フレームワークの動作を拡張したりする場合に使用できる高度な手法について説明します。
この章の内容は次のとおりです。
フレームワーク・ベースの開発の強力な機能の1つは、基本フレームワークを拡張して、組込み機能の動作を変更したり、すべてのアプリケーションが使用できる新機能を追加したりできることです。
次のことが必要な場合に、ADFビジネス・コンポーネント・フレームワークの動作を拡張するためのカスタム・コードを組み込む拡張クラスを作成することにより、ADFビジネス・コンポーネント・フレームワークのベース・クラスを拡張できます。
追加の汎用的な機能で組込み機能を補強する
組込み機能の動作方法を変更する
検出された不具合を一般的な方法で回避する
ADFビジネス・コンポーネント・フレームワーク拡張クラスは、あらゆるタイプのADFビジネス・コンポーネントに対するフレームワークのいずれかのベース・クラスを拡張することを目的としてユーザーが作成するJavaクラスです。
カスタマイズを行わずに使用した場合、ビジネス・コンポーネントは、XMLコンポーネント定義で完全に定義されており、コンポーネントのカスタムJavaコードやJavaクラス・ファイルをまったく必要とせず、十分な機能を備えています。ADFビジネス・コンポーネントのコンポーネントの組込み機能を拡張する必要がない場合、また、組込みイベントを処理するカスタム・コードを記述する必要がない場合、このXMLのみの方法でコンポーネントを使用できます。ただし、ADFビジネス・コンポーネント・フレームワークを拡張する場合も、JDeveloperでXMLドキュメントを操作できます。
アプリケーション固有のビジネス・コンポーネントの開発を始める前に、フレームワーク拡張クラスの完全なレイヤーを作成し、そのレイヤーをデフォルトで使用するようプロジェクト・レベルで設定することを検討することをお薦めします。当初はこれらのフレームワーク拡張クラスにカスタム・コードを追加することを想定していないかもしれませんが、このような設定は実際にカスタマイズが必要になった場合に役に立ちます。
これにより、新しい汎用機能や、組込み機能の補強や、不具合の一般的な回避といったことが、プロジェクトの途中ですべてのエンティティ・オブジェクトで必要になった場合でも、大きな不都合が起きることを回避できます。
フレームワーク拡張クラスを作成した後は、ベース・クラスではなくカスタマイズしたフレームワーク・クラスに基づいて、新しいADFコンポーネントを作成できます。もちろん、既存のコンポーネントの定義を更新し、新しいフレームワーク拡張クラスを使用するようにすることもできます。
注意: この章で説明する例を試すには、2.4.3項「AdvancedExamplesアプリケーション・ワークスペースのスタンドアロン・アプリケーション」の説明に従ってFusion Order Demoアプリケーションの |
カスタム・コードを追加してADFビジネス・コンポーネント・フレームワークの基本機能を拡張する必要がある場合は、作成するADFビジネス・コンポーネントの任意の主要タイプに対し、カスタムJavaクラスを有効にできます。コンポーネントのカスタム・クラスの生成は、JDeveloperの対応する概要エディタの「Java」ページで有効にします。このオプションを有効にすると、JDeveloperにより、構成可能な命名の標準に準拠したコンポーネントに関連するカスタム・クラスのJavaソース・ファイルが作成されます。このクラスの名前は、コンポーネントのXMLドキュメントに記録され、コンポーネントで必要とされるカスタムJavaコードは、このクラス内に記述できます。
フレームワーク拡張クラスを作成するには:
フレームワーク拡張クラスを組み込むプロジェクトを識別します。
あるプロジェクトのコンポーネントのみで使用されることが明らかな場合は、ビジネス・サービス・コンポーネントと同じプロジェクトに作成できます。あるいは、複数のFusion Webアプリケーションでフレームワーク拡張クラスを再利用する場合は、モデル・プロジェクトを別に作成して、フレームワーク拡張クラスを格納することもできます。
BC4Jランタイム・ライブラリがプロジェクトのライブラリ・リストに含まれることを確認します。
「プロジェクト・プロパティ」ダイアログの「ライブラリとクラスパス」ページを使用してこれを検証し、ない場合はライブラリを追加します。
「新規ギャラリ」の「一般」カテゴリで、「Javaクラス」を選択して新規クラスを作成します。
「Javaクラスの作成」ダイアログで、「拡張」フィールドのoracle.jbo.server
パッケージから適切なフレームワーク・ベース・クラスを指定します。
図37-1は、com.yourcompany.fwkext
パッケージにCustomAppModuleImpl
という名前のカスタム・フレームワーク拡張クラスを作成して基本アプリケーション・モジュール・コンポーネントの機能をカスタマイズする様子を示しています。目的のベース・クラスをすばやく見つけるには、拡張フィールドの隣の「参照」ボタンを使用して、JDeveloperの「クラス・ブラウザ」を起動します。「検索」タブを使用し、クラス名の一部(ワイルドカードとして*
を使用できます)を入力してクラスのリストをすばやく絞り込み、目的のクラスを見つけることができます。
「OK」をクリックすると、ユーザーが選択したパッケージ名に対応するプロジェクトのソース・パスのディレクトリに、カスタム・フレームワーク拡張クラスが作成されます。
注意: 一部のADFビジネス・コンポーネント・コンポーネント・クラスは、サーバー側とリモート・クライアント・バージョンの両方に存在します。たとえば、JDeveloperの「クラス・ブラウザ」を使用して、「検索」タブの「クラス名を一致」フィールドに |
新しいフレームワーク拡張クラスを作成しても、アプリケーションはそれを自動的には使用しません。フレームワーク拡張クラスを使用するプロジェクトのコンポーネントを指定する必要があります。次では、独自のフレームワーク拡張クラスに基づいて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
クラスの使用の詳細は、37.7.5.2項「カスタム・データベース・トランザクション・クラスを使用するためのアプリケーション・モジュールの構成」を参照してください。
注意: 便宜のため、Fusion Order Demoアプリケーションの |
完璧を期すため、次のクラスについてもカスタマイズしたフレームワーク・クラスを作成する場合もありますが、これらのクラスの内容をオーバーライドすることはほとんど必要がないことに注意してください。
public class CustomViewDefImpl extends ViewDefImpl
public class CustomEntityCache extends EntityCache
public class CustomApplicationModuleDefImpl extends ApplicationModuleDefImpl
任意のADFビジネス・コンポーネント・ウィザードまたはエディタの「Java」ページを使用して、ADFコンポーネントのベース・クラスを設定できます。
作業を始める前に、次のようにします。
37.1.1項「フレームワーク拡張クラスの作成方法」の説明に従って、フレームワーク拡張クラスを作成します。
フレームワーク拡張クラスを別のプロジェクトに作成した場合は、ビジネス・コンポーネントを含むプロジェクトの「プロジェクト・プロパティ」ダイアログの「依存性」ページで、「ビルド出力」を選択して、フレームワーク拡張プロジェクトをプロジェクト依存関係として追加します。
フレームワーク拡張クラスをJavaアーカイブ(JAR)ファイルにパッケージする場合は、JARファイルを参照するための名前付きライブラリ定義を作成し、ビジネス・コンポーネントを含むプロジェクトのライブラリ・リストにもそのライブラリを加えます。ライブラリがない場合に作成するには、「ツール」→「ライブラリの管理」メイン・メニュー項目から開くことのできる「ライブラリの管理」ダイアログを使用します。プロジェクトのライブラリ・リストを確認または変更するには、「プロジェクト・プロパティ」ダイアログの「ライブラリ」ページを使用します。
フレームワーク・クラスを参照できることを確認した後、ADFコンポーネントを作成できます。すべてのADFビジネス・コンポーネント・ウィザードおよびエディタの「Java」ページには同じ「クラスの拡張」ボタンが表示されるので、この方法を使用して、新規コンポーネントと既存コンポーネントの両方に対して目的のフレームワーク拡張ベース・クラスを選択できます。
作成するフレームワーク拡張クラスのレベルの数には固定の制限はありません。たとえば、会社で作成するすべてのFusion Webアプリケーションのすべてのアプリケーション・モジュールで使用する会社レベルのCustomAppModuleImpl
を作成した後、一部のプロジェクト・チームでそのフレームワーク拡張クラスをさらにカスタマイズすることが必要になったとします。そのようなチームでは、CustomAppModuleImpl
を拡張するSomeProjectCustomAppModuleImpl
クラスを作成し、プロジェクト固有のカスタム・アプリケーション・モジュールのコードをそこに含めることができます(例37-1を参照)。
例37-1 カスタム・クラスの拡張
public class SomeProjectCustomAppModuleImpl extends CustomAppModuleImpl { /* * Custom application module code specific to the * "SomeProject" project goes here. */ }
その後、この特定のプロジェクトの実装の一部として作成されるアプリケーション・モジュールでは、CustomAppModuleImpl
のかわりにSomeProjectCustomAppModuleImpl
をベース・クラスとして使用できます。
フレームワーク拡張クラスに基づいてADFコンポーネントを作成するには:
アプリケーション・ナビゲータで、目的のコンポーネントをダブルクリックします。
概要エディタで、「Java」ナビゲーション・タブをクリックし、「Javaオプションの編集」ボタンをクリックします。
「Javaオプションの選択」ダイアログで、「クラスの拡張」をクリックします。
「ベース・クラスのオーバーライド」ダイアログで、オーバーライドするフレームワーク・ベース・クラスの完全修飾名を入力します。「参照」ボタンを使用して、JDeveloperクラス・ブラウザでクラスをすばやく検索することもできます。
クラス・ブラウザを使用してコンポーネントのカスタム・ベース・クラスを選択する際、使用可能なクラスが自動的にフィルタリングされて、適切なクラスのみが表示されます。たとえば、図37-2で「参照」をクリックしてアプリケーション・モジュールの「オブジェクト」のベース・クラスを選択するときは、oracle.jbo.server.ApplicationModule
クラスを直接または間接に拡張する現在のプロジェクトのライブラリ・リストで使用できるクラスのみがリストに表示されます。探しているクラスが表示されない場合は、正しくないベース・クラスを拡張しているか、または誤ったコンポーネント・クラス名をオーバーライドするように選択しています。
フレームワーク拡張クラスの特定のセットを任意のプロジェクトの標準として使用する場合、「プロジェクト・プロパティ」ダイアログを使用して各コンポーネント・タイプの優先ベース・クラスを定義できます。ベース・クラスに対するこれらの優先指定を設定しても、プロジェクト内の既存コンポーネントには影響ありませんが、コンポーネント・ウィザードが作成する新しいコンポーネントにはこの設定が使用されます。
フレームワーク拡張クラスのプロジェクト・レベルのプリファレンスを定義するには:
アプリケーション・ナビゲータで、拡張クラスを含むモデル・プロジェクトを右クリックし、「プロジェクト・プロパティ」を選択します。
「プロジェクト・プロパティ」ダイアログのツリーで「ビジネス・コンポーネント」→「ベース・クラス」を展開します。
「ビジネス・コンポーネント」ページのアプリケーション・モジュール・オブジェクトのクラス名フィールドにそのクラスの完全修飾名を入力します。
たとえば、プロジェクトで作成された新規アプリケーション・モジュールがデフォルトでCustomAppModuleImpl
クラスを使用するように指定するには、図37-3に示すように、componentNameオブジェクトのクラス名フィールドにそのクラスの完全修飾名を入力します。
JDeveloperで作成するそれぞれの新しいプロジェクトに同じベース・クラス設定を適用する場合、「プリファレンス」ダイアログを使用してグローバル・レベルで設定を定義できます。グローバル・レベルで指定するベース・クラスは、ADFコンポーネントを含む既存のプロジェクトを変更しません。
フレームワーク拡張クラスのグローバル・プリファレンスを定義するには:
「ツール」で、「プリファレンス」を選択します。
「プリファレンス」ダイアログのツリーで「ビジネス・コンポーネント」→「ベース・クラス」を展開します。
「ビジネス・コンポーネント」ページのcomponentNameオブジェクトのクラス名フィールドにそのクラスの完全修飾名を入力します。
ページには、各コンポーネント・タイプに対して優先ベース・クラスを指定するために、図37-3に示すものと同じオプションが表示されます。
作成するADFコンポーネントがカスタム・フレームワーク拡張クラスを拡張するときは、選択したカスタム・クラス名を反映するように、JDeveloperがXMLコンポーネントの定義を更新します。
たとえば、CustomAppModuleImpl
をカスタム・アプリケーション・モジュール・ベース・クラスにして、com.yourcompany.yourapp
パッケージにYourService
アプリケーション・モジュールを作成したものとします。コンポーネントをカスタムJavaファイルのないXMLのみのコンポーネントにした場合、そのXMLコンポーネント定義(YourService.xml)は、例37-2のようになります。実行時にAppModuleタグのComponentClass
属性の値が読み取られて、コンポーネントを表すために使用するJavaクラスが識別されます。
例37-2 XMLコンポーネント定義に記録されるカスタム・ベース・クラス名
<AppModule Name="YourService" ComponentClass="com.yourcompany.fwkext.CustomAppModuleImpl" > <!-- etc. --> </AppModule>
図37-4は、XMLのみのYourService
アプリケーション・モジュールとカスタム拡張クラスの関係を示したものです。実行時には、ApplicationModuleImpl
クラスから基本動作を継承するCustomAppModuleImpl
クラスを使用します。
コンポーネントでJavaクラスが必要な場合は、前の項で示したように、コンポーネント・エディタの「Java」ページを開き、適切なチェック・ボックスを選択して有効にします。たとえば、YourServer
アプリケーション・モジュールに対してカスタム・アプリケーション・モジュール・クラスを有効にすると、適切なYourServiceImpl.java
クラスが作成されます。例37-3に示すように、コンポーネントのXMLコンポーネント定義も更新されて、カスタム・コンポーネント・クラスの名前が反映されます。
例37-3 XMLコンポーネント定義に記録されたカスタム・コンポーネント・クラス
<AppModule Name="YourService" ComponentClass="com.yourcompany.yourapp.YourServiceImpl" > <!-- etc. --> </AppModule>
また、例37-4に示すように、コンポーネントのカスタムJavaクラスのextends
句も変更されて、新しいカスタム・フレームワーク・ベース・クラスが反映されます。
例37-4 新しいベース・クラスを反映するためのコンポーネントのカスタム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. }
図37-5は、カスタムYourServiceImpl
クラスを含むYourService
アプリケーション・モジュールとフレームワーク拡張クラスの関係を示したものです。実行時には、ベースApplicationModuleImpl
クラスを拡張するCustomAppModuleImpl
フレームワーク拡張クラスから基本動作を継承するYourServiceImpl
クラスを使用します。
カスタムJavaクラスを含むADFコンポーネントがあり、後でコンポーネントをフレームワーク拡張クラスに基づくように変更する場合は、「Javaオプションの選択」ダイアログの「クラスの拡張」ボタンを使用して、コンポーネントのベース・クラスを変更してください。このダイアログは、コンポーネントの概要エディタの「Java」ページから開けます。これを行うと、コンポーネントのXMLコンポーネント定義が新しいベース・クラスを反映して更新され、さらにコンポーネントのカスタムJavaクラスのextends
句が変更されます。
注意: コンポーネント・エディタを使用せずに手動で |
フレームワーク拡張レイヤーのクラスを含むJARファイルを作成するには、「デプロイメント・プロファイルの作成 -- JARファイル」ダイアログを使用します。これは、「新規ギャラリ」の「一般」→「デプロイメント・プロファイル」カテゴリで使用できます。
デプロイメント・プロファイルにFrameworkExtensions
のような名前を設定し、「OK」をクリックします。デフォルトでは、JARファイルにはプロジェクト内のすべてのクラス・ファイルが格納されます。これが求める結果であるため、「JARデプロイメント・プロファイルのプロパティ」ダイアログが表示されたら、「OK」をクリックして終了してかまいません。
注意: ADFライブラリJARアーカイブ・タイプを使用してフレームワーク拡張機能レイヤーをパッケージ化しないでください。再使用可能な構成要素をパッケージ化してJDeveloperリソース・カタログで共有する場合は、ADFライブラリJARファイルを作成します。ADFコンポーネントおよびADFライブラリJARアーカイブ・タイプでの作業の詳細は、33.2項「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ファイルのライブラリを定義するには:
JDeveloperのメイン・メニューで「ツール」→「ライブラリの管理」を選択します。
「ライブラリの管理」ダイアログで「ライブラリ」タブを選択します。
ツリーで「User」フォルダを選択し、「新規」ボタンをクリックします。
表示される「ライブラリの作成」ダイアログで、ライブラリにFramework Extension Layerという名前を設定し、「クラス・パス」ノードを選択して、「エントリの追加」をクリックします。
表示される「パス・エントリの選択」ダイアログで、フレームワーク拡張コンポーネントのクラス・ファイルを含むFrameworkExtensions.jar
ファイルを選択し、「選択」をクリックします。
「ソースパス」ノードを選択し、「エントリの追加」をクリックします。
表示される「パス・エントリの選択」ダイアログで、フレームワーク拡張クラスのソース・ファイルが存在する..\FrameworkExtensions\src
ディレクトリを選択し、「選択」をクリックします。
「OK」をクリックして「ライブラリの作成」ダイアログを閉じ、新しいライブラリを定義します。
終了すると、図37-6に示すように、新しいユーザー定義ライブラリFramework Extension Layerが表示されます。この後は、ビジネス・サービスを作成するプロジェクトのライブラリ・リストにこのライブラリを追加することで、優先使用するコンポーネント・ベース・クラスとしてカスタム・フレームワーク拡張クラスが参照されるようになります。
フレームワーク拡張クラスで行う共通のタスクの1つは、カスタム・アプリケーション機能の実装です。フレームワーク拡張コードは特定の種類の全コンポーネントで使用されるように記述するので、これらのクラスで記述するコードでは、通常、一般的な方法でコンポーネントの属性を処理する必要があります。これに対応するため、ADFでは、実行時にコンポーネントのメタデータにアクセスできるAPIが提供されています。また、カスタム・メタデータのプロパティとコンポーネントまたは属性を関連付ける機能も提供されています。作成する汎用的なフレームワーク拡張コードでは、ランタイム・メタデータとカスタム・プロパティを使用して、必要な場合には特定のカスタム・プロパティが存在する場合にのみ使用される、一般的な機能を作成できます。
注意: この項の例では、Fusion Order Demoアプリケーションの |
図37-7は、ビュー・オブジェクトおよびエンティティ・オブジェクトに関するランタイム・メタデータにアクセスするためにADFで提供されている3つの主要なインタフェースを示しています。ViewObject
インタフェースは、StructureDef
インタフェースを拡張します。エンティティ定義を表すクラス(EntityDefImpl
)も、このインタフェースを実装します。名前が示すように、StructureDef
は構造とコンポーネントを定義しており、ビュー・オブジェクト行またはエンティティ行の各属性に関するランタイム・メタデータを提供するAttributeDef
オブジェクトのコレクションへのアクセスを提供します。AttributeDef
を使用することで、それに付随するAttributeHints
オブジェクトにアクセスし、表示ラベル、フォーマット・マスク、ツールチップなどのヒントを参照できます。
6.4.1項「ビュー・オブジェクトのデフォルト行セットを使用した操作のViewObjectインタフェース・メソッド」で説明したように、読取り専用のビューの場合、findByKey()
メソッドおよびsetCurrentRowWithKey
組込み操作は、setManageRowsByKey(true)
を呼び出すようにビュー・オブジェクトのcreate()
メソッドをオーバーライドした場合にのみ動作します。多くの読取り専用ビュー・オブジェクトを作成する場合、このような詳細を記憶しておくのは大変であるため、ビュー・オブジェクトのフレームワーク拡張クラスで自動化する機能の有力な候補となります。
FrameworkExtensions
プロジェクトには、アプリケーション内の全ビュー・オブジェクトに対するベース・クラスであるFODViewObjectImpl
クラスが含まれるものとします。ビュー・オブジェクトに対するこのフレームワーク拡張クラスは、例37-5に示すように、ViewObjectImpl
ベース・クラスを拡張し、create()
メソッドをオーバーライドして、このタスクを自動化します。実行時にビュー・オブジェクト・インスタンスを作成するとき、super.create()
を呼び出してデフォルトのフレームワーク機能を実行した後、このコードは、ビュー・オブジェクトが少なくとも1つの属性がキー属性としてマークされている読取り専用のビュー・オブジェクトかどうかを検査します。そうである場合は、setManageRowsByKey(true)
を呼び出します。
isReadOnlyNonEntitySQLViewWithAtLeastOneKeyAttribute()
ヘルパー・メソッドは、次の条件の組合せを検査することで、ビュー・オブジェクトが読取り専用かどうかを判定します。
isFullSql()
がtrue
このメソッドは、ビュー・オブジェクトの問合せが、含まれるエンティティ・オブジェクトの慣用名に基づいて選択リストを自動的に生成するのではなく、開発者によって完全に指定されている場合は、trueを返します。
getEntityDefs()
がnull
このメソッドは、ビュー・オブジェクトのエンティティ・オブジェクト慣用名を表すEntityDefImpl
オブジェクトの配列を返します。null
を返す場合は、ビュー・オブジェクトにはエンティティ・オブジェクトの慣用名がありません。
次に、getAttributeDefs()
メソッドが返すAttributeDef
配列をループして、ビュー・オブジェクトにキー属性があるかどうかを判定します。リストのいずれかの属性定義に対してisPrimaryKey()
メソッドがtrueを返す場合、そのビュー・オブジェクトにはキーがあります。
例37-5 キーによる管理行の設定の自動化
public class FODViewObjectImpl 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()
メソッドを使用することで、特定のカスタム・メタデータ・プロパティの存在または値に基づいて、条件付きで汎用機能を実行できます。
たとえば、FODViewObjectImpl
フレームワーク拡張クラスでは、ビュー・オブジェクトのinsertRow()
メソッド(例37-6参照)をオーバーライドし、条件付きで行を挿入し、行セットの最後の行として表示しています。このフレームワーク拡張クラスを拡張するビュー・オブジェクトでInsertNewRowsAtEnd
という名前のカスタム・メタデータ・プロパティが定義されている場合、この汎用コードは末尾への新しい行の挿入を実行します。ビュー・オブジェクトでこのプロパティが定義されていない場合は、insertRow()
のデフォルトの動作が実行されます。
例37-6 ビュー・オブジェクトのデフォルト行セットの末尾への条件付きでの新規行の挿入
public class FODViewObjectImpl 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()
メソッドを使用します。
カスタム・プロパティに依存するフレームワーク拡張クラスを作成すると、対応するコンポーネント・エディタの「カスタム・プロパティ」セクションのリストにカスタム・プロパティ名が表示されるように、JDeveloperを設定できます。定義済のカスタム・プロパティ名を設定するには、JDeveloperのメイン・メニューから「ツール」→「設定」を選択し、「設定」ダイアログで「ビジネス・コンポーネント」→「プロパティ名」タブを開きます。
属性の名前、Java型、SQL型、および他の多くの有用な情報を提供するだけでなく、AttributeDef
インタフェースのgetAttributeKind()
メソッドを使用することで、属性が表す種類を判定することができます。このメソッドは、表37-1で示されているAttributeDef
インタフェースのパブリック定数のいずれかに対応するbyte
値を返します。
表37-1 エンティティ・オブジェクトとビュー・オブジェクトの属性の種類
パブリックAttributeDef 定数 |
属性の種類の説明 |
---|---|
|
永続的属性 |
|
一時的属性 |
|
エンティティ・レベルの一時的属性にマップされるビュー・オブジェクト属性 |
|
SQL計算属性 |
|
動的属性 |
|
0以上の |
|
単一の |
フレームワーク拡張クラスの作成に加えて、すべてのコンポーネントがデフォルトで実装できるカスタム・インタフェースも作成できます。クライアント・インタフェースは、たとえばUIクライアントから起動されるアプリケーション・モジュールからメソッドを公開する際に、非常に便利です。ここではアプリケーション・モジュールの例について説明しますが、カスタム拡張ビュー・オブジェクトおよびビュー行インタフェースに対しても同じ機能を作成できます。クライアント・インタフェースの詳細は、9.9項「UIクライアントへのカスタム・サービス・メソッドの公開」および9.10項「アプリケーション・モジュールのクライアント・インタフェースのプログラム的操作」も参照してください。
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
クラスを実装すると、新しいアプリケーション・モジュールを作成できます。このモジュールはグローバル拡張インタフェースを公開し、カスタム・フレームワーク拡張クラスに基づくものです。この作成には、アプリケーション・モジュールの概要エディタを使用します。
カスタム・アプリケーション・モジュールを作成するには:
アプリケーション・ナビゲータで、カスタム・インタフェースを作成するアプリケーション・モジュールをダブルクリックします。
たとえば、グローバル拡張インタフェースCustomApplicationModule
を公開し、CustomApplicationModuleImpl
フレームワーク拡張クラスに基づく新しいProductModule
アプリケーション・モジュールを作成するとします。
概要エディタで、「Java」ナビゲーション・タブを選択し、「Javaオプションの編集」アイコンをクリックします。
「Javaクラス」ページには、「アプリケーション・モジュール・クラス」として識別されたアプリケーション・モジュールの既存のJavaクラスが表示されます。
デフォルトでは、作成するアプリケーション・モジュール用のJavaクラスがJDeveloperにより生成されます。ただし、この機能を無効にした場合は、「Javaクラス」セクションの「Javaオプションの編集」ボタンをクリックし、続いて「アプリケーション・モジュール・クラスの生成」を選択します。「OK」をクリックして、カスタム・インタフェースの作成元となるプロジェクトにJavaクラスを追加します。
「Javaオプションの選択」ダイアログで、「クラスの拡張」をクリックします。
「ベース・クラスのオーバーライド」ダイアログで、オーバーライドするフレームワーク・ベース・クラスの名前を指定して、「OK」をクリックします。
たとえば、アプリケーション・モジュールのベース・クラスとしてCustomApplicationModuleImpl
を選択します。
概要エディタの「Javaクラス」ページで、「クライアント・インタフェース」セクションを展開し、「アプリケーション・モジュール・クライアント・インタフェースの編集」ボタンをクリックします。
「クライアント・インタフェースの編集」ダイアログで、「インタフェース」ボタンをクリックします。
「拡張するインタフェースの選択」ダイアログで、利用可能なリストから目的のカスタム・アプリケーション・モジュール・インタフェースを選択して「OK」をクリックします。
たとえば、CustomApplicationModule
インタフェースを「選択済」リストに移動してコンポーネントで使用できるカスタム・インタフェースの1つとなるようにします。
「クライアント・インタフェースの編集」ダイアログで、「選択済」リストに、少なくとも1つのメソッドが表示されることを確認します。
注意: グローバル拡張インタフェースのメソッドの1つを重複して選択することになるとしても、「クライアント・インタフェース」ダイアログの「選択済」リストでは少なくとも1つのメソッドを選択する必要があります。JDeveloperでカスタム・インタフェースを生成するには、どのメソッドでもかまいません。 |
「OK」をクリックします。
「Javaクラス」ページには、「アプリケーション・モジュール・クライアント・インタフェース」として識別された、アプリケーション・モジュール用の新しいカスタム・インタフェースが表示されます。
「クライアント・インタフェースの編集」ダイアログを終了してアプリケーション・モジュールの概要エディタに戻ると、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
インタフェースにキャストし、それに含まれる汎用拡張メソッドを強く型付けされた方法で呼び出すことができます。
注意:
|
ビジネス・コンポーネント用のカスタムJavaクラスにコードを記述し、データベースのストアド・プロシージャとストアド・ファンクションを呼び出すことができます。ここでは、PL/SQLパッケージのプロシージャとファンクションに基づく簡単な例をいくつか示しますが、同じ手法を使用して、パッケージの一部ではないプロシージャやファンクションを呼び出すこともできます。
例37-7のPL/SQLパッケージについて考えてみます。
例37-7 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;
以降の項では、このパッケージのプロシージャおよびファンクションの各例を呼び出す方法について説明します。
注意: この項の例では、Fusion Order Demoアプリケーションの |
引数を受け取らないストアド・プロシージャを呼び出す必要がある場合は、(例37-8に示すようにoracle.jbo.server
パッケージ内の)DBTransaction
インタフェースのexecuteCommand()
メソッドを使用できます。
IN
モード引数のみを受け取るストアド・プロシージャ(何も指定しない場合のデフォルトのPL/SQLパラメータ・モード)を呼び出すには、JDBC PreparedStatement
オブジェクトを使用する必要があります。DBTransaction
インタフェースでは、現在のデータベース接続のコンテキストでこのオブジェクトを作成するためのcreatePreparedStatement()
メソッドが提供されています。例37-9で示すようなヘルパー・メソッドを使用することで、PreparedStatement
を使用してこの種のストアド・プロシージャを呼び出す処理が簡単になります。重要なこととして、ヘルパー・メソッドを使用することにより、実行後にJDBC PreparedStatement
を閉じるコードをカプセル化できます。このコードが実行する基本的な処理は次のとおりです。
渡される文のためのJDBC PreparedStatement
を作成し、PL/SQLのbegin
...end
ブロック内にラップします。
バインド変数が渡された場合は、値のループ処理を行います。
各バインド変数の値を文に設定します。
JDBCのバインド変数APIは1から始まる番号付けを使用するため、0から始まるforループのインデックス変数にコードで1を加えていることに注意してください。
文を実行します。
文を閉じます。
例37-9 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) {} } } }
このようなヘルパー・メソッドを使用すると、例37-7の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()
メソッドが提供されています。例37-10に示すようにヘルパー・メソッドを使用することで、CallableStatement
を使用してこの種のストアド・ファンクションを呼び出す処理が簡単になります。ヘルパー・メソッドは、使用するJDBC文の作成とクリーン・アップの両方をカプセル化します。
このコードが実行する基本的な処理は次のとおりです。
渡される文のためのJDBC CallableStatement
を作成し、PL/SQLのbegin
...end
ブロック内にラップします。
最初のバインド変数をファンクションの戻り値に登録します。
バインド変数が渡された場合は、値のループ処理を行います。
ユーザーが提供した各バインド変数の値を文に設定します。
JDBCのバインド変数APIは1から始まる番号付けを使用していて、ファンクションの戻り値は文の最初のバインド変数であるため、0から始まるforループのインデックス変数にコードで2を加えていることに注意してください。
文を実行します。
最初のバインド変数の値を返します。
文を閉じます。
例37-10 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) {} } } }
このようなヘルパー・メソッドを使用すると、例37-7の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
を使用する必要がありますが、ヘルパー・メソッドへの一般化は少し難しくなります。例37-11は、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
を閉じます。
例37-11 複数の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) {} } } }
例37-11で使用されている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が |
ADFビジネス・コンポーネントのコンポーネントは下位レベルのデータベース・プログラミングの詳細をすべて抽象化しているので、通常は、JDBCのConnection
オブジェクトに直接アクセスする必要はありません。40.2.2.3.3項「予約解放レベルについて」で説明されている予約されたリリース・モードを使用していない場合は、実行時に、異なるWebページ・リクエストの間で、まったく同じアプリケーション・モジュール・インスタンスまたはJDBC Connection
インスタンスがアプリケーションで使用されることが保証されません。この種のプールされたサービス環境でJDBC接続オブジェクトへの参照を誤って保持すると、実行時に想定外の動作が発生する可能性があるため、設計として、ADFビジネス・コンポーネント・レイヤーにはJDBC Connection
を直接取得するAPIはありません。これは、その直接的な使用および不注意による誤用を防ぐために意図的に行われていることです。
ただし、サード・パーティのコードをADFビジネス・コンポーネントと統合する場合はこれが可能であると便利なため、例37-12に示すようなヘルパー・メソッドを使用して接続にアクセスできます。
例37-12 現在のJDBC接続にアクセスするためのヘルパー・メソッド
/** * 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; }
注意: 例37-12のヘルパー・メソッドを使用して取得したJDBC接続を、独自のコード内のどこにもキャッシュしないでください。かわりに、必要になるたびにヘルパー・メソッドを呼び出すようにすることで、JDBC接続に対する参照を誤って保持し、それが後で別のユーザーによる別のリクエストで、ADFランタイム環境のプールされたサービスに対して使用されるのを防ぐようにします。 |
他のJavaコンポーネントと同様に、再利用可能なADFコンポーネントの1つ以上のパッケージを含むJARファイルを作成できます。その後、他のプロジェクトでこのコンポーネント・ライブラリからコンポーネントのパッケージをインポートし、新しいアプリケーションで参照できます。
ビジネス・コンポーネントのライブラリを構成するJavaクラスおよびXMLコンポーネント定義を含むJARファイルを作成するには、「ビジネス・コンポーネントのアーカイブ・プロファイルの作成」ダイアログを使用します。これは、「新規ギャラリ」の「一般」→「デプロイメント・プロファイル」カテゴリで使用できます。
注意: 「新規ギャラリ」に「Deployment Profiles」カテゴリが表示されない場合は、ダイアログの上部にある「フィルタ方法」ドロップダウン・リストで「すべてのテクノロジ」を選択すると、表示されるようになります。 |
デプロイメント・プロファイルにReusableComponents
のような名前を設定し、「OK」をクリックします。図37-8の「プロジェクト・プロパティ」ダイアログに示すように、ReusableComponents
ビジネス・コンポーネント・デプロイメント・アーカイブ・プロファイルには、ネストされた2つのJARデプロイメント・プロファイルが含まれます。
Common.deploy
MiddleTier.deploy
これら2つのネストされたプロファイルは、次のものをバンドルするために事前に構成されている標準のJARデプロイメント・プロファイルです。
すべてのビジネス・コンポーネント・カスタムJavaクラスおよびXMLコンポーネント定義をReusableComponentsCSMT.jar
アーカイブにバンドル
すべてのクライアント・インタフェース、メッセージ・バンドル・クラス、およびカスタム・ドメインをReusableComponentsCSCommon.jar
にバンドル
これらは、ADFビジネス・コンポーネント・ベースのアプリケーションを簡単にデプロイできるよう、このように分割されています。*CSMT.jar
は、中間層のアプリケーション・サーバーにのみデプロイされるよう設計されているコンポーネントのアーカイブです。*CSCommon.jar
は、アプリケーション・モジュールと通信するクライアントがそのアプリケーション・モジュールとは異なる物理サーバーで実行しているデプロイ・シナリオで、アプリケーション・サーバーとリモート・クライアント層の両方に共通のものです。
JARファイルを作成するには、アプリケーション・ナビゲータでビジネス・コンポーネントのプロジェクト・フォルダを右クリックし、「デプロイ」およびReusableComponents
プロファイルを選択します。JDeveloperの「ログ・ウィンドウ」の「デプロイ」タブに、次のようなフィードバックが表示されます。
---- Deployment started. ---- Apr 28, 2009 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, 2009 7:04:02 PM
ビジネス・コンポーネントの再利用可能なライブラリを作成すると、そのライブラリからコンポーネントのパッケージを他のプロジェクトにインポートして参照することができます。ライブラリからビジネス・コンポーネントのパッケージをインポートすると、ADFビジネス・コンポーネント・コンポーネントのウィザードおよびエディタの様々な「使用可能」リストでそのパッケージのコンポーネントを使用できるようになりますが、アプリケーション・ナビゲータには表示されず、編集もできません。アプリケーション・ナビゲータに表示されるのは、現在のプロジェクトのソース・パスに存在するコンポーネントのみです。
ヒント: アプリケーション・ナビゲータに表示される編集可能なコンポーネントが必要な場合は、現在のプロジェクトのソース・パスに含まれていないディレクトリからビジネス・コンポーネントを追加し、「プロジェクト・プロパティ」ダイアログの「プロジェクト・コンテンツ」ページを開いて、追加するコンポーネントの親ディレクトリを「Javaコンテンツ」リストのディレクトリの1つとして追加します。インポートしたコンポーネントのパッケージとは異なり、プロジェクトのソース・パスに追加したコンポーネントは、完全に編集可能であり、アプリケーション・ナビゲータに表示されます。 |
ライブラリからビジネス・コンポーネントのパッケージをインポートするには:
インポートを行うプロジェクトの「プロジェクト・プロパティ」ダイアログの「ライブラリ」タブで、JARファイルのライブラリを定義します。
ライブラリは、プロジェクト・レベルまたはユーザー・レベルとして定義できます。ライブラリ定義のクラス・パスに、*CSMT.jar
と*CSCommon.jar
の両方を必ず含めます。
インポートを行うプロジェクトのライブラリ・リストに、新しいライブラリを追加します。
アプリケーション・ナビゲータでインポートを行うプロジェクトを選択し、「ファイル」メニューから「インポート」を選択します。
「インポート」ダイアログで、リストからビジネス・コンポーネントを選択します。
ファイルを開くダイアログを使用して、ディレクトリの場合と同じようにライブラリの*CSMT.jar
ファイルの中に移動し、インポートするコンポーネントのパッケージ内のコンポーネントから、XMLコンポーネント定義ファイルを選択します。
パッケージのインポートが成功したことを示すアラートを確認します。
インポートするコンポーネントのパッケージごとに、手順3から6を繰り返します。
インポートしたコンポーネントのパッケージにProduct
のようなエンティティ・オブジェクトがあったとすると、エンティティ・オブジェクトの慣用名として、インポートしたProduct
コンポーネントを使用し、インポートを行っているプロジェクト内に新しいビュー・オブジェクトを作成できます。これはほんの一例です。インポートした任意のコンポーネントを、プロジェクトのソース・パスにある場合と同じように参照できます。唯一の違いは、インポートしたコンポーネントは編集できないことです。実際、再利用可能なコンポーネント・ライブラリのJARファイルには、コンポーネントのXMLコンポーネント定義ファイルとJava *.class
ファイルのみが含まれ、ソース・コードは含まれない場合があります。
コンポーネントのパッケージを誤ってインポートした場合、またはインポートしたコンポーネントのパッケージで使用していないものを削除する場合、「プロジェクト・プロパティ」ダイアログを使用してこれを実行することができます。
パッケージのインポートを取り消すには:
アプリケーション・ナビゲータで、インポート解除するパッケージを含むモデル・プロジェクトを右クリックし、「プロジェクト・プロパティ」を選択します。
「プロジェクト・プロパティ」ダイアログで「ビジネス・コンポーネント」→「インポート」を選択します。
インポートされたプロジェクトのリストから目的のプロジェクトを選択し、「削除」をクリックします。
「OK」をクリックし、JDeveloperの変更を保存します。
注意: プロジェクト内の他のコンポーネントからまだ参照されているインポート済パッケージは、削除しないでください。このようなパッケージを削除すると、プロジェクトを開いたときに例外が発生するか、またはアプリケーションの動作が予測できなくなる可能性があります。インポートしたパッケージのエントリを |
コンポーネントのパッケージをYourImportingProjectName
という名前のプロジェクトにインポートすると、インポートしているプロジェクトのソース・パスのルート・ディレクトリにあるYourImportingProjectName.jpx
ファイルに、そのパッケージへの参照が追加されます。このエントリの一部として、_LocationURL
という名前の設計時プロジェクトが含まれ、その値はインポートされたコンポーネントが存在するJARファイルを指します。
インポートされたライブラリ内のコンポーネントは編集不能です。ビジネス・コンポーネントを含んだパッケージをプロジェクトでインポートする場合、インポートするプロジェクトでは同じパッケージに新しいコンポーネントを追加できません。インポートするプロジェクトでは、その他のプロジェクト内に作成された新しいコンポーネントにあるインポート済コンポーネントを参照できますが、インポートしたパッケージに新しいコンポーネントを追加できません。
また、インポートしたコンポーネントを変更し、それを格納しているJARファイルを更新した場合、変更を反映するには、インポートしているプロジェクトを閉じてから開き直す必要があります。このとき、JDeveloperを終了する必要はありません。アプリケーション・ナビゲータでインポートしているプロジェクトを選択し、「ファイル」メニューから「閉じる」を選択した後、プロジェクトのノードを再び展開し、プロジェクトを閉じて開き直します。この手順を実行すると、インポートされているJARファイルの更新バージョンからコンポーネントが再度読み込まれます。
注意: この項の例では、Fusion Order Demoアプリケーションの |
カスタム・メッセージ・バンドルでエラー・コードに対してかわりのメッセージ文字列を提供することにより、組み込まれているADFビジネス・コンポーネントのエラー・メッセージをカスタマイズできます。
注意: 便宜のため、Fusion Order Demoの |
次の組込みエラー・メッセージをカスタマイズするとします。
JBO-27014: Attribute Name is Product 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}
は、エラー・メッセージのプレースホルダです。この例では、実行時に、{1}
はエンティティ・オブジェクトの名前に置き換えられ、{2}
は属性の名前に置き換えられます。
カスタムのメッセージ・バンドル・ファイルを作成するには、次のようにします。
アプリケーション・ナビゲータで、メッセージ・バンドル・ファイルを追加するモデル・プロジェクトを右クリックし、「プロジェクト・プロパティ」を選択します。
「プロジェクト・プロパティ」ダイアログで「ビジネス・コンポーネント」→「オプション」を選択します。
「このプロジェクトで使用するカスタム・メッセージ・バンドル」リストがダイアログの下部に表示されます。
「新規」をクリックします。
「MessageBundleクラスの作成」ダイアログでカスタム・メッセージ・バンドルの名前とパッケージを入力し、「OK」をクリックします。
注意: カスタム・メッセージ・バンドル・ファイルの完全修飾名が「このプロジェクトで使用するカスタム・メッセージ・バンドル」リストに表示されない場合は、「削除」ボタンをクリックした後、「追加」ボタンをクリックして、作成した新しいメッセージ・バンドル・ファイルを追加してください。カスタム・メッセージ・バンドル・ファイルが正しく登録されると、その完全修飾クラス名がリストに表示されます(図37-9を参照)。 |
「OK」をクリックして「プロジェクト・プロパティ」ダイアログを閉じ、新しいカスタム・メッセージ・バンドル・クラスをソース・エディタで開きます。
カスタム・メッセージ・バンドル・クラスの2次元のString
配列を編集し、使用するカスタマイズしたメッセージを入力します。
例37-13は、JBO-27014
エラーのエラー・メッセージ文字列をオーバーライドするカスタム・メッセージ・バンドル・クラスを示しています。
例37-13 カスタムADFビジネス・コンポーネント・メッセージ・バンドル
package devguide.advanced.customerrors; 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ビジネス・コンポーネントのエラー・メッセージをカスタマイズする場合、ネストされたエラー・メッセージの表示もカスタマイズする必要があります。そのためには、カスタム・エラー・ハンドラ・クラスを作成して登録する必要があります。
ビジネス・メソッドでエラーがスローされると、ADFバインディング・レイヤーはエラーを遮断して、登録されたカスタム・エラー・ハンドラ・クラスを呼び出します。一般に、エラー・ハンドラ・クラスは例外を読取り可能になるように書式設定します。このプロセスで、デフォルト・エラー・ハンドラDCErrorHandlerImpl
は通常、最上位のJboException
をスキップします。このオブジェクトは他のビジネス例外のラッパーでビジネス上重要でないためです。
最上位の例外をスキップすることが、ADFビジネス・コンポーネント・エラーの場合の望ましい動作ですが、デフォルトの動作では、SQLException
を置換するために設定されたカスタム・メッセージがスキップされます。この状況を回避するために、ネストされた例外の各項目を表示する間、カスタム・エラー・ハンドラ・クラスはDCErrorHandlerImpl::skipException(Exception
ex
)
をオーバーライドして、該当する例外を最終リストのユーザーに表示するかどうかを決定します。
作業を始める前に、次のようにします。
アプリケーション・モジュールの知識があると役立ちます。詳細は、37.9項「提供されるアプリケーションでの拡張コンポーネントの置換え」を参照してください。
次のタスクを完了する必要があります。
プロジェクトのSQLExceptionsのカスタム・メッセージを指定するには:
ADFバインディング・レイヤーで提供されるデフォルト・エラー・ハンドラDCErrorHandlerImpl
インタフェースを拡張する、エラー・ハンドラ・クラスを作成します。
エラー・ハンドラ・クラスで、DCErrorHandlerImpl::skipException(Exception
ex
)
メソッドのデフォルト・エラー・ハンドラ動作を例37-14に示すようにオーバーライドします。
データベース・レベルのエラー・メッセージに対して戻されるものなど、ネストされた例外の各項目を表示するには、このメソッドのオーバーライドが必須です。固有の例外タイプをチェックするロジックを実装する必要があります。また、ビジネス・シナリオに基づいて、それをリストに表示するかどうかを決定する必要があります。
その後、28.10項「エラー処理のカスタマイズ」の説明に従って、プロジェクトのDataBindings.cpx
ファイルにカスタム・エラー・ハンドラを登録できます。
例37-14に、ユーザーに表示されるエラー最終リストへのSQLIntegrityConstraintViolationException
の表示をスキップするエラー・ハンドラのカスタム実装を示します。一意の制約違反や外部キー制約違反などのエラーで生成される、他のデータベース・レベルのエラー・メッセージをスキップすることもできます。
例37-14 DDL文固有の制約名
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(false); } public CustomErrorHandler(boolean b) { super(b); } /** * 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アプリケーションでカスタム・エラー・メッセージをエンド・ユーザーに対して提供する場合があります。たとえば、例37-15に示す次のDDL文を使用して、アプリケーションのPRODUCTS_BASE
表にNAME_CANNOT_BEGIN_WITH_U
という制約を追加するものとします。
例37-15 DDL文固有の制約名
alter table products_base add ( constraint name_cannot_begin_with_u check (upper(substr(product_name,1,1)) != 'U') );
アプリケーションでカスタム・エラー・メッセージを定義するには、制約名をメッセージ・キーとして、カスタム・メッセージ・バンドルにメッセージを追加します。例37-16に、例37-15で定義したデータベース制約名と一致するキーNAME_CANNOT_BEGIN_WITH_U
を持つメッセージを定義した場合のCustomMessageBundle.java
クラスを示します。
例37-16 データベース制約違反のエラー・メッセージのカスタマイズ
package devguide.advanced.customerrors; import java.util.ListResourceBundle; public class CustomMessageBundle extends ListResourceBundle { private static final Object[][] sMessageStrings = new String[][] { {"NAME_CANNOT_BEGIN_WITH_U", "The name cannot begin with the letter u!"} }; protected Object[][] getContents() { return sMessageStrings; } }
カスタム・メッセージをデータベース制約違反に割り当てるデフォルト機能が要件を満たさない場合は、独自のカスタム制約エラー処理ルーチンを実装できます。そのためには、ADFトランザクション・クラスに対するカスタム・フレームワーク拡張クラスを作成し、実行時にそれを使用するようアプリケーション・モジュールを構成する必要があります。
ADFトランザクション用のカスタム・フレームワーク拡張クラスを記述するには、例37-17
で示すCustomDBTransactionImplのようなクラスを作成します。この例では、スローされるDMLConstraintException
エラーに対するカスタム処理を実行するため、トランザクション・オブジェクトのpostChanges()
メソッドをオーバーライドして、super.postChanges()
の呼び出しをtry
/catch
ブロックでラップしています。この簡単な例で実行しているカスタム処理は、ex.setExceptions(null)
を呼び出して、DMLConstraintException
が保持している可能性のあるネストされた詳細な例外をクリアすることのみです。かわりに、アプリケーションで必要な他の種類のカスタム例外処理を実行できます。たとえば、カスタム例外がJboException
を直接または間接に拡張している場合は、カスタム例外をスローできます。
例37-17 カスタム・データベース・トランザクション・フレームワーク拡張クラス
package devguide.advanced.customerrors; 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
プロパティの値に、このカスタム・トランザクション・ファクトリ・クラスの完全修飾名を構成します。
例37-18は、これを行うカスタム・データベース・トランザクション・ファクトリ・クラスを示しています。フレームワークがデータベース・トランザクション・ファクトリでcreate()
メソッドを呼び出すと、このクラスはCustomDBTransactionImpl
クラスの新しいインスタンスを返します。
例37-18 カスタム・データベース・トランザクション・ファクトリ・クラス
package devguide.advanced.customerrors; 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.customerrors.CustomDatabaseTransactionFactory
を割り当てます(図37-10を参照)。「ビジネス・コンポーネント構成の作成」ダイアログを開くには、アプリケーション・モジュールの概要エディタの「構成」ページで、「新規構成オブジェクトの作成」ボタンをクリックします。この構成を使用してアプリケーションを実行すると、カスタム・トランザクション・クラスが使用されます。
新しいビジネス・コンポーネントを作成するたびに、必要に応じて、既存のものを拡張してオリジナルのカスタマイズ・バージョンを作成できます。図37-11に示すように、ProductsByName
ビュー・オブジェクトでは、Products
ビュー・オブジェクトを拡張してTheStatus
という名前の名前付きバインド変数が追加され、このバインド変数を参照するようにWHERE
句がカスタマイズされています。
図ではビュー・オブジェクトの例が示されていますが、このコンポーネント継承機能はすべての種類のコンポーネントで使用できます。あるコンポーネントが別のコンポーネントを拡張する場合、拡張されたコンポーネントは親からすべてのメタデータと動作を継承します。拡張されたコンポーネントでは、メタデータとJavaコードの両方を使用して、新しい機能を追加したり、親コンポーネントの既存機能をカスタマイズしたりできます。
注意: この項の例では、Fusion Order Demoアプリケーションの |
拡張コンポーネントを作成するには、作成するコンポーネントの種類に対する「新規ギャラリ」のコンポーネント・ウィザードを使用します。たとえば、拡張ビュー・オブジェクトを作成するには、ビュー・オブジェクトの作成ウィザードを使用します。ウィザードの「名前」ページで、新しいコンポーネントの名前とパッケージを指定することに加えて、拡張するコンポーネントの完全修飾名を拡張フィールドで指定します。リストからコンポーネント名を選択するには、拡張フィールドの隣の「参照」ボタンを使用します。その後は、ウィザードの残りのパネルを使用して、通常の方法で拡張コンポーネントの作成を続けます。
拡張コンポーネントを定義した後で、拡張コンポーネントが継承している親コンポーネントを変更できます。コンポーネントの概要エディタをこの目的に使用できます。
作成後に親コンポーネントを変更するには:
コンポーネントをダブルクリックします。
コンポーネントの概要エディタで、「一般」ナビゲーション・タブをクリックし、「拡張対象」フィールドの横にある「オブジェクト拡張対象をリファクタします。」ボタンをクリックします。
「親の選択」ダイアログで、パッケージ・リストから拡張する目的のコンポーネントを選択します。
どの親からも継承しないように拡張コンポーネントを変更するには、「親の選択」ダイアログの「なし」チェック・ボックスを選択します。これは、この目的のためのコンポーネント削除および再作成と同じ結果になります。
作成するADFビジネス・コンポーネントは、XMLコンポーネント定義とオプションのJavaクラスで構成されます。別のコンポーネントを拡張するコンポーネントを作成すると、JDeveloperは、拡張されたコンポーネントのXMLコンポーネント定義と生成されるJavaコードの両方に、このコンポーネント継承を反映します。
JDeveloperは、新しいコンポーネントのXMLコンポーネント定義のルート・コンポーネント要素にExtends
属性を追加することで、親コンポーネントの名前を記録します。追加された新しい宣言機能、または親コンポーネントの定義のオーバーライドされた部分は、拡張コンポーネントのXMLコンポーネント定義に記述されます。これに対し、親コンポーネントから純粋に継承されたメタデータは、拡張コンポーネントでは繰り返し記述されません。
例37-19は、ProductsByName
ビュー・オブジェクトに対するProductsByName.xml
XMLコンポーネント定義を示しています。ViewObject要素のExtends
属性、拡張されたビュー・オブジェクトで追加されたバインド変数に関連するVariable要素、およびtheProductName
バインド変数を参照するように変更されたWHERE
句のWhere
属性のオーバーライドされた値に注意してください。
例37-19 XMLディスクリプタに親が反映された拡張コンポーネント
<ViewObject xmlns="http://xmlns.oracle.com/bc4j" Name="ProductsByName" Extends="devguide.advanced.baseproject.extsub.Products" Where="UPPER(PRODUCT_NAME) LIKE UPPER(:theProductName)||'%'" BindingStyle="OracleName" CustomQuery="false" RowClass="devguide.advanced.baseproject.extsub.ProductsByNameRowImpl" ComponentClass="devguide.advanced.baseproject.extsub.ProductsByNameImpl" ... <Variable Name="theProductName" Kind="where" Type="java.lang.String"/> ... </ViewObject>
拡張コンポーネントでカスタムJavaコードを有効にすると、親コンポーネントの各Javaクラスを拡張するためのJavaクラスが自動的に生成されます。これにより、拡張コンポーネントでは、必要に応じて、親コンポーネントのプログラム的な動作のすべての面をオーバーライドできます。親コンポーネントが、独自のカスタムJavaクラスを持たないXMLのみのコンポーネントである場合は、拡張コンポーネントのJavaクラスは、親が実行時に使用するベースJavaクラスを拡張します。これは、oracle.jbo.server
パッケージのデフォルトのADFビジネス・コンポーネント・フレームワーク・クラス、または親コンポーネントの拡張ダイアログで指定されている場合は独自のフレームワーク拡張クラスなどになります。
さらに、拡張コンポーネントがアプリケーション・モジュールまたはビュー・オブジェクトで、クライアント・インタフェースを有効にしてある場合は、拡張コンポーネントのクライアント・インタフェースが自動的に生成され、親コンポーネントの各クライアント・インタフェースが拡張されます。親コンポーネントのクライアント・インタフェースが存在しない場合は、拡張コンポーネントのクライアント・インタフェースは、oracle.jbo
パッケージの適切なベースADFビジネス・コンポーネント・インタフェースを直接拡張します。
拡張コンポーネントは親のカスタマイズ・バージョンであるため、親コンポーネントのJavaクラスまたはクライアント・インタフェースで動作するように作成したコードは、親コンポーネントまたはそのカスタマイズ・バージョンで問題なく動作します。
たとえば、次のようなカスタムJavaクラスとクライアント・インタフェースを含むベースProducts
ビュー・オブジェクトがあるものとします。
クラスProductsImpl
行クラスProductsRowImpl
インタフェースProducts
行インタフェースProductsRow
Products
を拡張するProductsByName
ビュー・オブジェクトを作成した場合、ベース・コンポーネントのクラスとインタフェースを使用して、Products
とProductsByName
の両方を操作できます。
例37-20は、Products
、ProductsRow
、ProductsByName
およびProductsByNameRow
の各クライアント・インタフェースで動作するテスト用のクライアント・プログラムです。この例については、次のことに注意してください。
親のProducts
インタフェースを、それを拡張するProductsByName
ビュー・オブジェクトを処理するために使用できます。
別の方法として、ProductsByName
ビュー・オブジェクトのインスタンスを、独自のさらに限定的なProductsByName
クライアント・インタフェースにキャストすることもできます。
キャストしてProductsByNameRow
インタフェースに固有のメソッドを呼び出す前に、本当に行ProductsRow
がより限定的なProductsByNameRow
のインスタンスであるかどうかをテストできます。
例37-20 親コンポーネントと拡張コンポーネントの使用
package devguide.advanced.baseproject.extsub; /* imports omitted */ public class TestClient { public static void main(String[] args) { String amDef = "devguide.advanced.baseproject.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()); } }
テスト・クライアントを実行すると、例37-21に示す結果が生成されます。
例37-21 TestClient.javaの実行結果
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は |
拡張コンポーネントを作成するとき、拡張コンポーネントのウィザードの「Java」ページにある「クラスの拡張」ボタンは無効になります。また、アプリケーション・モジュール・エディタの「Java」ページで、「Javaオプションの編集」ボタンをクリックすると、「Java」ダイアログの「クラスの拡張」ボタンは無効として表示されます。これは、JDeveloperが親コンポーネントの適切なクラスを自動的に拡張するため、別のクラスをユーザーが選択できるようにしても意味がないためです。
拡張エンティティ・オブジェクトを作成するときは、新しい属性、新しいアソシエーション、新しいバリデータ、および新しいカスタム・コードを導入できます。既存の属性の特定の宣言的部分、および親コンポーネントのクラスのメソッドをオーバーライドできます。
拡張ビュー・オブジェクトを作成するときは、新しい属性、新しいビュー・リンク、新しいバインド変数、および新しいカスタム・コードを導入できます。既存の属性の特定の宣言的部分、および親コンポーネントのクラスのメソッドをオーバーライドできます。
拡張アプリケーション・モジュールを作成するときは、新しいビュー・オブジェクト・インスタンスまたは新しいネストされたアプリケーション・モジュール・インスタンス、および新しいカスタム・コードを導入できます。親コンポーネントのクラスのメソッドをオーバーライドすることもできます。
拡張エンティティ・オブジェクトまたはビュー・オブジェクトで新しい属性を追加する場合、属性索引番号は親コンポーネントを規準にして計算されます。たとえば、37.8.4.1項「親のクラスとインタフェースは拡張コンポーネントの操作にも使用できる」に記載した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.baseproject.extsub.Products"); public static final int SOMEEXTRAATTR = MAXATTRCONST;
追加属性の索引値は、MAXATTRCONST+1
、MAXATTRCONST+2
などになります。
ソリューションの各クライアントがオンサイトでカスタマイズする必要のあるパッケージ化されたアプリケーションを配布する場合のために、ADFビジネス・コンポーネントでは、この作業を簡単にする便利な機能が提供されています。
注意: この項の例では、Fusion Order Demoアプリケーションの |
通常、オンサイトでのアプリケーションのカスタマイズは、配布されたアプリケーションのソース・コードを直接変更することで行います。この方法の短所が明らかになるのは、元のアプリケーションのパッチや新機能リリースをクライアントに提供するときです。基になるアプリケーションのソース・コードに適用されていたカスタマイズは、パッチまたは更新を行った後のアプリケーションに、わざわざ適用しなおす必要があります。この方法では、アプリケーションのカスタマイズやメンテナンスに費用がかかるだけでなく、以前のカスタマイズを新しいリリースに適用しなおすときの人為的エラーにより不具合が発生する可能性があります。
ADFビジネス・コンポーネントが提供する優れたコンポーネント・ベースの方法を使用してアプリケーションをカスタマイズすると、基になっているアプリケーションのソース・コードを変更する必要はなく、ソース・コードにアクセスする必要さえありません。提供されたアプリケーションのカスタマイズは、次の方法で行います。
基になるアプリケーションのコンポーネントのパッケージを、新しいプロジェクトにインポートします。
アプリケーションのカスタマイズを適用する新しいコンポーネントを作成し、必要に応じて基のアプリケーションの適切な親コンポーネントを拡張します。
グローバルなコンポーネント置換のリストを定義し、カスタマイズしたコンポーネントの名前を設定して、基になるアプリケーションの適切な親コンポーネントを置き換えます。
ユーザーが定義されたグローバル・コンポーネント置換リストを使用し、配布されたアプリケーションを実行すると、配布されたアプリケーションは、コードを変更することなく、カスタマイズされたアプリケーション・コンポーネントを使用します。元のアプリケーションのパッチまたは更新バージョンが提供されると、次にアプリケーションを再起動するときにコンポーネントのカスタマイズが更新バージョンに適用されるので、カスタマイズを適用しなおす必要はありません。
グローバルなコンポーネント置換を定義するには、基になるアプリケーションからインポートしたコンポーネントに基づいて拡張コンポーネントを作成したプロジェクトの、「プロジェクト・プロパティ」ダイアログを使用します。
注意: 基になるアプリケーションのコンポーネントのうち、基のコンポーネントを直接または間接に継承する拡張コンポーネントがあるもののみを置換できます。 |
拡張コンポーネントを置換するには:
アプリケーション・ナビゲータで、拡張コンポーネントを追加するモデル・プロジェクトを右クリックし、「プロジェクト・プロパティ」を選択します。
「プロジェクト・プロパティ」ダイアログで「ビジネス・コンポーネント」→「置換」を選択します。
「置換」ページの「使用可能」リストで、基になるアプリケーションのコンポーネントを選択します。
「置換する対象」リストで、置き換えるカスタマイズした拡張コンポーネントを選択します。
「追加」をクリックします。
たとえば、ベース・ビュー・オブジェクトProducts
を拡張するパッケージでビュー・オブジェクトCustomizedProducts
が作成されているとします。レガシーProducts
ビュー・オブジェクトのCustomizedProducts
ビュー・オブジェクトを置換するには、これらビュー・オブジェクトを図37-12のように選択して、コンポーネント置換を定義する必要があります。
YourExtendsAndSubstitutesProject
という名前のプロジェクトでグローバル・コンポーネント置換リストを定義すると、置換リストは、ソース・パスのルート・ディレクトリにあるYourExtendsAndSubstitutesProject
.jpx
に保存されます。
例37-22に示すように、このファイルには、置換されるコンポーネントごとに1つずつ、Substitute要素が含まれます。
例37-22 プロジェクトの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" 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
拡張子を除いたプロジェクト名のみを設定します。
37.8.4.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
に設定されるよう定義し、例37-20とまったく同じテスト用のクライアント・クラスを実行すると、サンプルの出力は、置換されたコンポーネントの使用を反映して例37-23のように変化します。
例37-23 システム・プロパティを設定したTestClient.javaの実行結果
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!
例37-20からの出力と比較すると、ファクトリ置換リストがあるため、元のテスト・プログラムのProducts
ビュー・オブジェクトにExtraViewAttribute
が追加され、Checksum
属性の値I am the CustomizedProduct Classが表示されるようになり、製品名として値Qを割り当てられなくなっています。これらのコンポーネント動作の変更は、提供されたコンポーネントの元のJavaまたはXMLソース・コードを変更することなく行われました。