ヘッダーをスキップ
Oracle® Fusion Middleware Oracle Application Development Framework Fusion開発者ガイド
11gリリース1(11.1.1.6.0)
B52028-04
  目次へ移動
目次
索引へ移動
索引

前
 
次
 

37 ビジネス・コンポーネントの高度な手法

この章では、すべての種類のADF Business Componentsに適用される高度な手法について説明します。

この章の内容は次のとおりです。

37.1 ADF Business Componentsの機能のグローバルな拡張

フレームワーク・ベースの開発の強力な機能の1つは、基本フレームワークを拡張して、組込み機能の動作を変更したり、すべてのアプリケーションが使用できる新機能を追加したりできることです。


注意:

この章で説明する例を試すには、2.4.3項「AdvancedExamplesアプリケーション・ワークスペースのスタンドアロン・アプリケーション」の説明に従ってFusion Order DemoアプリケーションのStandaloneExamplesモジュールのAdvancedExamplesワークスペースを使用します。Fusion Order Demoを取得してインストールする方法については、2.2項「Fusion Order Demoアプリケーションの設定」を参照してください。

37.1.1 フレームワーク拡張クラスの作成方法

ADF Business Componentsフレームワーク拡張クラスは、次のことを目的としてユーザーが作成する、フレームワークの基本クラスの1つを拡張するJavaクラスです。

  • 追加の汎用的な機能で組込み機能の動作を補強する

  • 組込み機能の動作方法を変更する

  • 検出された不具合を一般的な方法で回避する

フレームワーク拡張クラスを作成した後は、基本クラスではなくカスタマイズしたフレームワーク・クラスに基づいて、新しいADFコンポーネントを作成できます。もちろん、既存のコンポーネントの定義を更新し、新しいフレームワーク拡張クラスを使用するようにすることもできます。

フレームワーク拡張クラスを作成する手順:

  1. フレームワーク拡張クラスを組み込むプロジェクトを識別します。

    あるプロジェクトのコンポーネントのみで使用されることが明らかな場合は、ビジネス・サービス・コンポーネントと同じプロジェクトに作成できます。あるいは、複数のFusion Webアプリケーションでフレームワーク拡張クラスを再利用する場合は、FrameworkExtensionsプロジェクトを別に作成して、フレームワーク拡張クラスを格納することもできます。

  2. BC4Jランタイム・ライブラリがプロジェクトのライブラリ・リストに含まれることを確認します。

    「プロジェクト・プロパティ」ダイアログの「ライブラリ」ページを使用してこれを検証し、ない場合はライブラリを追加します。

  3. 「新規ギャラリ」「一般」カテゴリで、「Javaクラス」を選択して新規クラスを作成します。

  4. 「Javaクラスの作成」ダイアログで、「拡張」フィールドのoracle.jbo.serverパッケージから適切なフレームワーク・ベース・クラスを指定します。

    図37-1は、com.yourcompany.fwkextパッケージにCustomAppModuleImplという名前のカスタム・フレームワーク拡張クラスを作成して基本アプリケーション・モジュール・コンポーネントの機能をカスタマイズする様子を示しています。目的のベース・クラスをすばやく見つけるには、拡張フィールドの隣の「参照」ボタンを使用して、JDeveloperの「クラス・ブラウザ」を起動します。「検索」タブを使用し、クラス名の一部(ワイルドカードとして*を使用できます)を入力してクラスのリストをすばやく絞り込み、目的のクラスを見つけることができます。

    図37-1 アプリケーション・モジュールのフレームワーク拡張クラスの作成

    「Javaクラスの作成」ダイアログ

「OK」をクリックすると、ユーザーが選択したパッケージ名に対応するプロジェクトのソース・パスのディレクトリに、カスタム・フレームワーク拡張クラスが作成されます。


注意:

一部のADF Business Componentsコンポーネント・クラスは、サーバー側とリモート・クライアント・バージョンの両方に存在します。たとえば、JDeveloperの「クラス・ブラウザ」を使用して、「検索」タブの「クラス名を一致」フィールドにApplicationModuleImplと入力すると、結果のリストでは、oracle.jbo.serverパッケージとoracle.jbo.client.remoteパッケージに2つのApplicationModuleImplクラスが表示されます。フレームワーク拡張クラスを作成するときは、oracle.jbo.serverパッケージのベースADFクラスを使用します。

37.1.2 フレームワーク拡張クラス作成時の処理

新しいフレームワーク拡張クラスを作成しても、アプリケーションはそれを自動的には使用しません。フレームワーク拡張クラスを使用するプロジェクトのコンポーネントを指定する必要があります。次では、独自のフレームワーク拡張クラスに基づいてADFコンポーネントを作成する方法について説明します。

37.1.3 フレームワーク拡張クラスをADFコンポーネントの基礎にする方法

任意のADF Business Componentsウィザードまたはエディタの「Java」ページを使用して、ADFコンポーネントのベース・クラスを設定できます。

作業を始める前に、次のようにします。

  • 37.1.1項「フレームワーク拡張クラスの作成方法」の説明に従って、フレームワーク拡張クラスを作成します。

  • フレームワーク拡張クラスを別のプロジェクトに作成する場合は、ビジネス・コンポーネントを含むプロジェクトの「プロジェクト・プロパティ」ダイアログの「依存性」ページで、FrameworkExtensionプロジェクトをプロジェクト依存関係としてマークします。

  • フレームワーク拡張クラスをJavaアーカイブ(JAR)ファイルにパッケージする場合は、JARファイルを参照するための名前付きライブラリ定義を作成し、ビジネス・コンポーネントを含むプロジェクトのライブラリ・リストにもそのライブラリを加えます。ライブラリがない場合に作成するには、「ツール」→「ライブラリの管理」メイン・メニュー項目から開くことのできる「ライブラリの管理」ダイアログを使用します。プロジェクトのライブラリ・リストを確認または変更するには、「プロジェクト・プロパティ」ダイアログの「ライブラリ」ページを使用します。

フレームワーク・クラスを参照できることを確認した後、ADFコンポーネントを作成できます。すべてのADF Business Componentsウィザードおよびエディタの「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コンポーネントを作成する手順:

  1. アプリケーション・ナビゲータで、目的のコンポーネントをダブルクリックします。

  2. 概要エディタで、「Java」ナビゲーション・タブをクリックし、「Javaオプションの編集」ボタンをクリックします。

  3. 「Javaオプションの選択」ダイアログで、「クラスの拡張」をクリックします。

  4. 「ベース・クラスのオーバーライド」ダイアログで、オーバーライドするフレームワーク・ベース・クラスの完全修飾名を入力します。「参照」ボタンを使用して、JDeveloperクラス・ブラウザでクラスをすばやく検索することもできます。

    クラス・ブラウザを使用してコンポーネントのカスタム・ベース・クラスを選択する際、使用可能なクラスが自動的にフィルタリングされて、適切なクラスのみが表示されます。たとえば、図37-2「参照」をクリックしてアプリケーション・モジュールの「オブジェクト」のベース・クラスを選択するときは、oracle.jbo.server.ApplicationModuleクラスを直接または間接に拡張する現在のプロジェクトのライブラリ・リストで使用できるクラスのみがリストに表示されます。探しているクラスが表示されない場合は、正しくないベース・クラスを拡張しているか、または誤ったコンポーネント・クラス名をオーバーライドするように選択しています。

    図37-2 新規アプリケーション・モジュールのカスタム・ベース・クラスの指定

    カスタム・ベース・クラスを指定した「ベース・クラスのオーバーライド」ダイアログ

