プライマリ・コンテンツに移動
Oracle® Fusion Middleware Oracle Application Development FrameworkによるFusion Webアプリケーションの開発
12c (12.2.1.1.0)
E77397-02
目次へ移動
目次

前
次

17 ビジネス・コンポーネント機能の拡張

この章では、すべてのタイプのADFビジネス・コンポーネントにカスタム・コードを組み込んだり、ADFビジネス・コンポーネント・フレームワークの動作を拡張したりする場合に使用できる手法について説明します。

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

17.1 ビジネス・コンポーネント機能の拡張について

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

ADFビジネス・コンポーネント・フレームワークのベース・クラスを拡張して、フレームワーク内のすべてのタイプのコンポーネントにカスタム・コードを組み込んだり、ADFビジネス・コンポーネント・フレームワークの動作を拡張できます。

カスタマイズを行わずに使用した場合、ビジネス・コンポーネントは、XMLドキュメントで完全に定義されており、コンポーネントのカスタムJavaコードやJavaクラス・ファイルをまったく必要とせず、十分な機能を備えています。ADFビジネス・コンポーネントのコンポーネントの組込み機能を拡張する必要がない場合、また、組込みイベントを処理するカスタム・コードを記述する必要がない場合、このXMLのみの方法でコンポーネントを使用できます。ただし、ADFビジネス・コンポーネント・フレームワークを拡張する場合も、JDeveloperでXMLドキュメントを操作できます。

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

17.1.1 ビジネス・コンポーネントの拡張の追加機能

ADFビジネス・コンポーネント・フレームワークを使用する前に、他のOracle ADF機能について理解しておくと役立つ場合があります。次に、関連する他の機能へのリンクを示します。

17.2 ADFビジネス・コンポーネント拡張クラスの作成

ADFビジネス・コンポーネント・フレームワーク拡張クラスは、次のことを目的としてユーザーが作成する、フレームワークの基本クラスの1つを拡張するJavaクラスです。

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

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

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

アプリケーション固有のビジネス・コンポーネントの開発を始める前に、フレームワーク拡張クラスの完全なレイヤーを作成し、そのレイヤーをデフォルトで使用するようプロジェクト・レベルで設定することを検討することをお薦めします。当初はこれらのフレームワーク拡張クラスにカスタム・コードを追加することを想定していないかもしれませんが、このような設定は実際にカスタマイズが必要になった場合に役に立ちます。

これにより、新しい汎用機能や、組込み機能の補強や、不具合の一般的な回避といったことが、プロジェクトの途中ですべてのエンティティ・オブジェクトで必要になった場合でも、大きな不都合が起きることを回避できます。

注意:

この章の例を試すには、「SummitADF_Examplesワークスペースからのスタンドアロン・サンプルの実行」の説明に従って、SummitADF_Examplesワークスペースを使用します。Summit ADFスタンドアロン・サンプル・アプリケーションを取得およびインストールする方法の詳細は、「Oracle ADF用Summitサンプル・アプリケーションの設定」を参照してください。

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

カスタム・コードを追加してADFビジネス・コンポーネント・フレームワークの基本機能を拡張する必要がある場合は、作成するビジネス・コンポーネントの任意の主要タイプに対し、カスタムJavaクラスを有効にできます。コンポーネントのカスタム・クラスの生成は、JDeveloperの対応する概要エディタの「Java」ページで有効にします。このオプションを有効にすると、JDeveloperにより、構成可能な命名の標準に準拠したコンポーネントに関連するカスタム・クラスのJavaソース・ファイルが作成されます。このクラスの名前は、コンポーネントのXMLドキュメントに記録され、コンポーネントで必要とされるカスタムJavaコードは、このクラス内に記述できます。

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

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

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

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

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

  3. 「アプリケーション」ウィンドウで、拡張クラスを作成するプロジェクトを右クリックし、「新規」「Javaクラス」の順に選択します。
  4. 「Javaクラスの作成」ダイアログで、「拡張」フィールドのoracle.jbo.serverパッケージから適切なフレームワーク・ベース・クラスを指定します。

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

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

    この図は周囲のテキストで説明しています

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

注意:

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

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

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

17.2.3 フレームワークの拡張ベース・クラスのカスタマイズに関する必知事項

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

ユーザーが選択したパッケージ名(com.yourcompany.fwkextなど。それぞれがoracle.jbo.server.*パッケージをインポート)のカスタマイズされたフレームワーク・ベース・クラスの共通セットは、次のクラスで構成されます。

  • public class CustomEntityImpl extends EntityImpl

  • public class CustomEntityDefImpl extends EntityDefImpl

  • public class CustomViewObjectImpl extends ViewObjectImpl

  • public class CustomViewRowImpl extends ViewRowImpl

  • public class CustomApplicationModuleImpl extends ApplicationModuleImpl

  • public class CustomDBTransactionImpl extends DBTransactionImpl2

  • public class CustomDatabaseTransactionFactoryImpl extends DatabaseTransactionFactory

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

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

  • public class CustomViewDefImpl extends ViewDefImpl

  • public class CustomEntityCache extends EntityCache

  • public class CustomApplicationModuleDefImpl extends ApplicationModuleDefImpl

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

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

始める前に:

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

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

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

フレームワーク・クラスを参照できることを確認した後、ビジネス・コンポーネントを作成できます。すべてのADFビジネス・コンポーネント・ウィザードおよびエディタの「Java」ページには同じ「クラスの拡張」ボタンが表示されるので、この方法を使用して、新規コンポーネントと既存コンポーネントの両方に対して目的のフレームワーク拡張ベース・クラスを選択できます。

作成するフレームワーク拡張クラスのレベルの数には固定の制限はありません。たとえば、会社で作成するすべてのFusion Webアプリケーションのすべてのアプリケーション・モジュールで使用する会社レベルのCustomAppModuleImplを作成した後、一部のプロジェクト・チームでそのフレームワーク拡張クラスをさらにカスタマイズすることが必要になったとします。そのようなチームでは、CustomAppModuleImplを拡張するSomeProjectCustomAppModuleImplクラスを作成し、プロジェクト固有のカスタム・アプリケーション・モジュールのコードをそこに含めることができます(次の例を参照)。

public class SomeProjectCustomAppModuleImpl
       extends CustomAppModuleImpl {
  /*
   * Custom application module code specific to the
   * "SomeProject" project goes here.
   */
}

その後、この特定のプロジェクトの実装の一部として作成されるアプリケーション・モジュールでは、CustomAppModuleImplのかわりにSomeProjectCustomAppModuleImplをベース・クラスとして使用できます。