37.1.4 すべての新規コンポーネントのフレームワーク拡張クラスを定義する方法

フレームワーク拡張クラスの特定のセットを任意のプロジェクトの標準として使用する場合、「プロジェクト・プロパティ」ダイアログを使用して各コンポーネント・タイプの優先ベース・クラスを定義できます。ベース・クラスに対するこれらの優先指定を設定しても、プロジェクト内の既存コンポーネントには影響ありませんが、コンポーネント・ウィザードが作成する新しいコンポーネントにはこの設定が使用されます。

フレームワーク拡張クラスのプロジェクト・レベルのプリファレンスを定義する手順:

  1. アプリケーション・ナビゲータで、拡張クラスを含むモデル・プロジェクトを右クリックし、「プロジェクト・プロパティ」を選択します。

  2. 「プロジェクト・プロパティ」ダイアログのツリーで「ビジネス・コンポーネント」→「ベース・クラス」を展開します。

  3. 「ビジネス・コンポーネント」ページのアプリケーション・モジュール・オブジェクトのクラス名フィールドにそのクラスの完全修飾名を入力します。

    たとえば、プロジェクトで作成された新規アプリケーション・モジュールがデフォルトでCustomAppModuleImplクラスを使用するように指定するには、図37-3に示すように、componentNameオブジェクトのクラス名フィールドにそのクラスの完全修飾名を入力します。

    図37-3 ADFコンポーネントのベース・クラスに対するプロジェクト・レベルの設定

    「ビジネス・コンポーネント: ベース・クラス」ダイアログ

37.1.5 すべての新規プロジェクトのフレームワーク拡張クラスを定義する方法

JDeveloperで作成するそれぞれの新しいプロジェクトに同じベース・クラス設定を適用する場合、「プリファレンス」ダイアログを使用してグローバル・レベルで設定を定義できます。グローバル・レベルで指定するベース・クラスは、ADFコンポーネントを含む既存のプロジェクトを変更しません。

フレームワーク拡張クラスのグローバル・プリファレンスを定義する手順:

  1. 「ツール」で、「プリファレンス」を選択します。

  2. 「プリファレンス」ダイアログのツリーで「ビジネス・コンポーネント」→「ベース・クラス」を展開します。

  3. 「ビジネス・コンポーネント」ページのcomponentNameオブジェクトのクラス名フィールドにそのクラスの完全修飾名を入力します。

    ページには、各コンポーネント・タイプに対して優先ベース・クラスを指定するために、図37-3に示すものと同じオプションが表示されます。

37.1.6 フレームワーク拡張クラスをコンポーネントの基礎にするときの処理

作成するADFコンポーネントがカスタム・フレームワーク拡張クラスを拡張するときは、選択したカスタム・クラス名を反映するように、JDeveloperがXMLコンポーネントの定義を更新します。

37.1.6.1 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クラスを使用します。

図37-4 拡張フレームワーク・ベース・クラスを参照するXMLのみのコンポーネント

拡張フレームワーク・ベース・クラスの流れ

37.1.6.2 カスタムJavaクラスのあるコンポーネント

コンポーネントで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クラスを使用します。

図37-5 カスタマイズされたフレームワーク・ベース・クラスを拡張するカスタムJavaのあるコンポーネント

カスタマイズされた拡張フレームワーク・クラスの流れ

37.1.7 カスタム・コンポーネントのJavaファイルのExtends句の更新について

カスタムJavaクラスを含むADFコンポーネントがあり、後でコンポーネントをフレームワーク拡張クラスに基づくように変更する場合は、コンポーネントの概要エディタの「Java」ページの「クラスの拡張」ボタンを使用して、コンポーネントのベース・クラスを変更してください。これを行うと、コンポーネントのXMLコンポーネント定義が新しいベース・クラスを反映して更新され、さらにコンポーネントのカスタムJavaクラスのextends句が変更されます。コンポーネント・エディタを使用せずに手動でextends句を更新すると、コンポーネントのXMLコンポーネント定義に新しい継承が反映されず、次にエディタを開いたときに、手動で変更したextends句は、コンポーネント・エディタが正しいコンポーネント・ベース・クラスであるとする値で上書きされます。

37.2 フレームワーク拡張のレイヤーの作成

アプリケーション固有のビジネス・コンポーネントの開発を始める前に、フレームワーク拡張クラスの完全なレイヤーを作成し、そのレイヤーをデフォルトで使用するようプロジェクト・レベルで設定することを検討することをお薦めします。これらのフレームワーク拡張クラスにカスタム・コードを追加することはまだ考えていない場合でも、次のようなことが必要になった場合は、この推奨事項に従うことをお薦めします。

最初にこれらの設定をしておかないと、新しい汎用機能や、組込み機能の補強や、不具合の一般的な回避といったことが、プロジェクトの途中ですべてのエンティティ・オブジェクトで必要になった場合に、チームにとって大きな不都合となります。プロジェクトの開始時にフレームワーク・クラスの完全なレイヤーを設定しておくことは、このような不都合と、プロジェクトの途中でそれに対処する場合の無駄な時間に対する保険となります。作成したフレームワーク・クラスは、JDeveloperにより自動的に使用されます。

37.2.1 フレームワーク拡張レイヤー・クラスのレイヤーの作成方法

ユーザーが選択したパッケージ名(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

フレームワーク拡張レイヤー・クラスを再利用可能なライブラリとしてパッケージしやすくするため、これらを使用するプロジェクトとは別のプロジェクトでこれらを作成します。

カスタムのDBTransactionImplクラスの使用の詳細は、37.8.5.2項「カスタム・データベース・トランザクション・クラスを使用するためのアプリケーション・モジュールの構成」を参照してください。


注意:

便宜のため、Fusion Order DemoアプリケーションのStandaloneExamplesモジュールのAdvancedExamplesワークスペースのFrameworkExtensionsプロジェクトに、これらのクラスのセットが収められています。アプリケーション・ナビゲータでcom.yourcompany.adfextensionsパッケージを選択し、コンテキスト・メニューから「リファクタ」→「名前の変更」オプションを選択して、すべてのクラスが入っているパッケージの名前を自由に変更できます。

完璧を期すため、次のクラスについてもカスタマイズしたフレームワーク・クラスを作成する場合もありますが、これらのクラスの内容をオーバーライドすることはほとんど必要がないことに注意してください。

  • public class CustomViewDefImpl extends ViewDefImpl

  • public class CustomEntityCache extends EntityCache

  • public class CustomApplicationModuleDefImpl extends ApplicationModuleDefImpl

37.2.2 フレームワーク拡張レイヤーをJARファイルにパッケージする方法

フレームワーク拡張レイヤーのクラスを含む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, 2008 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, 2008 1:42:41 PM

37.2.3 フレームワーク拡張JARファイルのライブラリ定義の作成方法

JDeveloperは、再利用可能なコンポーネント・ライブラリで構成されるJARファイルを編成するための簡便な方法として、名前付きのライブラリを使用します。

フレームワーク拡張JARファイルのライブラリを定義する手順:

  1. JDeveloperのメイン・メニューで「ツール」→「ライブラリの管理」を選択します。

  2. 「ライブラリの管理」ダイアログで「ライブラリ」タブを選択します。

  3. ツリーで「User」フォルダを選択し、「新規」ボタンをクリックします。

  4. 表示される「ライブラリの作成」ダイアログで、ライブラリにFramework Extension Layerという名前を設定し、「クラス・パス」ノードを選択して、「エントリの追加」をクリックします。

  5. 表示される「パス・エントリの選択」ダイアログで、フレームワーク拡張コンポーネントのクラス・ファイルを含むFrameworkExtensions.jarファイルを選択し、「選択」をクリックします。

  6. 「ソースパス」ノードを選択し、「エントリの追加」をクリックします。

  7. 表示される「パス・エントリの選択」ダイアログで、フレームワーク拡張クラスのソース・ファイルが存在する..\FrameworkExtensions\srcディレクトリを選択し、「選択」をクリックします。

  8. 「OK」をクリックして「ライブラリの作成」ダイアログを閉じ、新しいライブラリを定義します。

終了すると、図37-6に示すように、新しいユーザー定義ライブラリFramework Extension Layerが表示されます。この後は、ビジネス・サービスを作成するプロジェクトのライブラリ・リストにこのライブラリを追加することで、優先使用するコンポーネント・ベース・クラスとしてカスタム・フレームワーク拡張クラスが参照されるようになります。

図37-6 フレームワーク拡張レイヤーの新しいユーザー定義ライブラリ

「ライブラリの管理」ダイアログ

37.3 拡張クラスによるフレームワークの動作のカスタマイズ

フレームワーク拡張クラスで行う共通のタスクの1つは、カスタム・アプリケーション機能の実装です。フレームワーク拡張コードは特定の種類の全コンポーネントで使用されるように記述するので、これらのクラスで記述するコードでは、通常、一般的な方法でコンポーネントの属性を処理する必要があります。これに対応するため、ADFでは、実行時にコンポーネントのメタデータにアクセスできるAPIが提供されています。また、カスタム・メタデータのプロパティとコンポーネントまたは属性を関連付ける機能も提供されています。作成する汎用的なフレームワーク拡張コードでは、ランタイム・メタデータとカスタム・プロパティを使用して、必要な場合には特定のカスタム・プロパティが存在する場合にのみ使用される、一般的な機能を作成できます。


注意:

この項の例では、Fusion Order DemoアプリケーションのStandaloneExamplesモジュールのAdvancedExamplesアプリケーション・ワークスペースのProgrammaticallySetPropertiesプロジェクトを参照します。

37.3.1 ビュー・オブジェクトおよびエンティティ・オブジェクトのランタイム・メタデータにアクセスする方法

図37-7は、ビュー・オブジェクトおよびエンティティ・オブジェクトに関するランタイム・メタデータにアクセスするためにADFが提供する3つの主要なインタフェースを示しています。ViewObjectインタフェースは、StructureDefインタフェースを拡張します。エンティティ定義を表すクラス(EntityDefImpl)も、このインタフェースを実装します。名前が示すように、StructureDefは構造とコンポーネントを定義しており、ビュー・オブジェクト行またはエンティティ行の各属性に関するランタイム・メタデータを提供するAttributeDefオブジェクトのコレクションへのアクセスを提供します。AttributeDefを使用することで、それに付随するAttributeHintsオブジェクトにアクセスし、表示ラベル、フォーマット・マスク、ツールチップなどのヒントを参照できます。

図37-7 ビュー・オブジェクトおよびエンティティ・オブジェクトで使用できるランタイム・メタデータ

オブジェクトで使用可能なメタデータの流れ

37.3.2 ランタイム・メタデータを使用した汎用機能の実装方法

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.
}

37.3.3 カスタム・プロパティによって駆動される汎用機能の実装方法

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()メソッドを使用します。

37.3.4 設計時のカスタム・プロパティ名の構成方法

カスタム・プロパティに依存するフレームワーク拡張クラスを作成すると、対応するコンポーネント・エディタの「カスタム・プロパティ」セクションのリストにカスタム・プロパティ名が表示されるように、JDeveloperを設定できます。定義済のカスタム・プロパティ名を設定するには、JDeveloperのメイン・メニューから「ツール」→「設定」を選択し、「設定」ダイアログで「ビジネス・コンポーネント」→「プロパティ名」タブを開きます。

37.3.5 属性の種類について

属性の名前、Java型、SQL型、および他の多くの有用な情報を提供するだけでなく、AttributeDefインタフェースのgetAttributeKind()メソッドを使用することで、属性が表す種類を判定することができます。このメソッドは、表37-1で示されているAttributeDefインタフェースのパブリック定数のいずれかに対応するbyte値を返します。

表37-1 エンティティ・オブジェクトとビュー・オブジェクトの属性の種類

パブリックAttributeDef定数 属性の種類の説明

ATTR_PERSISTENT

永続的属性

ATTR_TRANSIENT

一時的属性

ATTR_ENTITY_DERIVED

エンティティ・レベルの一時的属性にマップされるビュー・オブジェクト属性

ATTR_SQL_DERIVED

SQL計算属性

ATTR_DYNAMIC

動的属性

ATTR_ASSOCIATED_ROWITERATOR

0以上のRowのセットのRowSetを返すアクセッサ属性

ATTR_ASSOCIATED_ROW

単一のRowを返すアクセッサ属性


37.3.6 カスタム・プロパティについて

実行時にカスタム・プロパティの値をプログラムで設定すると便利な場合があります。この機能を実行するためのsetProperty() APIは、設計上、oracle.jboパッケージのViewObjectApplicationModuleまたはAttributeDefインタフェースのクライアントでは利用できませんが、ADFコンポーネントのカスタムJavaクラスで記述したコードでは使用できます。

37.4 汎用拡張インタフェースの作成

フレームワーク拡張クラスの作成に加えて、すべてのコンポーネントがデフォルトで実装できるカスタム・インタフェースも作成できます。クライアント・インタフェースは、たとえばUIクライアントから起動されるアプリケーション・モジュールからメソッドを公開する際に、非常に便利です。ここではアプリケーション・モジュールの例について説明しますが、カスタム拡張ビュー・オブジェクトおよびビュー行インタフェースに対しても同じ機能を作成できます。クライアント・インタフェースの詳細は、9.9項「UIクライアントへのカスタム・サービス・メソッドの公開」および9.10項「アプリケーション・モジュールのクライアント・インタフェースのプログラム的操作」も参照してください。

ApplicationModuleImplを拡張するCustomApplicationModuleImplクラスがあり、次のような2つのカスタム・メソッドを公開するものとします。

public void doFeatureOne(String arg);
public int anotherFeature(String arg);

カスタム拡張インタフェースCustomApplicationModuleを作成し、CustomApplicationModuleImplクラスでそれを実装するには、次の手順を実行します。

  1. アプリケーション・モジュール・コンポーネントでグローバルに公開するメソッドを含むカスタム・インタフェースを作成します。このシナリオでは、このインタフェースは次のようになります。

    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インタフェースを拡張していないことに注意してください。

  2. 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();
      }
    }
    
  3. プロジェクトを再ビルドします。

    ADFウィザードは、コンパイルが正常に終了したインタフェースのみを参照します。

CustomApplicationModuleImplクラスを実装すると、新しいアプリケーション・モジュールを作成できます。このモジュールはグローバル拡張インタフェースを公開し、カスタム・フレームワーク拡張クラスに基づくものです。この作成には、アプリケーション・モジュールの概要エディタを使用します。