フレームワーク拡張クラスに基づいてビジネス・コンポーネントを作成するには:

  1. 「アプリケーション」ウィンドウで、目的のコンポーネントをダブルクリックします。
  2. 概要エディタで、「Java」ナビゲーション・タブをクリックし、「Javaオプションの編集」ボタンをクリックします。
  3. 「Javaオプションの選択」ダイアログで、「クラスの拡張」をクリックします。
  4. 「ベース・クラスのオーバーライド」ダイアログで、オーバーライドするフレームワーク・ベース・クラスの完全修飾名を入力します。「参照」ボタンを使用して、JDeveloperクラス・ブラウザでクラスをすばやく検索することもできます。

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

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

    この図は周囲のテキストで説明しています

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

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

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

  1. 「アプリケーション」ウィンドウで、拡張クラスを含むモデル・プロジェクトを右クリックし、「プロジェクト・プロパティ」を選択します。
  2. 「プロジェクト・プロパティ」ダイアログで、「ADFビジネス・コンポーネント」→「ベース・クラス」の順に開き、「アプリケーション・モジュール・オブジェクト・クラス」の名前フィールドにベース・クラスの完全修飾名を入力します。

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

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

    この図は周囲のテキストで説明しています

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

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

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

  1. メイン・メニューで、「ツール」「プリファレンス」を選択します。
  2. 「プリファレンス」ダイアログのツリーで「ADFビジネス・コンポーネント」→「ベース・クラス」を開きます。
  3. 「ビジネス・コンポーネント」ページのcomponentNameオブジェクトのクラス名フィールドにそのクラスの完全修飾名を入力します。

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

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

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

17.2.7.1 XMLのみのコンポーネント

たとえば、CustomAppModuleImplをカスタム・アプリケーション・モジュール・ベース・クラスにして、com.yourcompany.yourappパッケージにYourServiceアプリケーション・モジュールを作成したものとします。コンポーネントをカスタムJavaファイルのないXMLのみのコンポーネントにした場合、そのXMLドキュメント(YourService.xml)は、次の例のようになります。実行時にAppModuleタグのComponentClass属性の値が読み取られて、コンポーネントを表すために使用するJavaクラスが識別されます。

<AppModule
   Name="YourService"
   ComponentClass="com.yourcompany.fwkext.CustomAppModuleImpl" >
  <!-- etc. -->
</AppModule>

図17-4は、XMLのみのYourServiceアプリケーション・モジュールとカスタム拡張クラスの関係を示したものです。実行時には、ApplicationModuleImplクラスから基本動作を継承するCustomAppModuleImplクラスを使用します。

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

この図は周囲のテキストで説明しています

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

コンポーネントでJavaクラスが必要な場合は、前の項で示したように、コンポーネント・エディタの「Java」ページを開き、適切なチェック・ボックスを選択して有効にします。たとえば、YourServerアプリケーション・モジュールに対してカスタム・アプリケーション・モジュール・クラスを有効にすると、適切なYourServiceImpl.javaクラスが作成されます。次の例に示すように、コンポーネントのXMLドキュメントも更新されて、カスタム・コンポーネント・クラスの名前が反映されます。

<AppModule
   Name="YourService"
   ComponentClass="com.yourcompany.yourapp.YourServiceImpl" >
  <!-- etc. -->
</AppModule>

また、次の例に示すように、コンポーネントのカスタムJavaクラスのextends句も変更されて、新しいカスタム・フレームワーク・ベース・クラスが反映されます。

package com.yourcompany.yourapp;
import com.yourcompany.fwkext.CustomAppModuleImpl;
// ---------------------------------------------------------------------
// ---    File generated by Oracle ADF Business Components Design Time.
// ---    Custom code may be added to this class.
// ---    Warning: Do not modify method signatures of generated methods.
// ---------------------------------------------------------------------
public class YourServiceImpl extends CustomAppModuleImpl {
  /**This is the default constructor (do not remove)   */
  public YourServiceImpl() {}
  // etc.
}

図17-5は、カスタムYourServiceImplクラスを含むYourServiceアプリケーション・モジュールとフレームワーク拡張クラスの関係を示したものです。実行時には、ベースApplicationModuleImplクラスを拡張するCustomAppModuleImplフレームワーク拡張クラスから基本動作を継承するYourServiceImplクラスを使用します。

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

この図は周囲のテキストで説明しています

17.2.8 カスタム・コンポーネントのJavaファイルのExtends句の更新に関する必知事項

カスタムJavaクラスを含むビジネス・コンポーネントがあり、後でコンポーネントをADFビジネス・コンポーネント・フレームワーク拡張クラスに基づくように変更する場合は、「Javaオプションの選択」ダイアログの「クラスの拡張」ボタンを使用して、コンポーネントのベース・クラスを変更してください。このダイアログは、コンポーネントの概要エディタの「Java」ページから開けます。これを行うと、コンポーネントのXMLドキュメントが新しいベース・クラスを反映して更新され、さらにコンポーネントのカスタムJavaクラスのextends句が変更されます。

注意:

コンポーネント・エディタを使用せずに手動でextends句を更新すると、コンポーネントのXMLドキュメントに新しい継承が反映されず、次にエディタを開いたときに、手動で変更したextends句は、コンポーネント・エディタが正しいコンポーネント・ベース・クラスであるとする値で上書きされます。

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

フレームワーク拡張レイヤーのクラスを含むJARファイルを作成するには、「デプロイメント・プロファイルの作成 -- JARファイル」ダイアログを使用します。これは、「新規ギャラリ」「一般」→「デプロイメント・プロファイル」カテゴリで使用できます。

デプロイメント・プロファイルにFrameworkExtensionsのような名前を設定し、「OK」をクリックします。デフォルトでは、JARファイルにはプロジェクト内のすべてのクラス・ファイルが格納されます。これが求める結果であるため、「JARデプロイメント・プロファイルのプロパティ」ダイアログが表示されたら、「OK」をクリックして終了してかまいません。

注意:

ADFライブラリJARアーカイブ・タイプを使用してフレームワーク拡張機能レイヤーをパッケージ化しないでください。再使用可能な構成要素をパッケージ化してJDeveloperリソース・カタログで共有する場合は、ADFライブラリJARファイルを作成します。ビジネス・コンポーネントおよびADFライブラリJARアーカイブ・タイプでの作業の詳細は、「ADFライブラリへの再利用可能なADFコンポーネントのパッケージ化」を参照してください。

最後に、JARファイルを作成するには、「アプリケーション」ウィンドウでプロジェクト・ノードを右クリックし、ポップアップ・メニューから「デプロイ」YourProfileName「JARファイルへ」を選択します。JDeveloperの「ログ・ウィンドウ」「デプロイ」タブに、次のようなフィードバックが表示されます。