カスタム・アプリケーション・モジュールを作成する手順:

  1. アプリケーション・ナビゲータで、カスタム・インタフェースを作成するアプリケーション・モジュールをダブルクリックします。

    たとえば、グローバル拡張インタフェースCustomApplicationModuleを公開し、CustomApplicationModuleImplフレームワーク拡張クラスに基づく新しいProductModuleアプリケーション・モジュールを作成するとします。

  2. 概要エディタで、「Java」ナビゲーション・タブを選択し、「Javaオプションの編集」アイコンをクリックします。

    「Javaクラス」ページには、「アプリケーション・モジュール・クラス」として識別されたアプリケーション・モジュールの既存のJavaクラスが表示されます。

    デフォルトでは、作成するアプリケーション・モジュール用のJavaクラスがJDeveloperにより生成されます。ただし、この機能を無効にした場合は、「Javaクラス」セクションの「Javaオプションの編集」ボタンをクリックし、続いて「アプリケーション・モジュール・クラスの生成」を選択します。「OK」をクリックして、カスタム・インタフェースの作成元となるプロジェクトにJavaクラスを追加します。

  3. 「Javaオプションの選択」ダイアログで、「クラスの拡張」をクリックします。

  4. 「ベース・クラスのオーバーライド」ダイアログで、オーバーライドするフレームワーク・ベース・クラスの名前を指定して、「OK」をクリックします。

    たとえば、アプリケーション・モジュールのベース・クラスとしてCustomApplicationModuleImplを選択します。

  5. 概要エディタの「Javaクラス」ページで、「クライアント・インタフェース」セクションを展開し、「アプリケーション・モジュール・クライアント・インタフェースの編集」ボタンをクリックします。

  6. 「クライアント・インタフェースの編集」ダイアログで、「インタフェース」ボタンをクリックします。

  7. 「拡張するインタフェースの選択」ダイアログで、利用可能なリストから目的のカスタム・アプリケーション・モジュール・インタフェースを選択して「OK」をクリックします。

    たとえば、CustomApplicationModuleインタフェースを「選択済」リストに移動してコンポーネントで使用できるカスタム・インタフェースの1つとなるようにします。

  8. 「クライアント・インタフェースの編集」ダイアログで、「選択済」リストに、少なくとも1つのメソッドが表示されることを確認します。


    注意:

    グローバル拡張インタフェースのメソッドの1つを重複して選択することになるとしても、「クライアント・インタフェース」ダイアログの「選択済」リストでは少なくとも1つのメソッドを選択する必要があります。JDeveloperでカスタム・インタフェースを生成するには、どのメソッドでもかまいません。

  9. 「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インタフェースにキャストし、それに含まれる汎用拡張メソッドを強く型付けされた方法で呼び出すことができます。


注意:

ViewObjectImplフレームワーク拡張クラスおよびViewRowImpl拡張クラスのメソッドを公開する場合も、基本的な手順は同じです。

37.5 ストアド・プロシージャとストアド・ファンクションの呼出し

ビジネス・コンポーネント用のカスタム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アプリケーションのStandaloneExamplesモジュールのAdvancedExamplesアプリケーション・ワークスペースのStoredProcedureInvocationプロジェクトを参照します。

37.5.1 引数のないストアド・プロシージャの呼出し方法

引数を受け取らないストアド・プロシージャを呼び出す必要がある場合は、(例37-8に示すようにoracle.jbo.serverパッケージ内の)DBTransactionインタフェースのexecuteCommand()メソッドを使用できます。

例37-8 引数のないストアド・プロシージャの実行

// In StoredProcTestModuleImpl.java
public void callProcWithNoArgs() {
  getDBTransaction().executeCommand(
    "begin devguidepkg.proc_with_no_args; end;");
}

37.5.2 IN引数のみのストアド・プロシージャの呼出し方法

INモード引数のみを受け取るストアド・プロシージャ(何も指定しない場合のデフォルトのPL/SQLパラメータ・モード)を呼び出すには、JDBC PreparedStatementオブジェクトを使用する必要があります。DBTransactionインタフェースでは、現在のデータベース接続のコンテキストでこのオブジェクトを作成するためのcreatePreparedStatement()メソッドが提供されています。例37-9で示すようなヘルパー・メソッドを使用することで、PreparedStatementを使用してこの種のストアド・プロシージャを呼び出す処理が簡単になります。重要なこととして、ヘルパー・メソッドを使用することにより、実行後にJDBC PreparedStatementを閉じるコードをカプセル化できます。このコードが実行する基本的な処理は次のとおりです。

  1. 渡される文のためのJDBC PreparedStatementを作成し、PL/SQLのbegin...endブロック内にラップします。

  2. バインド変数が渡された場合は、値のループ処理を行います。

  3. 各バインド変数の値を文に設定します。

    JDBCのバインド変数APIは1から始まる番号付けを使用するため、0から始まるforループのインデックス変数にコードで1を加えていることに注意してください。

  4. 文を実行します。

  5. 文を閉じます。

例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-7proc_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は名前付きのバインド変数の使用もサポートしますが、ヘルパー・メソッドは位置でバインド変数の値を設定するのみであるため、このように単純な位置によるバインド変数を使用しても問題ありません。

37.5.3 IN引数のみのストアド・ファンクションの呼出し方法

INモードの引数のみを受け取るストアド・ファンクションの呼出しでは、文を実行した後でファンクションの結果の値にアクセスするために、JDBC CallableStatementオブジェクトを使用する必要があります。DBTransactionインタフェースでは、現在のデータベース接続のコンテキストでこのオブジェクトを作成するためのcreateCallableStatement()メソッドが提供されています。例37-10に示すようにヘルパー・メソッドを使用することで、CallableStatementを使用してこの種のストアド・ファンクションを呼び出す処理が簡単になります。ヘルパー・メソッドは、使用するJDBC文の作成とクリーン・アップの両方をカプセル化します。

このコードが実行する基本的な処理は次のとおりです。

  1. 渡される文のためのJDBC CallableStatementを作成し、PL/SQLのbegin...endブロック内にラップします。

  2. 最初のバインド変数をファンクションの戻り値に登録します。

  3. バインド変数が渡された場合は、値のループ処理を行います。

  4. ユーザーが提供した各バインド変数の値を文に設定します。

    JDBCのバインド変数APIは1から始まる番号付けを使用していて、ファンクションの戻り値は文の最初のバインド変数であるため、0から始まるforループのインデックス変数にコードで2を加えていることに注意してください。

  5. 文を実行します。

  6. 最初のバインド変数の値を返します。

  7. 文を閉じます。

例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-7func_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は名前付きのバインド変数の使用もサポートしますが、ヘルパー・メソッドは位置でバインド変数の値を設定するのみであるため、このように単純な位置によるバインド変数を使用しても問題ありません。

37.5.4 他の種類のストアド・プロシージャの呼出し方法

OUTモードまたはIN OUTモードの引数を含むdevguidepkg.proc_with_out_argsのようなストアド・プロシージャまたはストアド・ファンクションの呼出しでは、前の項の場合と同じようにCallableStatementを使用する必要がありますが、ヘルパー・メソッドへの一般化は少し難しくなります。例37-11は、devguidepkg.proc_with_out_argsプロシージャを呼び出すために必要なJDBCコードを示しています。

このコードが実行する基本的な処理は次のとおりです。

  1. 文が呼び出すPL/SQLブロックを定義します。

  2. PL/SQLブロックに対するCallableStatementを作成します。

  3. OUTパラメータの位置と型を登録します。

  4. INパラメータのバインド変数を設定します。

  5. 文を実行します。

  6. 複数の戻り値を保持するためのJavaBeanを作成します。

    DateAndStringBeanクラスには、dateValおよびstringValという名前のBeanプロパティが含まれます。

  7. 最初のOUTパラメータを使用して、dateValプロパティの値を設定します。

  8. 2番目のOUTパラメータを使用して、stringValプロパティの値を設定します。

  9. 結果を返します。

  10. 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がjava.io.Serializableインタフェースを実装する必要があります。これはマーカー・インタフェースであるため、implements Serializableキーワードを追加するのみでよく、インタフェースのメソッドの実装をコーディングする必要はありません。

37.6 現在のデータベース・トランザクションへのアクセス

ADF Business Componentsのコンポーネントは下位レベルのデータベース・プログラミングの詳細をすべて抽象化しているので、通常は、JDBCのConnectionオブジェクトに直接アクセスする必要はありません。40.2.2.3.3項「予約解放レベルについて」で説明されている予約されたリリース・モードを使用していない場合は、実行時に、異なるWebページ・リクエストの間で、まったく同じアプリケーション・モジュール・インスタンスまたはJDBC Connectionインスタンスがアプリケーションで使用されることが保証されません。この種のプールされたサービス環境でJDBC Connectionオブジェクトへの参照を誤って保持すると、実行時に想定外の動作が発生する可能性があるため、設計として、ADF Business ComponentsレイヤーにはJDBC Connectionを直接取得するAPIはありません。これは、JDBC Connectionの直接的な使用および不注意による誤用を防ぐために意図的に行われていることです。

ただし、サード・パーティのコードをADF Business Componentsと統合する場合はこれが可能であると便利なため、例37-12で示すようなヘルパー・メソッドを使用してConnectionにアクセスできます。

例37-12 現在の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;
}

注意:

例37-12のヘルパー・メソッドを使用して取得したJDBC Connectionを、独自のコード内のどこにもキャッシュしないでください。かわりに、必要になるたびにヘルパー・メソッドを呼び出すようにすることで、JDBC Connectionに対する参照を誤って保持し、それが後で別のユーザーによる別のリクエストで、ADFランタイム環境のプールされたサービスに対して使用されるのを防ぐようにします。

37.7 再利用可能なビジネス・コンポーネントのライブラリでの作業

他のJavaコンポーネントと同様に、再利用可能なADFコンポーネントの1つ以上のパッケージを含むJARファイルを作成できます。その後、他のプロジェクトでこのコンポーネント・ライブラリからコンポーネントのパッケージをインポートし、新しいアプリケーションで参照できます。

37.7.1 ビジネス・コンポーネントの再利用可能なライブラリの作成方法

ビジネス・コンポーネントのライブラリを構成する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 Business Componentsベースのアプリケーションを簡単にデプロイできるよう、このように分割されています。*CSMT.jarは、中間層のアプリケーション・サーバーにのみデプロイされるよう設計されているコンポーネントのアーカイブです。*CSCommon.jarは、アプリケーション・モジュールと通信するクライアントがそのアプリケーション・モジュールとは異なる物理サーバーで実行しているデプロイ・シナリオで、アプリケーション・サーバーとリモート・クライアント層の両方に共通のものです。

図37-8 ネストされたプロファイルを含むビジネス・コンポーネント・アーカイブ・デプロイメント・プロファイル

「プロジェクト・プロパティ」ダイアログのデプロイメント・プロファイル

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

37.7.2 ライブラリから再利用可能なコンポーネントのパッケージをインポートする方法

ビジネス・コンポーネントの再利用可能なライブラリを作成すると、そのライブラリからコンポーネントのパッケージを他のプロジェクトにインポートして参照することができます。ライブラリからビジネス・コンポーネントのパッケージをインポートすると、ADF Business Componentsコンポーネントのウィザードおよびエディタの様々な「使用可能」リストでそのパッケージのコンポーネントを使用できるようになりますが、アプリケーション・ナビゲータには表示されず、編集もできません。アプリケーション・ナビゲータに表示されるのは、現在のプロジェクトのソース・パスに存在するコンポーネントのみです。


ヒント:

アプリケーション・ナビゲータに表示される編集可能なコンポーネントが必要な場合は、現在のプロジェクトのソース・パスに含まれていないディレクトリからビジネス・コンポーネントを追加し、「プロジェクト・プロパティ」ダイアログの「プロジェクト・コンテンツ」ページを開いて、追加するコンポーネントの親ディレクトリを「Javaコンテンツ」リストのディレクトリの1つとして追加します。インポートしたコンポーネントのパッケージとは異なり、プロジェクトのソース・パスに追加したコンポーネントは、完全に編集可能であり、アプリケーション・ナビゲータに表示されます。

ライブラリからビジネス・コンポーネントのパッケージをインポートする手順:

  1. インポートを行うプロジェクトの「プロジェクト・プロパティ」ダイアログの「ライブラリ」タブで、JARファイルのライブラリを定義します。

    ライブラリは、プロジェクト・レベルまたはユーザー・レベルとして定義できます。ライブラリ定義のクラス・パスに、*CSMT.jar*CSCommon.jarの両方を必ず含めます。

  2. インポートを行うプロジェクトのライブラリ・リストに、新しいライブラリを追加します。

  3. アプリケーション・ナビゲータでインポートを行うプロジェクトを選択し、「ファイル」メニューから「インポート」を選択します。

  4. 「インポート」ダイアログで、リストから「ビジネス・コンポーネント」を選択します。

  5. ファイルを開くダイアログを使用して、ディレクトリの場合と同じようにライブラリの*CSMT.jarファイルの中に移動し、インポートするコンポーネントのパッケージ内のコンポーネントから、XMLコンポーネント定義ファイルを選択します。

  6. パッケージのインポートが成功したことを示すアラートを確認します。

  7. インポートするコンポーネントのパッケージごとに、手順3から6を繰り返します。

インポートしたコンポーネントのパッケージにProductのようなエンティティ・オブジェクトがあったとすると、エンティティ・オブジェクトの慣用名として、インポートしたProductコンポーネントを使用し、インポートを行っているプロジェクト内に新しいビュー・オブジェクトを作成できます。これはほんの一例です。インポートした任意のコンポーネントを、プロジェクトのソース・パスにある場合と同じように参照できます。唯一の違いは、インポートしたコンポーネントは編集できないことです。実際、再利用可能なコンポーネント・ライブラリのJARファイルには、コンポーネントのXMLコンポーネント定義ファイルとJava *.classファイルのみが含まれ、ソース・コードは含まれない場合があります。

37.7.3 インポートしたパッケージをプロジェクトから削除する方法

コンポーネントのパッケージを誤ってインポートした場合、またはインポートしたコンポーネントのパッケージで使用していないものを削除する場合、「プロジェクト・プロパティ」ダイアログを使用してこれを実行することができます。

パッケージのインポートを取り消す手順:

  1. アプリケーション・ナビゲータで、インポート解除するパッケージを含むモデル・プロジェクトを右クリックし、「プロジェクト・プロパティ」を選択します。

  2. 「プロジェクト・プロパティ」ダイアログで「ビジネス・コンポーネント」→「インポート」を選択します。

  3. インポートされたプロジェクトのリストから目的のプロジェクトを選択し、「削除」をクリックします。

  4. 「OK」をクリックし、JDeveloperの変更を保存します。


注意:

プロジェクト内の他のコンポーネントからまだ参照されているインポート済パッケージは、削除しないでください。このようなパッケージを削除すると、プロジェクトを開いたときに例外が発生するか、またはアプリケーションの動作が予測できなくなる可能性があります。インポートしたパッケージのエントリを*.jpxファイルから手動で削除する場合は、最初に、「プロジェクト・プロパティ」ダイアログの「ビジネス・コンポーネント: インポート」ページで、「使用方法の表示」ボタンをクリックし、そのパッケージ内のコンポーネントを参照しているものがないことを確認してください。

37.7.4 ライブラリから再利用可能なコンポーネントのパッケージをインポートするときの処理

コンポーネントのパッケージをYourImportingProjectNameという名前のプロジェクトにインポートすると、インポートしているプロジェクトのソース・パスのルート・ディレクトリにあるYourImportingProjectName.jpxファイルに、そのパッケージへの参照が追加されます。このエントリの一部として、_LocationURLという名前の設計時プロジェクトが含まれ、その値はインポートされたコンポーネントが存在するJARファイルを指します。

37.7.5 インポートされたプロジェクトについて

インポートされたライブラリ内のコンポーネントは編集不能です。ビジネス・コンポーネントを含んだパッケージをプロジェクトでインポートする場合、インポートするプロジェクトでは同じパッケージに新しいコンポーネントを追加できません。インポートするプロジェクトでは、その他のプロジェクト内に作成された新しいコンポーネントにあるインポート済コンポーネントを参照できますが、インポートしたパッケージに新しいコンポーネントを追加できません。

また、インポートしたコンポーネントを変更し、それを格納しているJARファイルを更新した場合、変更を反映するには、インポートしているプロジェクトを閉じてから開き直す必要があります。このとき、JDeveloperを終了する必要はありません。アプリケーション・ナビゲータでインポートしているプロジェクトを選択し、「ファイル」メニューから「閉じる」を選択した後、プロジェクトのノードを再び展開し、プロジェクトを閉じて開き直します。この手順を実行すると、インポートされているJARファイルの更新バージョンからコンポーネントが再度読み込まれます。

37.8 ビジネス・コンポーネントのエラー・メッセージのカスタマイズ


注意:

この項の例では、Fusion Order DemoアプリケーションのStandaloneExamplesモジュールのAdvancedExamplesアプリケーション・ワークスペースのCustomizedErrorMessagesプロジェクトを参照します。

カスタム・メッセージ・バンドルでエラー・コードに対してかわりのメッセージ文字列を提供することにより、組み込まれているADF Business Componentsのエラー・メッセージをカスタマイズできます。


注意:

便宜のため、Fusion Order DemoのAdvancedExamplesワークスペースのCustomizedErrorMessagesプロジェクトでは、この項で説明したカスタム・メッセージ・バンドル・サンプルをテストできます。ProductModuleを実行する前に、addProductsTableConstraint.sqlスクリプトを実行して、該当するデータベース制約でサンプルを設定する必要があります。Business Component BrowserでProductModuleアプリケーション・モジュールを実行する際、ProductId属性を空白にして「検証」ボタンをクリックできます。これにより、JOBエラーのカスタム・エラー・メッセージが表示されます。変更をロールバックして、ProductName属性にundefinedを入力します。「コミット」をクリックすると、データベース・エラーのカスタム・エラー・メッセージを表示できます(「検証」をクリックしても、データベースへのコミットは実行されないため、この場合、データベース・エラーは生成されません)。

37.8.1 基になっているADF Business Componentsのエラー・メッセージをカスタマイズする方法

次の組込みエラー・メッセージをカスタマイズするとします。

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}は属性の名前に置き換えられます。

カスタムのメッセージ・バンドル・ファイルを作成するには、次のようにします。

  1. アプリケーション・ナビゲータで、メッセージ・バンドル・ファイルを追加するモデル・プロジェクトを右クリックし、「プロジェクト・プロパティ」を選択します。

  2. 「プロジェクト・プロパティ」ダイアログで「ビジネス・コンポーネント」→「オプション」を選択します。

    「このプロジェクトで使用するカスタム・メッセージ・バンドル」リストがダイアログの下部に表示されます。

  3. 「新規」をクリックします。

  4. 「MessageBundleクラスの作成」ダイアログでカスタム・メッセージ・バンドルの名前とパッケージを入力し、「OK」をクリックします。


    注意:

    カスタム・メッセージ・バンドル・ファイルの完全修飾名が「このプロジェクトで使用するカスタム・メッセージ・バンドル」リストに表示されない場合は、「削除」ボタンをクリックした後、「追加」ボタンをクリックして、作成した新しいメッセージ・バンドル・ファイルを追加してください。カスタム・メッセージ・バンドル・ファイルが正しく登録されると、その完全修飾クラス名がリストに表示されます(図37-9を参照)。

    図37-9 リソース・メッセージ・バンドルが表示されたプロジェクト・プロパティ

    「プロジェクト・プロパティ」ダイアログのリソース・バンドル
  5. 「OK」をクリックして「プロジェクト・プロパティ」ダイアログを閉じ、新しいカスタム・メッセージ・バンドル・クラスをソース・エディタで開きます。

  6. カスタム・メッセージ・バンドル・クラスの2次元のString配列を編集し、使用するカスタマイズしたメッセージを入力します。

    例37-13は、JBO-27014エラーのエラー・メッセージ文字列をオーバーライドするカスタム・メッセージ・バンドル・クラスを示しています。

    例37-13 カスタムADF Business Componentsメッセージ・バンドル

    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;
      }
    }
    

37.8.2 基になっているADF Business Componentsのエラー・メッセージをカスタマイズするときに行われる処理

このメッセージをカスタム・メッセージ・バンドル・ファイルに追加した後、Business Component Browserを使用してアプリケーションをテストし、必須属性の値を空白にすると、デフォルトのメッセージのかわりにカスタム・エラー・メッセージが表示されます。

JBO-27014: You must provide a value for Name

必要なだけいくつでも、メッセージ・バンドルにメッセージを追加できます。エラー・コード・キーが組込みエラー・メッセージ・コードのいずれかと一致するメッセージは、実行時に、oracle.jbo.CSMessageBundleメッセージ・バンドルのデフォルト・メッセージのかわりに使用されます。

37.8.3 カスタマイズ・エラー・メッセージをネストされた例外として表示する方法

ADF Business Componentsのエラー・メッセージをカスタマイズする場合、ネストされたエラー・メッセージの表示もカスタマイズする必要があります。そのためには、カスタム・エラー・ハンドラ・クラスを作成して登録する必要があります。

ビジネス・メソッドでエラーがスローされると、ADFバインディング・レイヤーはエラーを遮断して、登録されたカスタム・エラー・ハンドラ・クラスを呼び出します。一般に、エラー・ハンドラ・クラスは例外を読取り可能になるように書式設定します。このプロセスで、デフォルト・エラー・ハンドラDCErrorHandlerImplは通常、最上位のJboExceptionをスキップします。このオブジェクトは他のビジネス例外のラッパーでビジネス上重要でないためです。

最上位の例外をスキップすることが、ADF Business Componentsエラーの場合の望ましい動作ですが、デフォルトの動作では、SQLExceptionを置換するために設定されたカスタム・メッセージがスキップされます。この状況を回避するために、ネストされた例外の各項目を表示する間、カスタム・エラー・ハンドラ・クラスはDCErrorHandlerImpl::skipException(Exception ex)をオーバーライドして、該当する例外を最終リストのユーザーに表示するかどうかを決定します。

作業を始める前に、次のようにします。

アプリケーション・モジュールの知識があると役立ちます。詳細は、37.10項「提供されるアプリケーションでの拡張コンポーネントの置換え」を参照してください。