----  Deployment started.  ----    Feb 14, 2013 1:42:39 PM
Running dependency analysis...
Wrote JAR file to ...\FrameworkExtensions\deploy\FrameworkExtensions.jar
Elapsed time for deployment:  2 seconds
----  Deployment finished.  ----    Reb 14, 2013 1:42:41 PM

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

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

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

  1. メイン・メニューで、「ツール」「ライブラリの管理」を選択します。
  2. 「ライブラリの管理」ダイアログの「ライブラリ」タブをクリックし、「ユーザー」を選択して「新規」をクリックします。
  3. 表示される「ライブラリの作成」ダイアログで、ライブラリにFramework Extension Layerという名前を設定し、「クラス・パス」ノードを選択して、「エントリの追加」をクリックします。
  4. 「パス・エントリの選択」ダイアログで、フレームワーク拡張コンポーネントのクラス・ファイルを含むJARファイルを選択し、「選択」をクリックします。
  5. 「ソース・パス」ノードを選択し、「エントリの追加」をクリックします。
  6. 表示される「パス・エントリの選択」ダイアログで、フレームワーク拡張クラスのソース・ファイルが存在するディレクトリを選択し、「選択」をクリックします。

    たとえば、JARファイルFrameworkExtensions.jarに対して..\FrameworkExtensions\srcを選択します。

  7. 「OK」をクリックします。

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

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

この図は周囲のテキストで説明しています

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

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

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

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

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

この図は周囲のテキストで説明しています

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

「ビュー・オブジェクトのデフォルト行セットを使用した操作のViewObjectインタフェース・メソッド」で説明したように、読取り専用のビューの場合、findByKey()メソッドおよびsetCurrentRowWithKey組込み操作は、setManageRowsByKey(true)をコールするようにビュー・オブジェクトのcreate()メソッドをオーバーライドした場合にのみ動作します。多くの読取り専用ビュー・オブジェクトを作成する場合、このような詳細を記憶しておくのは大変であるため、ビュー・オブジェクトのフレームワーク拡張クラスで自動化する機能の有力な候補となります。

FrameworkExtensionsプロジェクトには、アプリケーション内の全ビュー・オブジェクトに対するベース・クラスであるSummitViewObjectImplクラスが含まれるものとします。ビュー・オブジェクトに対するこのフレームワーク拡張クラスは、次の例に示すように、ViewObjectImplベース・クラスを拡張し、create()メソッドをオーバーライドして、このタスクを自動化します。実行時にビュー・オブジェクト・インスタンスを作成するとき、super.create()を呼び出してデフォルトのフレームワーク機能を実行した後、このコードは、ビュー・オブジェクトが少なくとも1つの属性がキー属性としてマークされている読取り専用のビュー・オブジェクトかどうかを検査します。そうである場合は、setManageRowsByKey(true)を呼び出します。

isReadOnlyNonEntitySQLViewWithAtLeastOneKeyAttribute()ヘルパー・メソッドは、次の条件の組合せを検査することで、ビュー・オブジェクトが読取り専用かどうかを判定します。

  • isFullSql()true

    このメソッドは、ビュー・オブジェクトの問合せが、含まれるエンティティ・オブジェクトの慣用名に基づいて選択リストを自動的に生成するのではなく、開発者によって完全に指定されている場合は、trueを返します。

  • getEntityDefs()null

    このメソッドは、ビュー・オブジェクトのエンティティ・オブジェクト慣用名を表すEntityDefImplオブジェクトの配列を返します。nullを返す場合は、ビュー・オブジェクトにはエンティティ・オブジェクトの慣用名がありません。

次に、getAttributeDefs()メソッドが返すAttributeDef配列をループして、ビュー・オブジェクトにキー属性があるかどうかを判定します。リストのいずれかの属性定義に対してisPrimaryKey()メソッドがtrueを返す場合、そのビュー・オブジェクトにはキーがあります。

public class SummitViewObjectImpl extends ViewObjectImpl {
  protected void create() {
    super.create();
    if (isReadOnlyNonEntitySQLViewWithAtLeastOneKeyAttribute()) {
      setManageRowsByKey(true);
    }
  }
  boolean isReadOnlyNonEntitySQLViewWithAtLeastOneKeyAttribute() {
    if (getViewDef().isFullSql() && getEntityDefs() == null) {
      for (AttributeDef attrDef : getAttributeDefs()) {
        if (attrDef.isPrimaryKey()) {
          return true;
        }
      }
    }
    return false;
  }
  // etc.
}

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

アプリケーション・モジュール、ビュー・オブジェクトおよびエンティティ・オブジェクトを作成するときに、これらのビジネス・コンポーネントの概要エディタの「一般」ナビゲーション・タブを選択し、「カスタム・プロパティ」セクションを開くと、任意のコンポーネントのカスタム・メタデータ・プロパティを定義できます。カスタム・メタデータ・プロパティは名前と値のペアであり、これを使用することで、コンポーネントに関する追加の宣言情報を、フレームワーク拡張クラスで作成する汎用コードに伝えることができます。コードでは、getProperty()メソッドを使用することで、特定のカスタム・メタデータ・プロパティの存在または値に基づいて、条件付きで汎用機能を実行できます。

たとえば、SummitViewObjectImplフレームワーク拡張クラスでは、ビュー・オブジェクトのinsertRow()メソッド(次の例を参照)をオーバーライドし、条件付きで、行セットの最後の行として表示される行を挿入しています。このフレームワーク拡張クラスを拡張するビュー・オブジェクトでInsertNewRowsAtEndという名前のカスタム・メタデータ・プロパティが定義されている場合、この汎用コードは末尾への新しい行の挿入を実行します。ビュー・オブジェクトでこのプロパティが定義されていない場合は、insertRow()のデフォルトの動作が実行されます。

public class SummitViewObjectImpl extends ViewObjectImpl {
  private static final String INSERT_NEW_ROWS_AT_END = "InsertNewRowsAtEnd";
  public void insertRow(Row row) {
    super.insertRow(row);
    if (getProperty(INSERT_NEW_ROWS_AT_END) != null) {
      row.removeAndRetain();
      last();
      next();
      getDefaultRowSet().insertRow(row);    
    }
  }
  // etc.
}

コンポーネント・レベルでカスタム・プロパティを定義するだけでなく、ビュー・オブジェクト属性、エンティティ・オブジェクト属性、およびドメインでも、プロパティを定義できます。実行時にこれらのプロパティにアクセスするには、特定の属性に対するAttributeDefインタフェースのgetProperty()メソッドを使用します。

17.3.4 属性の種類に関する必知事項

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


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

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

ATTR_PERSISTENT

永続的属性

ATTR_TRANSIENT

一時的属性

ATTR_ENTITY_DERIVED

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

ATTR_SQL_DERIVED

SQL計算属性

ATTR_DYNAMIC

動的属性

ATTR_ASSOCIATED_ROWITERATOR

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

ATTR_ASSOCIATED_ROW

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


17.3.5 カスタム・プロパティに関する必知事項

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

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

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

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 ADF Business Components Design Time.
// ---------------------------------------------------------------------
public interface ProductModule
       extends CustomApplicationModule, ApplicationModule {
  void doSomethingProductRelated();
}