次のタスクを完了する必要があります。

37.8.1項「基になっているADF Business Componentsのエラー・メッセージをカスタマイズする方法」の説明に従って、リソース・バンドルのエラー・メッセージを作成します。

プロジェクトのSQLExceptionsのカスタム・メッセージを指定する手順:

  1. ADFバインディング・レイヤーで提供されるデフォルト・エラー・ハンドラDCErrorHandlerImplインタフェースを拡張する、エラー・ハンドラ・クラスを作成します。

  2. エラー・ハンドラ・クラスで、DCErrorHandlerImpl::skipException(Exception ex)メソッドのデフォルト・エラー・ハンドラ動作を例37-14に示すようにオーバーライドします。

    データベース・レベルのエラー・メッセージに対して戻されるものなど、ネストされた例外の各項目を表示するには、このメソッドのオーバーライドが必須です。固有の例外タイプをチェックするロジックを実装する必要があります。また、ビジネス・シナリオに基づいて、それをリストに表示するかどうかを決定する必要があります。

  3. その後、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);
    }

)

37.8.4 データベース制約違反のエラー・メッセージのカスタマイズ方法

データベースに制約を設定している場合、制約の違反が発生したときに、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;
  }
}

37.8.5 カスタム制約エラー処理ルーチンの実装方法

カスタム・メッセージをデータベース制約違反に割り当てるデフォルト機能が要件を満たさない場合は、独自のカスタム制約エラー処理ルーチンを実装できます。そのためには、ADFトランザクション・クラスに対するカスタム・フレームワーク拡張クラスを作成し、実行時にそれを使用するようアプリケーション・モジュールを構成する必要があります。

37.8.5.1 カスタム・データベース・トランザクション・フレームワーク拡張クラスの作成

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;
    }
  }
}

37.8.5.2 カスタム・データベース・トランザクション・クラスを使用するためのアプリケーション・モジュールの構成

アプリケーション・モジュールで実行時にカスタム・データベース・トランザクション・クラスを使用するには、次の手順を実行する必要があります。

  1. カスタマイズしたトランザクション・クラスのインスタンスを返すようにcreate()メソッドをオーバーライドするDatabaseTransactionFactoryクラスのカスタム実装を提供します。

  2. 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-10 ADFビジネス・コンポーネントではカスタムのデータベース・トランザクション・クラスを使用可能

ビジネス・コンポーネントでカスタムのデータベース・トランザクション・クラスを使用可能

37.9 継承を使用する拡張コンポーネントの作成

新しいビジネス・コンポーネントを作成するたびに、必要に応じて、既存のものを拡張してオリジナルのカスタマイズ・バージョンを作成できます。図37-11に示すように、ProductsByNameビュー・オブジェクトでは、Productsビュー・オブジェクトを拡張してTheStatusという名前の名前付きバインド変数が追加され、このバインド変数を参照するようにWHERE句がカスタマイズされています。

図37-11 別のコンポーネントを拡張できるADF Business Components

ビジネス・コンポーネントは相互に拡張可能

図ではビュー・オブジェクトの例が示されていますが、このコンポーネント継承機能はすべての種類のコンポーネントで使用できます。あるコンポーネントが別のコンポーネントを拡張する場合、拡張されたコンポーネントは親からすべてのメタデータと動作を継承します。拡張されたコンポーネントでは、メタデータとJavaコードの両方を使用して、新しい機能を追加したり、親コンポーネントの既存機能をカスタマイズしたりできます。


注意:

この項の例では、Fusion Order DemoアプリケーションのStandaloneExamplesモジュールのAdvancedExamplesワークスペースのBaseProjectプロジェクトを参照します。

37.9.1 別のコンポーネントを拡張するコンポーネントの作成方法

拡張コンポーネントを作成するには、作成するコンポーネントの種類に対する「新規ギャラリ」のコンポーネント・ウィザードを使用します。たとえば、拡張ビュー・オブジェクトを作成するには、ビュー・オブジェクトの作成ウィザードを使用します。ウィザードの「名前」ページで、新しいコンポーネントの名前とパッケージを指定することに加えて、拡張するコンポーネントの完全修飾名を拡張フィールドで指定します。リストからコンポーネント名を選択するには、拡張フィールドの隣の「参照」ボタンを使用します。その後は、ウィザードの残りのパネルを使用して、通常の方法で拡張コンポーネントの作成を続けます。

37.9.2 コンポーネントを作成後に拡張する方法

拡張コンポーネントを定義した後で、拡張コンポーネントが継承している親コンポーネントを変更できます。コンポーネントの概要エディタをこの目的に使用できます。

作成後に親コンポーネントを変更する手順:

  1. コンポーネントをダブルクリックします。

  2. コンポーネントの概要エディタで、「一般」ナビゲーション・タブをクリックし、「拡張対象」フィールドの横にある「オブジェクト拡張対象をリファクタします。」ボタンをクリックします。

  3. 「親の選択」ダイアログで、パッケージ・リストから拡張する目的のコンポーネントを選択します。

どの親からも継承しないように拡張コンポーネントを変更するには、「親の選択」ダイアログの「なし」チェック・ボックスを選択します。これは、この目的のためのコンポーネント削除および再作成と同じ結果になります。

37.9.3 別のコンポーネントを拡張するコンポーネントの作成時に行われる処理

作成するADF Business Componentsは、XMLコンポーネント定義とオプションのJavaクラスで構成されます。別のコンポーネントを拡張するコンポーネントを作成すると、JDeveloperは、拡張されたコンポーネントのXMLコンポーネント定義と生成されるJavaコードの両方に、このコンポーネント継承を反映します。

37.9.3.1 拡張コンポーネントのXMLディスクリプタの理解

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>

37.9.3.2 拡張コンポーネントに対するJavaコード生成の理解

拡張コンポーネントでカスタムJavaコードを有効にすると、親コンポーネントの各Javaクラスを拡張するためのJavaクラスが自動的に生成されます。これにより、拡張コンポーネントでは、必要に応じて、親コンポーネントのプログラム的な動作のすべての面をオーバーライドできます。親コンポーネントが、独自のカスタムJavaクラスを持たないXMLのみのコンポーネントである場合は、拡張コンポーネントのJavaクラスは、親が実行時に使用するベースJavaクラスを拡張します。これは、oracle.jbo.serverパッケージのデフォルトのADF Business Componentsフレームワーク・クラス、または親コンポーネントの拡張ダイアログで指定されている場合は独自のフレームワーク拡張クラスなどになります。

さらに、拡張コンポーネントがアプリケーション・モジュールまたはビュー・オブジェクトで、クライアント・インタフェースを有効にしてある場合は、拡張コンポーネントのクライアント・インタフェースが自動的に生成され、親コンポーネントの各クライアント・インタフェースが拡張されます。親コンポーネントのクライアント・インタフェースが存在しない場合は、拡張コンポーネントのクライアント・インタフェースは、oracle.jboパッケージの適切なベースADF Business Componentsインタフェースを直接拡張します。

37.9.4 注意事項

37.9.4.1 親のクラスとインタフェースは拡張コンポーネントの操作にも使用できる

拡張コンポーネントは親のカスタマイズ・バージョンであるため、コンポーネントのJavaクラスまたはクライアント・インタフェースで動作するように作成したコードは、親コンポーネントまたはそのカスタマイズ・バージョンで問題なく動作します。

たとえば、次のようなカスタムJavaクラスとクライアント・インタフェースを含むベースProductsビュー・オブジェクトがあるものとします。

  • クラスProductsImpl

  • 行クラスProductsRowImpl

  • インタフェースProducts

  • 行インタフェースProductsRow

Productsを拡張するProductsByNameビュー・オブジェクトを作成した場合、ベース・コンポーネントのクラスとインタフェースを使用して、ProductsProductsByNameの両方を操作できます。

例37-20は、ProductsProductsRowProductsByNameおよびProductsByNameRow の各クライアント・インタフェースで動作するテスト用のクライアント・プログラムです。この例については、次のことに注意してください。

  1. 親のProductsインタフェースを、それを拡張するProductsByNameビュー・オブジェクトを処理するために使用できます。

  2. 別の方法として、ProductsByNameビュー・オブジェクトのインスタンスを、独自のさらに限定的なProductsByNameクライアント・インタフェースにキャストすることもできます。

  3. キャストして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はProductエンティティ・オブジェクトに基づくエンティティ・ベースのビュー・オブジェクトです。Productエンティティ・オブジェクトには、一時的属性Checksumが含まれ、I am the Product classという文字列を返します。この一時的属性が含まれる理由の詳細は、37.10項「提供されるアプリケーションでの拡張コンポーネントの置換え」の例を参照してください。

37.9.4.2 拡張コンポーネントでは「クラスの拡張」が無効になる

拡張コンポーネントを作成するとき、拡張コンポーネントのウィザードの「Java」ページにある「クラスの拡張」ボタンは無効になります。また、アプリケーション・モジュール・エディタの「Java」ページで、「Javaオプションの編集」ボタンをクリックすると、「Java」ダイアログの「クラスの拡張」ボタンは無効として表示されます。これは、JDeveloperが親コンポーネントの適切なクラスを自動的に拡張するため、別のクラスをユーザーが選択できるようにしても意味がないためです。

37.9.4.3 主要なコンポーネント・タイプで拡張できるもの

エンティティ・オブジェクト

拡張エンティティ・オブジェクトを作成するときは、新しい属性、新しいアソシエーション、新しいバリデータ、および新しいカスタム・コードを導入できます。既存の属性の特定の宣言的部分、および親コンポーネントのクラスのメソッドをオーバーライドできます。

ビュー・オブジェクト

拡張ビュー・オブジェクトを作成するときは、新しい属性、新しいビュー・リンク、新しいバインド変数、および新しいカスタム・コードを導入できます。既存の属性の特定の宣言的部分、および親コンポーネントのクラスのメソッドをオーバーライドできます。

アプリケーション・モジュール

拡張アプリケーション・モジュールを作成するときは、新しいビュー・オブジェクト・インスタンスまたは新しいネストされたアプリケーション・モジュール・インスタンス、および新しいカスタム・コードを導入できます。親コンポーネントのクラスのメソッドをオーバーライドすることもできます。

37.9.4.4 親を規準とする拡張コンポーネントの属性索引

拡張エンティティ・オブジェクトまたはビュー・オブジェクトで新しい属性を追加する場合、属性索引番号は親コンポーネントを規準にして計算されます。たとえば、37.9.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+1MAXATTRCONST+2などになります。

37.10 提供されるアプリケーションでの拡張コンポーネントの置換え

ソリューションの各クライアントがオンサイトでカスタマイズする必要のあるパッケージ化されたアプリケーションを配布する場合のために、ADF Business Componentsでは、この作業を簡単にする便利な機能が提供されています。


注意:

この項の例では、Fusion Order DemoアプリケーションのStandaloneExamplesモジュールのAdvancedExamplesワークスペースのBaseProjectプロジェクトとExtendAndSubstituteプロジェクトを参照します。

通常、オンサイトでのアプリケーションのカスタマイズは、配布されたアプリケーションのソース・コードを直接変更することで行います。この方法の短所が明らかになるのは、元のアプリケーションのパッチや新機能リリースをクライアントに提供するときです。基になるアプリケーションのソース・コードに適用されていたカスタマイズは、パッチまたは更新を行った後のアプリケーションに、わざわざ適用しなおす必要があります。この方法では、アプリケーションのカスタマイズやメンテナンスに費用がかかるだけでなく、以前のカスタマイズを新しいリリースに適用しなおすときの人為的エラーにより不具合が発生する可能性があります。

ADF Business Componentsが提供する優れたコンポーネント・ベースの方法を使用してアプリケーションをカスタマイズすると、基になっているアプリケーションのソース・コードを変更する必要はなく、ソース・コードにアクセスする必要さえありません。提供されたアプリケーションのカスタマイズは、次の方法で行います。

  1. 基になるアプリケーションのコンポーネントのパッケージを、新しいプロジェクトにインポートします。

  2. アプリケーションのカスタマイズを適用する新しいコンポーネントを作成し、必要に応じて基のアプリケーションの適切な親コンポーネントを拡張します。

  3. グローバルなコンポーネント置換のリストを定義し、カスタマイズしたコンポーネントの名前を設定して、基になるアプリケーションの適切な親コンポーネントを置き換えます。

ユーザーが定義されたグローバル・コンポーネント置換リストを使用し、配布されたアプリケーションを実行すると、配布されたアプリケーションは、コードを変更することなく、カスタマイズされたアプリケーション・コンポーネントを使用します。元のアプリケーションのパッチまたは更新バージョンが提供されると、次にアプリケーションを再起動するときにコンポーネントのカスタマイズが更新バージョンに適用されるので、カスタマイズを適用しなおす必要はありません。

37.10.1 拡張コンポーネントの置換方法

グローバルなコンポーネント置換を定義するには、基になるアプリケーションからインポートしたコンポーネントに基づいて拡張コンポーネントを作成したプロジェクトの、「プロジェクト・プロパティ」ダイアログを使用します。


注意:

基になるアプリケーションのコンポーネントのうち、基のコンポーネントを直接または間接に継承する拡張コンポーネントがあるもののみを置換できます。

拡張コンポーネントを置換する手順:

  1. アプリケーション・ナビゲータで、拡張コンポーネントを追加するモデル・プロジェクトを右クリックし、「プロジェクト・プロパティ」を選択します。

  2. 「プロジェクト・プロパティ」ダイアログで「ビジネス・コンポーネント」→「置換」を選択します。

  3. 「置換」ページの「使用可能」リストで、基になるアプリケーションのコンポーネントを選択します。

  4. 「置換する対象」リストで、置き換えるカスタマイズした拡張コンポーネントを選択します。

  5. 「追加」をクリックします。

    たとえば、ベース・ビュー・オブジェクトProductsを拡張するパッケージでビュー・オブジェクトCustomizedProductsが作成されているとします。レガシーProductsビュー・オブジェクトのCustomizedProductsビュー・オブジェクトを置換するには、これらビュー・オブジェクトを図37-12のように選択して、コンポーネント置換を定義する必要があります。

    図37-12 ビジネス・コンポーネントの置換の定義

    「ビジネス・コンポーネント: 置換」ダイアログ

37.10.2 置換時に行われる処理

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>

37.10.3 基のアプリケーションで置換コンポーネントを有効化する方法

元のアプリケーションが置換されたコンポーネントを使用するよう指定するには、Javaシステム・プロパティFactory-Substitution-Listを定義し、その値に、置換リストが格納されている*.jpxファイルに対するプロジェクトの名前を設定します。値には、*.jprまたは*.jpx拡張子を除いたプロジェクト名のみを設定します。

37.9.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ソース・コードを変更することなく行われました。