これが済むと、クライアント・コードは、ProductModuleアプリケーション・モジュールをCustomApplicationModuleインタフェースにキャストし、それに含まれる汎用拡張メソッドを強く型付けされた方法で呼び出すことができます。

注意:

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

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

ビジネス・コンポーネント用のカスタムJavaクラスにコードを記述し、データベースのストアド・プロシージャとストアド・ファンクションを呼び出すことができます。ここでは、PL/SQLパッケージのプロシージャとファンクションに基づく簡単な例をいくつか示しますが、同じ手法を使用して、パッケージの一部ではないプロシージャやファンクションを呼び出すこともできます。

次の例のPL/SQLパッケージについて考えてみます。

create or replace package invokestoredprocpkg as
  procedure proc_with_no_args;
  procedure proc_with_three_args(n number, d date, v varchar2);
  function  func_with_three_args(n number, d date, v varchar2) return varchar2;
  procedure proc_with_out_args(n number, d out date, v in out varchar2);
end invokestoredprocpkg;

以降の項では、このパッケージのプロシージャおよびファンクションの各例を呼び出す方法について説明します。

注意:

この項の例では、SummitADF_Examplesアプリケーション・ワークスペースのoracle.summit.model.invokingstoredprocedureパッケージを参照します。

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

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

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

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

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

  1. 渡される文のためのJDBC PreparedStatementを作成し、PL/SQLのbegin...endブロック内にラップします。
  2. バインド変数が渡された場合は、値のループ処理を行います。
  3. 各バインド変数の値を文に設定します。

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

  4. 文を実行します。
  5. 文を閉じます。
protected void callStoredProcedure(String stmt, Object[] bindVars) {
  PreparedStatement st = null;
  try {
    // 1. Create a JDBC PreparedStatement for 
    st = getDBTransaction().createPreparedStatement("begin "+stmt+";end;",0);
    if (bindVars != null) {
      // 2. Loop over values for the bind variables passed in, if any
      for (int z = 0; z < bindVars.length; z++) {
        // 3. Set the value of each bind variable in the statement
        st.setObject(z + 1, bindVars[z]);
      }
    }
    // 4. Execute the statement
    st.executeUpdate();
  }
  catch (SQLException e) {
    throw new JboException(e);
  }
  finally {
    if (st != null) {
      try {
        // 5. Close the statement
        st.close();
      }
      catch (SQLException e) {}
    }
  }
}

このようなヘルパー・メソッドを使用した場合、前の例のproc_with_three_argsプロシージャのコールは次のようになります。

// In StoredProcTestModuleImpl.java
public void callProcWithThreeArgs(Number n, Date d, String v) {
  callStoredProcedure("callStoredProcedure.proc_with_three_args(?,?,?)",
                      new Object[]{n,d,v});
}

ファンクションに渡される引数のJDBCバインド変数のプレースホルダとして疑問符が使用されていることに注意してください。JDBCは名前付きのバインド変数の使用もサポートしますが、ヘルパー・メソッドは位置でバインド変数の値を設定するのみであるため、このように単純な位置によるバインド変数を使用しても問題ありません。

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

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

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

  1. 渡される文のためのJDBC CallableStatementを作成し、PL/SQLのbegin...endブロック内にラップします。
  2. 最初のバインド変数をファンクションの戻り値に登録します。
  3. バインド変数が渡された場合は、値のループ処理を行います。
  4. ユーザーが提供した各バインド変数の値を文に設定します。

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

  5. 文を実行します。
  6. 最初のバインド変数の値を返します。
  7. 文を閉じます。
// Some constants
public static int NUMBER = Types.NUMERIC;
public static int DATE = Types.DATE;
public static int VARCHAR2 = Types.VARCHAR;

protected Object callStoredFunction(int sqlReturnType, String stmt,
                                    Object[] bindVars) {
  CallableStatement st = null;
  try {
    // 1. Create a JDBC CallabledStatement  
    st = getDBTransaction().createCallableStatement(
           "begin ? := "+stmt+";end;",0);
    // 2. Register the first bind variable for the return value
    st.registerOutParameter(1, sqlReturnType);
    if (bindVars != null) {
      // 3. Loop over values for the bind variables passed in, if any
      for (int z = 0; z < bindVars.length; z++) {
        // 4. Set the value of user-supplied bind vars in the stmt
        st.setObject(z + 2, bindVars[z]);
      }
    }
    // 5. Set the value of user-supplied bind vars in the stmt
    st.executeUpdate();
    // 6. Return the value of the first bind variable
    return st.getObject(1);
  }
  catch (SQLException e) {
    throw new JboException(e);
  }
  finally {
    if (st != null) {
      try {
        // 7. Close the statement
        st.close();
      }
      catch (SQLException e) {}
    }
  }
}

このようなヘルパー・メソッドを使用した場合、前の例のfunc_with_three_argsプロシージャのコールは次のようになります。

// In InvokingStoredProcAppModuleImpl.java
public String callFuncWithThreeArgs(Number n, Date d, String v) {
  return (String)callStoredFunction(VARCHAR2,
                            "invokestoredprocpkg.func_with_three_args(?,?,?)",
                            new Object[]{n,d,v});
}

ファンクションに渡される引数のJDBCバインド変数のプレースホルダとして疑問符が使用されていることに注意してください。JDBCは名前付きのバインド変数の使用もサポートしますが、ヘルパー・メソッドは位置でバインド変数の値を設定するのみであるため、このように単純な位置によるバインド変数を使用しても問題ありません。

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

OUTモードまたはIN OUTモードの引数を含むinvokestoredprocpkg.proc_with_out_argsのようなストアド・プロシージャまたはストアド・ファンクションの呼出しでは、前の項の場合と同じようにCallableStatementを使用する必要がありますが、ヘルパー・メソッドへの一般化は少し難しくなります。次の例は、invokestoredprocpkg.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を閉じます。
public DateAndStringBean callProcWithOutArgs(Number n, String v) {
  CallableStatement st = null;
  try  {
    // 1. Define the PL/SQL block for the statement to invoke
    String stmt = "begin invokestoredprocpkg.proc_with_out_args(?,?,?); end;";
    // 2. Create the CallableStatement for the PL/SQL block
    st = getDBTransaction().createCallableStatement(stmt,0);
    // 3. Register the positions and types of the OUT parameters
    st.registerOutParameter(2,Types.DATE);
    st.registerOutParameter(3,Types.VARCHAR);
    // 4. Set the bind values of the IN parameters
    st.setObject(1,n);
    st.setObject(3,v);
    // 5. Execute the statement
    st.executeUpdate();
    // 6. Create a bean to hold the multiple return values
    DateAndStringBean result = new DateAndStringBean();
    // 7. Set value of dateValue property using first OUT param
    result.setDateVal(new Date(st.getDate(2)));
    // 8. Set value of stringValue property using 2nd OUT param
    result.setStringVal(st.getString(3));
    // 9. Return the result
    return result;
  } catch (SQLException e)  {
    throw new JboException(e);
  } finally  {
    if (st != null) {
      try {
        // 10. Close the JDBC CallableStatement
        st.close();
      }
      catch (SQLException e) {}
    }    
  }
}   

前の例で使用されているDateAndString Beanは、次のような2つのBeanプロパティを持つ簡単なJavaBeanです。

package oracle.summit.model.invokingstoredprocedure;

import java.io.Serializable;
import oracle.jbo.domain.Date;

public class DateAndStringBean implements Serializable {
  Date dateVal;
  String stringVal;
  public void setDateVal(Date dateVal) {this.dateVal=dateVal;}
  public Date getDateVal() {return dateVal;}
  public void setStringVal(String stringVal) {this.stringVal=stringVal;}
  public String getStringVal() {return stringVal;}
}

注意:

カスタム・メソッドを、アプリケーション・モジュールのカスタム・サービス・インタフェースに組み込む正当な候補にできるようにするには(望ましい場合)、Beanがjava.io.Serializableインタフェースを実装する必要があります。これはマーカー・インタフェースであるため、implements Serializableキーワードを追加するのみでよく、インタフェースのメソッドの実装をコーディングする必要はありません。

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

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

ただし、サード・パーティのコードをADFビジネス・コンポーネントと統合する場合はこれが可能であると便利なため、次の例に示すようなヘルパー・メソッドを使用して接続にアクセスできます。

/**
 * Put this method in your XXXImpl.java class where you need
 * to access the current JDBC connection
 */
private Connection getCurrentConnection() throws SQLException {
 /* Note that we never execute this statement, so no commit really happens */
 PreparedStatement st = getDBTransaction().createPreparedStatement("commit",1);
 Connection conn = st.getConnection();
 st.close();
 return conn;
}

注意:

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

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

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

注意:

この項の例では、SummitADF_Examplesアプリケーション・ワークスペースのoracle.summit.model.custommessagesパッケージを参照します。

17.7.1 基になっているADFビジネス・コンポーネントのエラー・メッセージをカスタマイズする方法

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

JBO-27014: Attribute OrderFilled in SOrd is required

Oracle Worldwide SupportからOracle Application Development Framework (Oracle ADF)のソース・コードを入手している場合は、oracle.jboパッケージのCSMessageBundle.javaファイルを見ると、このエラー・メッセージが次の行の組合せと関連していることがわかります。

public class CSMessageBundle extends CheckedListResourceBundle {
  // etc.
  public static final String EXC_VAL_ATTR_MANDATORY = "27014";
  // etc. 
  private static final Object[][] sMessageStrings = {
    // etc.
    {EXC_VAL_ATTR_MANDATORY, "Attribute {2} in {1} is required"},
    // etc.
  }
}

番号付きのトークン{2}および{1}は、エラー・メッセージのプレースホルダです。この例では、実行時に、{l}はエンティティ・オブジェクトの名前に置き換えられ、{2}は属性の名前に置き換えられます。

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

  1. 「アプリケーション」ウィンドウで、メッセージ・バンドル・ファイルを追加するプロジェクトを右クリックし、「プロジェクト・プロパティ」を選択します。
  2. 「プロジェクト・プロパティ」ダイアログで「ビジネス・コンポーネント」→「オプション」を選択し、「新規」をクリックします。
  3. 「MessageBundleクラスの作成」ダイアログで、カスタム・メッセージ・バンドルの名前とパッケージを入力し、「OK」をクリックします。

    注意:

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

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

    この図は周囲のテキストで説明しています
  4. 「プロジェクト・プロパティ」ダイアログで、「OK」をクリックして「プロジェクト・プロパティ」ダイアログを閉じ、新しいカスタム・メッセージ・バンドル・クラスをソース・エディタで開きます。
  5. カスタム・メッセージ・バンドル・クラスの2次元のString配列を編集し、使用するカスタマイズしたメッセージを入力します。

    次のサンプルは、名前付き属性の値を指定する必要があることをユーザーに知らせるJBO-27014エラーのエラー・メッセージ文字列をオーバーライドするカスタム・メッセージ・バンドル・クラスを示しています。2つ目のカスタム・エラー・メッセージは、ORD_ORDER_FILLED_CKという名前のデータベース制約に違反しているときに表示され、期待されるOrder Filled値をユーザーに通知します。制約のエラー・メッセージのカスタマイズの詳細は、「データベース制約違反のエラー・メッセージのカスタマイズ方法」を参照してください。

    package oracle.summit.model.custommessages;
    
    import java.util.ListResourceBundle;
    
    public class CustomMessageBundle extends ListResourceBundle {
      private static final Object[][] sMessageStrings 
        = new String[][] {
           {"27014","You must provide a value for {2}"},
           {"S_ORD_ORDER_FILLED_CK", "The order filled value must be Y or N"}
         };
    
    
      /* Return String Identifiers and corresponding Messages in a 
       * two-dimensional array.
       */
      protected Object[][] getContents() {
        return sMessageStrings;
      }
    }

17.7.2 基になっているADFビジネス・コンポーネントのエラー・メッセージをカスタマイズするときに行われる処理

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

JBO-27014: You must provide a value for Order Filled

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

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

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

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

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

始める前に:

アプリケーション・モジュールの知識があると役立ちます。詳細は、「ビジネス・コンポーネントのエラー・メッセージのカスタマイズ」を参照してください。

他のOracle ADF機能を使用して追加できる機能を理解しておくことも役立ちます。詳細は、「ビジネス・コンポーネントの拡張の追加機能」を参照してください。

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

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

  1. ADFモデル・データ・バインディング・レイヤーで提供されるデフォルト・エラー・ハンドラDCErrorHandlerImplインタフェースを拡張する、エラー・ハンドラ・クラスを作成します。
  2. エラー・ハンドラ・クラスで、DCErrorHandlerImpl::skipException(Exception ex)メソッドのデフォルト・エラー・ハンドラ動作を次の例に示すようにオーバーライドします。

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

  3. その後、「エラー処理のカスタマイズ」の説明に従って、プロジェクトのDataBindings.cpxファイルにカスタム・エラー・ハンドラを登録できます。

次の例は、ユーザーに表示されるエラー最終リストへのSQLIntegrityConstraintViolationExceptionの表示をスキップするエラー・ハンドラのカスタム実装を示しています。一意の制約違反や外部キー制約違反などのエラーで生成される、他のデータベース・レベルのエラー・メッセージをスキップすることもできます。

package view;

import java.sql.SQLIntegrityConstraintViolationException;

import oracle.adf.model.BindingContext;
import oracle.adf.model.RegionBinding;
import oracle.adf.model.binding.DCBindingContainer;
import oracle.adf.model.binding.DCErrorHandlerImpl;

import oracle.adf.model.binding.DCErrorMessage;

import oracle.jbo.DMLConstraintException;
import oracle.jbo.JboException;

public class CustomErrorHandler extends DCErrorHandlerImpl {

    public CustomErrorHandler() {
        super();
    }


    /**
     * If an exception is a RowValException or a TxnValException
     * and they have nested exceptions, then do not display
     * it.
     */
    @Override
    protected boolean skipException(Exception ex) {

        if (ex instanceof DMLConstraintException) {
            return false;
        } else if (ex instanceof SQLIntegrityConstraintViolationException) {
            return true;
        }
        return super.skipException(ex);
    }
)

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

データベースに制約を設定している場合、制約の違反が発生したときに、Fusion Webアプリケーションでカスタム・エラー・メッセージをエンド・ユーザーに対して提供する場合があります。たとえば、次の例に示す次のDDL文を使用して、アプリケーションのS_ORD表にS_ORD_ORDER_FILLED_CKという制約を追加するものとします。

alter table s_ord add (
  constraint S_ORD_ORDER_FILLED_CK
      check (ORDER_FILLED IN ('Y', 'N'))
);

アプリケーションでカスタム・エラー・メッセージを定義するには、制約名をメッセージ・キーとして、カスタム・メッセージ・バンドルにメッセージを追加します。次の例は、前の例で定義したデータベース制約名と一致するキーS_ORD_ORDER_FILLED_CKを持つメッセージを定義した場合のCustomMessageBundle.javaクラスを示しています。

package oracle.summit.model.custommessages;

import java.util.ListResourceBundle;

public class CustomMessageBundle extends ListResourceBundle {
  private static final Object[][] sMessageStrings 
    = new String[][] {
       {"27014","You must provide a value for {2}"},
       {"S_ORD_ORDER_FILLED_CK", "The order filled value must be Y or N"}
     };

  protected Object[][] getContents() {
    return sMessageStrings;
  }
}

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

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

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

ADFビジネス・コンポーネント・トランザクション用のカスタム・フレームワーク拡張クラスを記述するには、次の例で示すCustomDBTransactionImplのようなクラスを作成します。この例では、スローされるDMLConstraintExceptionエラーに対するカスタム処理を実行するため、トランザクション・オブジェクトのpostChanges()メソッドをオーバーライドして、super.postChanges()の呼び出しをtry/catchブロックでラップしています。この簡単な例で実行しているカスタム処理は、ex.setExceptions(null)を呼び出して、DMLConstraintExceptionが保持している可能性のあるネストされた詳細な例外をクリアすることのみです。かわりに、アプリケーションで必要な他の種類のカスタム例外処理を実行できます。たとえば、カスタム例外がJboExceptionを直接または間接に拡張している場合は、カスタム例外をスローできます。

package oracle.summit.model.custommessages;

import oracle.jbo.DMLConstraintException;
import oracle.jbo.server.DBTransactionImpl2;
import oracle.jbo.server.TransactionEvent;

public class CustomDBTransactionImpl  extends DBTransactionImpl2 {
  public void postChanges(TransactionEvent te) {
    try {
      super.postChanges(te);
    }
    /*
     * Catch the DML constraint exception
     * and perform custom error handling here
     */
    catch (DMLConstraintException ex) { 
      ex.setExceptions(null);
      throw ex;
    }
  }
}

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

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

  1. カスタマイズしたトランザクション・クラスのインスタンスを返すようにcreate()メソッドをオーバーライドするDatabaseTransactionFactoryクラスのカスタム実装を提供します。
  2. TransactionFactoryプロパティの値に、このカスタム・トランザクション・ファクトリ・クラスの完全修飾名を構成します。

次の例は、これを行うカスタム・データベース・トランザクション・ファクトリ・クラスを示しています。フレームワークがデータベース・トランザクション・ファクトリでcreate()メソッドを呼び出すと、このクラスはCustomDBTransactionImplクラスの新しいインスタンスを返します。

package oracle.summit.model.custommessages;

import oracle.jbo.server.DBTransactionImpl2;
import oracle.jbo.server.DatabaseTransactionFactory;

public class CustomDatabaseTransactionFactory
                     extends DatabaseTransactionFactory {
  public CustomDatabaseTransactionFactory() {
  }
  /**
   * Return an instance of our custom CustomDBTransactionImpl class
   * instead of the default implementation.
   *
   * @return instance of custom CustomDBTransactionImpl implementation.
   */
  public DBTransactionImpl2 create() {
    return new CustomDBTransactionImpl();
  }    
}

これを実行するには、アプリケーション・モジュール構成(bc4j.xcfgファイル)の概要エディタの「プロパティ」タブを使用して、TransactionFactoryプロパティに値oracle.summit.model.custommessages.CustomDatabaseTransactionFactoryを割り当てます。アプリケーション・モジュール構成の概要エディタを表示するには、「アプリケーション」ウィンドウでアプリケーション・モジュールをダブルクリックして、概要エディタの「構成」ナビゲーション・タブを選択します。次に、概要エディタの「構成」ページで構成を選択し、構成のハイパーリンクをクリックします。アプリケーション・モジュール構成の概要エディタで「プロパティ」タブを選択し、「プロパティの追加」をクリックして「プロパティの追加」ダイアログから次のプロパティを選択し、「OK」をクリックします。

  • TransactionFactory

次に、「プロパティ」リストで、TransactionFactoryプロパティの値を入力します。

  • TransactionFactory = oracle.summit.model.custommessages.CustomDatabaseTransactionFactory

この構成を使用してアプリケーションを実行すると、カスタム・トランザクション・クラスが使用されます。

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

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

図17-9 別のコンポーネントを拡張できるADFビジネス・コンポーネント

この図は周囲のテキストで説明しています

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

注意:

この項の例では、SummitADF_Examplesアプリケーション・ワークスペースのoracle.summit.model.extendパッケージを参照します。

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

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

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

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

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

  1. 「アプリケーション」ウィンドウで、コンポーネントをダブルクリックします。

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

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

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

17.8.3 別のコンポーネントを拡張するコンポーネントの作成時の処理

ADFビジネス・コンポーネント・データ・モデル・プロジェクトで作成するビジネス・コンポーネントは、XMLドキュメントとオプションのJavaクラスで構成されます。別のコンポーネントを拡張するコンポーネントを作成すると、JDeveloperは、拡張されたコンポーネントのXMLドキュメントと生成されるJavaコードの両方に、このコンポーネント継承を反映します。

17.8.3.1 親コンポーネントから継承された拡張コンポーネントの属性

拡張コンポーネントを作成すると、拡張コンポーネントは親コンポーネントから属性および属性プロパティを継承します。継承される属性は拡張コンポーネントに定義されておらず、親コンポーネントから渡されるだけであるため、この接続が維持されます。したがって、拡張コンポーネントの作成後に親コンポーネントの属性を変更した場合、拡張コンポーネントの属性はその変更を反映します。

たとえば、ProductViewビュー・オブジェクトを拡張するProductViewEx拡張ビュー・オブジェクトを作成するとします。次に、ProductView親ビュー・オブジェクトでShortDesc属性の「表示」UIヒントを「非表示」に設定することに決めます。この操作を行うと、ShortDesc属性がProductViewEx拡張ビュー・オブジェクトでも非表示になります。

ただし、拡張オブジェクトで属性をオーバーライドした場合、オーバーライドされた属性は拡張オブジェクトに再定義されるため、この接続は切断されます。これにより、親オブジェクトまたはその親オブジェクトから導出された他のオブジェクトに影響を与えずに、拡張オブジェクトに変更を加えることができます。オーバーライドは選択した属性にのみ適用され、他の継承された属性は影響を受けないことに注意してください。

たとえば、ProductViewビュー・オブジェクトと2つの拡張ビュー・オブジェクトProductViewEx1およびProductViewEx2があるとします。ProductView親ビュー・オブジェクトでShortDesc属性の「表示」UIヒントを「非表示」に設定した後、ProductViewEx1拡張ビュー・オブジェクトでこれを「表示」に設定することに決めます。ProductViewEx1ShortDescをオーバーライドし、「表示」UIヒントを「表示」に設定すると、変更はProductViewEx1拡張ビュー・オブジェクトにのみ反映され、ProductViewEx2拡張ビュー・オブジェクトまたはProductView親ビュー・オブジェクトには反映されません。

17.8.3.2 拡張コンポーネントのXMLディスクリプタに追加される属性

JDeveloperは、新しいコンポーネントのXMLドキュメントのルート・コンポーネント要素にExtends属性を追加することで、親コンポーネントの名前を記録します。追加された新しい宣言機能、または親コンポーネントの定義のオーバーライドされた部分は、拡張コンポーネントのXMLドキュメントに記述されます。これに対し、親コンポーネントから純粋に継承されたメタデータは、拡張コンポーネントでは繰り返し記述されません。

次の例は、ProductViewExビュー・オブジェクトのProductViewEx.xml XMLドキュメントを示しています。ViewObject要素のExtends属性、拡張されたビュー・オブジェクトで追加されたバインド変数に関連するVariable要素、およびtheProductNameバインド変数を参照するように変更されたWHERE句のWhere属性のオーバーライドされた値に注意してください。

<ViewObject
  xmlns="http://xmlns.oracle.com/bc4j"
  Name="ProductViewEx"
  InheritPersonalization="true"
  BindingStyle="OracleName"
  CustomQuery="false"
  ComponentClass="oracle.summit.model.extend.ProductViewExImpl"
  RowClass="oracle.summit.model.extend.ProductViewExRowImpl"
  RowInterface="oracle.summit.model.extend.common.ProductViewExRow"
  ClientRowProxyName="oracle.summit.model.extend.client.ProductViewExRowClient"
  ComponentInterface="oracle.summit.model.extend.common.ProductViewEx"
  ClientProxyName="oracle.summit.model.extend.client.ProductViewExClient"
  Extends="oracle.summit.model.extend.ProductView"
  Where="UPPER(PRODUCT_NAME) LIKE UPPER(:theProductName)||'%'"
...
  <Variable
     Name="bv_ProductName"
     Kind="where"
     Type="java.lang.String"/>
...
</ViewObject>

17.8.3.3 拡張コンポーネントに生成されたJavaクラス

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

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

17.8.4 コンポーネントの拡張に関する必知事項

17.8.4.1 拡張コンポーネントの親のクラスとインタフェース

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

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

  • クラスProductViewImpl

  • 行クラスProductViewRowImpl

  • クライアント・インタフェースProductView

  • 行クライアント・インタフェースProductViewRow

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

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

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

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

  3. キャストしてProductViewExRowインタフェースに固有のメソッドを呼び出す前に、本当に行ProductViewRowがより限定的なProductViewExRowのインスタンスであるかどうかをテストできます。

package oracle.summit.model.extend;

import oracle.jbo.ApplicationModule;
import oracle.jbo.AttributeDef;
import oracle.jbo.Row;
import oracle.jbo.StructureDef;
import oracle.jbo.ValidationException;
import oracle.jbo.ViewObject;
import oracle.jbo.client.Configuration;

import oracle.summit.model.extend.common.ProductView;
import oracle.summit.model.extend.common.ProductViewEx;
import oracle.summit.model.extend.common.ProductViewExRow;
import oracle.summit.model.extend.common.ProductViewRow;


public class TestClient {  
    public static void main(String[] args) {
    String        amDef = "oracle.summit.model.extend.AppModule";
    String        config = "AppModuleLocal";
    ApplicationModule am =
                         Configuration.createRootApplicationModule(amDef,config);
    ProductView products = (ProductView)am.findViewObject("ProductView1");
    products.executeQuery();
    ProductViewRow product = (ProductViewRow)products.first();
    printAllAttributes(products,product);
    testSomethingOnProductsRow(product);
    products = (ProductView)am.findViewObject("ProductViewEx1");
    ProductViewEx productsByName = (ProductViewEx)products;
    productsByName.setbv_ProductName("bunny");
    productsByName.executeQuery();
    product = (ProductViewRow)productsByName.first();
    printAllAttributes(productsByName,product);
    testSomethingOnProductsRow(product);
    am.getTransaction().rollback();
    Configuration.releaseRootApplicationModule(am,true);
  }
  private static void testSomethingOnProductsRow(ProductViewRow product) {
    try {
      if (product instanceof ProductViewExRow) {
        ProductViewExRow productByName = (ProductViewExRow)product;
        productByName.someExtraFeature("Test");        
      }
      product.setName("Q");
      System.out.println("Setting the Name attribute to 'Q' succeeded.");
    }
    catch (ValidationException v) {
      System.out.println(v.getLocalizedMessage());      
    }    
  }
  private static void printAllAttributes(ViewObject vo, Row r) {
    String viewObjName = vo.getName();
    System.out.println("Printing attribute for a row in VO '"+
                       viewObjName+"'");
    StructureDef def = r.getStructureDef();
    StringBuilder sb = new StringBuilder();
    int numAttrs = def.getAttributeCount();
    AttributeDef[] attrDefs = def.getAttributeDefs();
    for (int z = 0; z < numAttrs; z++) {
      Object value = r.getAttribute(z);
      sb.append(z > 0 ? "  " : "")
        .append(attrDefs[z].getName())
        .append("=")
        .append(value == null ? "<null>" : value)
        .append(z < numAttrs - 1 ? "\n" : "");
    }
    System.out.println(sb.toString());
  }
}

テスト・クライアントを実行すると、次のような結果が生成されます。

Printing attribute for a row in VO 'ProductView1'
Id=10011
  Name=Bunny Boot
  ShortDesc=Beginners ski boot
  LongtextId=518
  ImageId=1001
  SuggestedWhlslPrice=150
  WhlslUnits=<null>
  SomeValue=I am from the Product Impl Class
Setting the Name attribute to 'Q' succeeded.

注意:

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

17.8.4.2 拡張コンポーネントに生成されたクラス

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

17.8.4.3 ビジネス・コンポーネントのタイプ

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

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

ビュー・オブジェクト

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

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

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

17.8.4.4 拡張コンポーネントの新しい属性

拡張エンティティ・オブジェクトまたはビュー・オブジェクトで新しい属性を追加する場合、属性索引番号は親コンポーネントを規準にして計算されます。たとえば、「拡張コンポーネントの親のクラスとインタフェース」で示されているProductViewビュー・オブジェクトについて考えます。カスタム・ビュー行クラスを有効にする場合、ProductViewRowImpl.javaクラスで定義される次のような属性索引定数が含まれる場合があります。

public class ProductViewRowImpl extends SummitViewRowImpl
                             implements ProductViewRow {

  /**
   * AttributesEnum: generated enum for identifying attributes and accessors. 
   * Do not modify.
   */
  public enum AttributesEnum {...}

  public static final int ID = AttributesEnum.Id.index();
  public static final int NAME = AttributesEnum.Name.index();
  public static final int SHORTDESC = AttributesEnum.ShortDesc.index();
  public static final int LONGTEXTID = AttributesEnum.LongtextId.index();
  public static final int IMAGEID = AttributesEnum.ImageId.index();
  public static final int SUGGESTEDWHLSLPRICE =
                              AttributesEnum.SuggestedWhlslPrice.index();
  public static final int WHLSLUNITS = AttributesEnum.WhlslUnits.index();
  public static final int SOMEVALUE = AttributesEnum.SomeValue.index();
  //etc.
}

ProductViewExのような拡張ビュー・オブジェクトを作成するとき、そのビュー・オブジェクトでSomeExtraAttrのような属性を追加し、カスタム・ビュー行クラスを有効にした場合、その属性定数は、親コンポーネントの属性定数の最大値を規準にして計算されます。

public class ProductViewExRowImpl extends ProductViewRowImpl
                                   implements ProductViewExRow {

  public static final int MAXUSAGECONST = 1;

  public enum AttributesEnum {
      SomeExtraAttr {
          public Object get(ProductViewExRowImpl obj) {
              return obj.getSomeExtraAttr();
          }

          public void put(ProductViewExRowImpl obj, Object value) {
              obj.setAttributeInternal(index(), value);
          }
      }
       
      private static AttributesEnum[] vals = null;
      private static int firstIndex =
          ViewDefImpl.getMaxAttrConst("oracle.summit.model.extend.ProductView");

      public abstract Object get(ProductViewExRowImpl object);
 
      public abstract void put(ProductViewExRowImpl object, Object value);
 
      public int index() {
          return AttributesEnum.firstIndex() + ordinal();
      }
 
      public static int firstIndex() {
          return firstIndex;
      }
 
      public static int count() {
          return AttributesEnum.firstIndex() +
                                 AttributesEnum.staticValues().length;
      }
 
      public static AttributesEnum[] staticValues() {
          if (vals == null) {
              vals = AttributesEnum.values();
          }
          return vals;
      }
  }
  public static final int SOMEEXTRAATTR = AttributesEnum.SomeExtraAttr.index();
...
}

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

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

注意:

この項の例では、SummitADF_Examplesアプリケーション・ワークスペースのExtendedProjectプロジェクトを参照します。

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

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

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

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

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

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

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

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

注意:

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

拡張コンポーネントを置換するには:

  1. 「アプリケーション」ウィンドウで、拡張コンポーネントを追加するプロジェクトを右クリックし、「プロジェクト・プロパティ」を選択します。
  2. 「プロジェクト・プロパティ」ダイアログで、「ADFビジネス・コンポーネント」→「置換」を選択し、「置換」リストでベース・アプリケーションのコンポーネントを選択します。
  3. 「置換する対象」リストで、置き換えるカスタマイズした拡張コンポーネントを選択します。
  4. 「追加」をクリックします。

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

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

    この図は周囲のテキストで説明しています

17.9.2 置換時の処理

ExtendedProjectという名前のプロジェクトでグローバル・コンポーネント置換リストを定義すると、置換リストは、ソース・パスのルート・ディレクトリにあるExtendedProject.jpxに保存されます。

次の例に示すように、このファイルには、置換されるコンポーネントごとに1つずつ、Substitute要素が含まれます。

<JboProject
   Name="ExtendedProject"
   SeparateXMLFiles="true"
   PackageName="oracle.summit.model.extended" >
   <Containee
      Name="custompackage"
      FullName="oracle.summit.model.custompackage"
      ObjectType="JboPackage" >
   </Containee>
   <Containee
      Name="extended"
      FullName="oracle.summit.model.extended"
      ObjectType="JboPackage" >
   </Containee>
   <AppContainee
      Name="Model"
      FullName="oracle.summit.model.Model"
      ObjectType="JboProject">
   <Substitutes>
      <Substitute
        OldName="oracle.summit.model.extended.ProductEx"
        NewName="oracle.summit.model.custompackage.CustomProduct" />
   </Substitutes>
</JboProject>

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

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

たとえば、「拡張コンポーネントの親のクラスとインタフェース」で説明されている、Productエンティティ・オブジェクトとProductViewビュー・オブジェクトをカスタマイズする簡単な例について考えます。カスタマイズのため、次のようにしてExtendedProjectという名前の新しいオブジェクトを作成するものとします。

  • ベース・コンポーネントを含むJARファイルに対するライブラリを定義します。

  • ProductおよびProductViewを含むパッケージをインポートします。

  • CustomizeProductおよびCustomizeProductViewという個別のパッケージ名で、新しい拡張コンポーネントを作成します。

  • 拡張コンポーネントを使用するためのコンポーネント置換リストを定義します。

拡張コンポーネントを作成するときは次のようにします。

  • ProductViewExビュー・オブジェクトにSomeExtraAttributeという名前の新しいビュー属性を追加します。

  • 新しい検証ルールをCustomizedProductエンティティ・オブジェクトに追加し、Qという製品名は使用できないようにします。

  • CustomizedProduct.javaクラスのgetChecksum()メソッドをオーバーライドし、I am the CustomizedProduct Classを返すようにします。

Factory-Substitution-List Javaシステム・プロパティを、値がExtendsAndSubstitutesに設定されるよう定義し、「拡張コンポーネントの親のクラスとインタフェース」で説明するものとまったく同じテスト用のクライアント・クラスを実行すると、サンプルの出力は、置換されたコンポーネントの使用を反映して変化します。