ヘッダーをスキップ
Oracle Application Development Framework Forms/4GL開発者のための開発者ガイド
10g(10.1.3.0)
B40013-02
  目次
目次
索引
索引

戻る
戻る
 
次へ
次へ
 

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

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

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


注意:

この章の例の稼働バージョンを試すには、サンプルのダウンロード・ページ(http://otn.oracle.com/documentation/jdev/b25947_01)からAdvancedExamplesワークスペースをダウンロードしてください。

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

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

25.1.1 ADF Business Componentsフレームワーク拡張クラスとは

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

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

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

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

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

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

フレームワーク拡張クラスを作成するには、次の手順に従います。

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

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

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

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

  3. 「Javaクラスの作成」ダイアログを使用して、新しいクラスを作成します。

    このダイアログは、「新規ギャラリ」「General」カテゴリで使用できます。

  4. 拡張フィールドのoracle.jbo.serverパッケージから、適切なフレームワーク・ベース・クラスを指定します。

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

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

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

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


注意:

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

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

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

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

任意のADF Business Componentsウィザードまたはエディタの「Java」ページを使用して、ADFコンポーネントのベース・クラスを設定できます。実際に行う前に、次のチェックリストを確認してください。

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

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

図25-2は、フレームワーク・クラスを参照できることを確認した後、新しいアプリケーション・モジュールのベース・クラスとしてCustomAppModuleImplクラスを使用する方法を示しています。ウィザードの「Java」ページの「クラスの拡張」ボタンをクリックすると表示される拡張ダイアログでは、フレームワーク拡張クラスの完全修飾名を入力できます(または、「参照」ボタンをクリックし、JDeveloperの「クラス・ブラウザ」を使用して検索します)。

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

カスタム・ベース・クラスを指定するときの拡張ダイアログの図

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


注意:

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

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

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

25.1.5.1 フレームワーク拡張クラスをXMLのみのコンポーネントの基礎にする場合

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

例25-1 XMLコンポーネント定義に記録されるカスタム・ベース・クラス名

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

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

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

拡張フレームワーク・ベース・クラスの流れを示す図

25.1.5.2 フレームワーク拡張クラスをカスタムJavaクラスのあるコンポーネントの基礎にする場合

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

例25-2 XMLコンポーネント定義に記録されたカスタム・コンポーネント・クラス

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

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

例25-3 新しいベース・クラスを反映するためのコンポーネントのカスタムJavaクラスの更新

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

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

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

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

25.1.6 留意事項

25.1.6.1 カスタム・コンポーネントのJavaファイルのExtends句を手動で更新しない

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

25.1.6.2 複数レベルのフレームワーク拡張クラスを使用できる

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

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

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

25.1.6.3 フレームワーク拡張クラスに対するプロジェクト・レベルの設定

フレームワーク拡張クラスの特定のセットをプロジェクトの標準として使用する場合は、「プロジェクト・プロパティ」ダイアログの「ビジネス・コンポーネント: ベース・クラス」ページ(図25-5を参照)を開き、各コンポーネント・タイプに対して優先的に使用するベース・クラスを定義できます。たとえば、プロジェクトで作成される新しいアプリケーション・モジュールがデフォルトでCustomAppModuleImplクラスを使用するよう指定するには、図で示されているように、「アプリケーション・モジュール」「オブジェクト」クラス名フィールドに、そのクラスの完全修飾名を入力します。ベース・クラスに対するこれらの優先指定を設定しても、プロジェクト内の既存コンポーネントには影響ありませんが、コンポーネント・ウィザードが作成する新しいコンポーネントにはこの設定が使用されます。

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

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

25.1.6.4 IDEレベルでのフレームワーク拡張クラスの設定

JDeveloperで「ツール」「設定」メイン・メニュー項目を選択すると開く「ビジネス・コンポーネント: ベース・クラス」ページでは、グローバル・レベルでフレームワーク・ベース・クラスの設定を行うことができます。この設定は、ADFコンポーネントを含む既存のプロジェクトには影響を与えませんが、作成される新しいプロジェクトのプロジェクト・レベルのビジネス・コンポーネント・ベース・クラス設定のデフォルト値として使用されます。

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

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

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

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

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

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


注意:

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

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

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

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


注意:

「新規ギャラリ」「Deployment Profiles」カテゴリが表示されない場合は、ダイアログの上部にある「フィルタ方法」ドロップダウン・リストで「すべてのテクノロジ」を選択すると、表示されるようになります。

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

最後に、JARファイルを作成するには、アプリケーション・ナビゲータの「リソース」フォルダでFrameworkExtensions.deployノードを選択し、ポップアップ・メニューで「JARファイルにデプロイ」を選択します。JDeveloperの「ログ・ウィンドウ」「デプロイ」タブに、次のようなフィードバックが表示されます。

----  Deployment started.  ----    Apr 28, 2006 1:42:39 PM
Running dependency analysis...
Wrote JAR file to ...\FrameworkExtensions\deploy\FrameworkExtensions.jar
Elapsed time for deployment:  less than one second
----  Deployment finished.  ----    Apr 28, 2006 1:42:39 PM

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

JDeveloperは、再利用可能なコンポーネント・ライブラリで構成されるJARファイルを編成するための簡便な方法として、名前付きのライブラリを使用します。フレームワーク拡張JARファイルのライブラリを定義するには、次の操作を実行します。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

例25-4 キーによる管理行の設定の自動化

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

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

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

たとえば、SRDemoアプリケーションのSRViewObjectImplフレームワーク拡張クラスでは、ビュー・オブジェクトのinsertRow()メソッドをオーバーライドし、条件に基づいて行を挿入し、行セットの最後の行として表示しています。このフレームワーク拡張クラスを拡張するビュー・オブジェクトでInsertNewRowsAtEndという名前のカスタム・メタデータ・プロパティが定義されている場合、この汎用コードは末尾への新しい行の挿入を実行します。ビュー・オブジェクトでこのプロパティが定義されていない場合は、insertRow()のデフォルトの動作が実行されます。SRDemoアプリケーションでは、ServiceHistoriesビュー・オブジェクトでこのカスタム・メタデータ・プロパティが定義されているので、追加された新規行は最後に挿入されます。

例25-5 ビュー・オブジェクトのデフォルト行セットの末尾への条件付きでの新規行の挿入

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

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

25.3.4 留意事項

25.3.4.1 実行時における属性の種類の判定

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

表25-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を返すアクセッサ属性


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

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

25.3.4.3 実行時のカスタム・プロパティの設定

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


注意:

AdvancedExamplesワークスペースのProgrammaticallySetPropertiesプロジェクトを使用して、この手法の例を試すことができます。ダウンロード方法については、この章の最初にある注意を参照してください。

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

フレームワーク拡張クラスの作成に加えて、すべてのコンポーネントがデフォルトで実装できるカスタム・インタフェースも作成できます。ここではアプリケーション・モジュールの例について説明しますが、カスタム拡張ビュー・オブジェクトおよびビュー行インタフェースに対しても同じ機能を作成できます。


注意:

この項の例では、AdvancedExamplesワークスペースのCustomizedExtensionInterfaceプロジェクトを参照します。ダウンロード方法については、この章の最初にある注意を参照してください。

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ウィザードは、コンパイルが正常に終了したインタフェースのみを参照します。

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

  1. アプリケーション・モジュールにカスタムJavaクラスがあることを確認します。

    ない場合は、アプリケーション・モジュール・エディタの「Java」ページで「適用」ボタンをクリックして有効にできます。

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

    そのためには、再びアプリケーション・モジュール・エディタの「Java」タブを使用し、「クラスの拡張」ボタンをクリックして拡張ダイアログを開きます。

  3. クライアントがコンポーネントで使用できるカスタム・インタフェースの1つとして、CustomApplicationModuleインタフェースを指定します。

    そのためには、「クライアント・インタフェース」ページを開きます。「インタフェース」ボタンをクリックします。CustomApplicationModuleインタフェースを「使用可能」リストから「選択済」リストに移動し、「OK」をクリックします。

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


    注意:

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

アプリケーション・モジュール・エディタを終了すると、JDeveloperがアプリケーション・モジュールのカスタム・インタフェースProductModuleを生成します。このインタフェースは、次のように、ApplicationModuleインタフェースとCustomApplicationModule拡張インタフェースの両方を自動的に拡張します。

package devguide.advanced.customintf.common;
import devguide.advanced.customintf.fwkext.CustomApplicationModule;

import oracle.jbo.ApplicationModule;
// ---------------------------------------------------------------------
// ---    File generated by Oracle ADF Business Components Design Time.
// ---------------------------------------------------------------------
public interface ProductModule
       extends CustomApplicationModule, ApplicationModule {
  void doSomethingProductRelated();
}

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


注意:

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

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

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

次のようなPL/SQLパッケージについて考えます。

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

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


注意:

この項の例では、AdvancedExamplesワークスペースのStoredProcedureInvocationプロジェクトを参照します。ダウンロード方法については、この章の最初にある注意を参照してください。

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

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

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

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

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

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

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

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

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

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

  4. 文を実行します。

  5. 文を閉じます。

例25-7 IN引数のみのストアド・プロシージャの呼出しを簡単にするヘルパー・メソッド

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

このようなヘルパー・メソッドを使用すると、前記のproc_with_three_argsプロシージャの呼出しは次のようになります。

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

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

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

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

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

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

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

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

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

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

  5. 文を実行します。

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

  7. 文を閉じます。

例25-8 IN引数のみのストアド・ファンクションの呼出しを簡単にするヘルパー・メソッド

// Some constants
public static int NUMBER = Types.NUMERIC;
public static int DATE = Types.DATE;
public static int VARCHAR2 = Types.VARCHAR;

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

このようなヘルパー・メソッドを使用すると、前記のfunc_with_three_argsプロシージャの呼出しは次のようになります。

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

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

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

OUTモードまたはIN OUTモードの引数を含むdevguidepkg.proc_with_out_argsのようなストアド・プロシージャまたはストアド・ファンクションの呼出しでは、前の項の場合と同じようにCallableStatementを使用する必要がありますが、ヘルパー・メソッドへの一般化は少し難しくなります。例25-9は、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を閉じます。

例25-9 複数のOUT引数があるストアド・プロシージャの呼出し

public Date callProcWithOutArgs(Number n, String v) {
  CallableStatement st = null;
  try  {
    // 1. Define the PL/SQL block for the statement to invoke
    String stmt = "begin devguidepkg.proc_with_out_args(?,?,?); end;";
    // 2. Create the CallableStatement for the PL/SQL block
    st = getDBTransaction().createCallableStatement(stmt,0);
    // 3. Register the positions and types of the OUT parameters
    st.registerOutParameter(2,Types.DATE);
    st.registerOutParameter(3,Types.VARCHAR);
    // 4. Set the bind values of the IN parameters
    st.setObject(1,n);
    st.setObject(3,v);
    // 5. Execute the statement
    st.executeUpdate();
    // 6. Create a bean to hold the multiple return values
    DateAndStringBean result = new DateAndStringBean();
    // 7. Set value of dateValue property using first OUT param
    result.setDateVal(new Date(st.getDate(2)));
    // 8. Set value of stringValue property using 2nd OUT param
    result.setStringVal(st.getString(3));
    // 9. Return the result
    return result;
  } catch (SQLException e)  {
    throw new JboException(e);
  } finally  {
    if (st != null) {
      try {
        // 10. Close the JDBC CallableStatement
        st.close();
      }
      catch (SQLException e) {}
    }
  }
}

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

package devguide.advanced.storedproc;
import java.io.Serializable;
import oracle.jbo.domain.Date;
public class DateAndStringBean implements Serializable {
  Date dateVal;
  String stringVal;
  public void setDateVal(Date dateVal) {this.dateVal=dateVal;}
  public Date getDateVal() {return dateVal;}
  public void setStringVal(String stringVal) {this.stringVal=stringVal;}
  public String getStringVal() {return stringVal;}
}

注意:

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

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

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

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

例25-10 現在のJDBC Connectionにアクセスするためのヘルパー・メソッド

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

注意:

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

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

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


注意:

この項の例では、AdvancedExamplesワークスペースのReusableComponentsProjectImportingReusableComponents、およびOtherProjectWithComponentsの各プロジェクトを参照します。ダウンロード方法については、この章の最初にある注意を参照してください。

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

ビジネス・コンポーネントのライブラリを構成するJavaクラスおよびXMLコンポーネント定義を含むJARファイルを作成するには、「ビジネス・コンポーネントのアーカイブ・プロファイルの作成」ダイアログを使用します。このダイアログは、「新規ギャラリ」「General」「Deployment Files」カテゴリで使用できます。


注意:

「新規ギャラリ」「Deployment Profiles」カテゴリが表示されない場合は、ダイアログの上部にある「フィルタ方法」ドロップダウン・リストで「すべてのテクノロジ」を選択すると、表示されるようになります。

デプロイメント・プロファイルにReusableComponents.bcdeployのような名前を設定し、「OK」をクリックします。図25-8で示されているように、ReusableComponents.bcdeployビジネス・コンポーネント・デプロイメント・アーカイブ・プロファイルには、ネストされた2つのJARデプロイメント・プロファイルが含まれます。

これら2つのネストされたプロファイルは、次のものをバンドルするために事前に構成されている標準のJARデプロイメント・プロファイルです。

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

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

アプリケーション・ナビゲータでのデプロイメント・プロファイルの図

JARファイルを作成するには、アプリケーション・ナビゲータの「リソース」フォルダでReusableComponents.bcdeployノードを選択し、ポップアップ・メニューで「デプロイ」を選択します。JDeveloperの「ログ・ウィンドウ」「デプロイ」タブに、次のようなフィードバックが表示されます。

----  Deployment started.  ----    Apr 28, 2006 7:04:02 PM
Running dependency analysis...
Wrote JAR file to ...\ReuseableComponents\deploy\ReuseableComponentsCSMT.jar
Running dependency analysis...
Wrote JAR file to ...\ReuseableComponents\deploy\ReuseableComponentsCSCommon.jar
Elapsed time for deployment:  less than one second
----  Deployment finished.  ----    Apr 28, 2006 7:04:02 PM

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

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

ライブラリからビジネス・コンポーネントのパッケージをインポートするには、次の手順を実行します。

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

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

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

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

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

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

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

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

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

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

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

25.7.4 留意事項

25.7.4.1 プロジェクトのソース・パスへのビジネス・コンポーネントの他のディレクトリの追加

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

25.7.4.2 JARの変更を反映するために閉じて開きなおす必要がある場合

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

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

コンポーネントのパッケージを誤ってインポートした場合、またはインポートしたコンポーネントのパッケージで使用していないものを削除する場合、JDeveloperには対話式にこれを行う方法はありません。パッケージのインポートを取り消すには、次の手順を実行する必要があります。

  1. 該当するワークスペースをアプリケーション・ナビゲータから削除します。

  2. テキスト・エディタを使用して、YourImportingProjectName.jpxファイルを編集します。

  3. 削除するインポート済パッケージを表すContainee要素を、このファイルから削除します。

    このとき、削除するパッケージに対する適切なContaineeタグから対応する/Containeeタグまでの間のすべての行(両方のタグの行を含みます)を、ファイルから削除します。

  4. JDeveloperでワークスペースを再び開きます。


注意:

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

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


注意:

この項の例では、AdvancedExamplesワークスペースのCustomizedErrorMessagesプロジェクトを参照します。ダウンロード方法については、この章の最初にある注意を参照してください。

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

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

JBO-27014: Attribute Name is Product is required

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

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

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

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

  1. ビジネス・コンポーネントが含まれるプロジェクトの「プロジェクト・プロパティ」ダイアログで、「ビジネス・コンポーネント」「オプション」ページを開きます。

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

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

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


    注意:

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

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

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

    例25-11は、前記の例で使用したJBO-27014エラーに対するエラー・メッセージ文字列をオーバーライドするカスタム・メッセージ・バンドル・クラスを示しています。

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

    package devguide.advanced.customerrs;
    import java.util.ListResourceBundle;
    public class CustomMessageBundle extends ListResourceBundle {
      private static final Object[][] sMessageStrings
        = new String[][] {
           {"27014","You must provide a value for {2}"}
         };
      protected Object[][] getContents() {
        return sMessageStrings;
      }
    }
    

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

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

JBO-27014: You must provide a value for Name

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

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

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

alter table products add (
  constraint name_cannot_begin_with_x
      check (upper(substr(name,1,1)) != 'X')
);

アプリケーションでカスタム・エラー・メッセージを定義するには、制約名をメッセージ・キーとして、カスタム・メッセージ・バンドルにメッセージを追加するだけです。たとえば、前の項で作成したものと同じCustomMessageBundle.javaクラスを使用すると、例25-12は、上で定義したデータベース制約の名前と一致するキーNAME_CANNOT_BEGIN_WITH_Xを持つメッセージを定義した状態を示します。

例25-12 データベース制約違反のエラー・メッセージのカスタマイズ

package devguide.advanced.customerrs;
import java.util.ListResourceBundle;
public class CustomMessageBundle extends ListResourceBundle {
  private static final Object[][] sMessageStrings
    = new String[][] {
       {"27014","You must provide a value for {2}"},
       {"NAME_CANNOT_BEGIN_WITH_X",
        "The name cannot begin with the letter x!"}
     };
  protected Object[][] getContents() {
    return sMessageStrings;
  }
}

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

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

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

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

例25-13 カスタム・データベース・トランザクション・フレームワーク拡張クラス

package devguide.advanced.customerrs;
import oracle.jbo.DMLConstraintException;
import oracle.jbo.JboException;
import oracle.jbo.common.StringManager;
import oracle.jbo.server.DBTransactionImpl2;
import oracle.jbo.server.TransactionEvent;
public class CustomDBTransactionImpl  extends DBTransactionImpl2 {
  public void postChanges(TransactionEvent te) {
    try {
      super.postChanges(te);
    }
    /*
     * Catch the DML constraint exception
     * and perform custom error handling here
     */
    catch (DMLConstraintException ex) {
      ex.setExceptions(null);
      throw ex;
    }
  }
}

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

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

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

  2. TransactionFactoryプロパティの値に、このカスタム・トランザクション・ファクトリ・クラスの完全修飾名を構成します。

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

例25-14 カスタム・データベース・トランザクション・ファクトリ・クラス

package devguide.advanced.customerrs;
import oracle.jbo.server.DBTransactionImpl2;
import oracle.jbo.server.DatabaseTransactionFactory;
public class CustomDatabaseTransactionFactory
       extends DatabaseTransactionFactory {
  public CustomDatabaseTransactionFactory() {
  }
  /**
   * Return an instance of our custom ToyStoreDBTransactionImpl class
   * instead of the default implementation.
   *
   * @return instance of custom CustomDBTransactionImpl implementation.
   */
  public DBTransactionImpl2 create() {
    return new CustomDBTransactionImpl();
  }
}

最後の作業として、構成エディタの「プロパティ」タブを使用し、TransactionFactoryプロパティに値devguide.advanced.customerrs.CustomDatabaseTransactionFactoryを割り当てます。この構成を使用してアプリケーションを実行すると、カスタム・トランザクション・クラスが使用されます。

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

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

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

ビジネス・コンポーネントが他のビジネス・コンポーネントを拡張できることを示す図

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


注意:

この項の例では、AdvancedExamplesワークスペースのBaseProjectプロジェクトを参照します。ダウンロード方法については、この章の最初にある注意を参照してください。

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

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

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

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

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

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

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

例25-15 XMLディスクリプタに親が反映された拡張コンポーネント

<ViewObject
   Name="ServiceRequestsByStatus"
   Extends="oracle.srdemo.model.queries.ServiceRequests"
   Where="((ServiceRequest.CREATED_BY = CreatedByUser.USER_ID)
          AND (ServiceRequest.ASSIGNED_TO = AssignedToUser.USER_ID(+)))
          AND (ServiceRequest.PROD_ID = Product.PROD_ID)
          AND STATUS LIKE NVL(:StatusCode,&#39;%&#39;)"
   OrderBy="REQUEST_DATE DESC"
   BindingStyle="OracleName"
   CustomQuery="false"
   ComponentClass="oracle.srdemo.model.queries.ServiceRequestsByStatusImpl"
   FetchMode="FETCH_AS_NEEDED"
   UseGlueCode="false" >
   <Variable
      Name="StatusCode"
      Kind="where"
      Type="java.lang.String"
      DefaultValue="%" >
   </Variable>
</ViewObject>

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

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

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

25.9.3 留意事項

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

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

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

  • クラスProductsImpl

  • 行クラスProductsRowImpl

  • インタフェースProducts

  • 行インタフェースProductsRow

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

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

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

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

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

例25-16 親コンポーネントと拡張コンポーネントの使用

package devguide.advanced.extsub;
/* imports omitted */
public class TestClient {
  public static void main(String[] args) {
    String        amDef = "devguide.advanced.extsub.ProductModule";
    String        config = "ProductModuleLocal";
    ApplicationModule am =
    Configuration.createRootApplicationModule(amDef,config);
    Products products = (Products)am.findViewObject("Products");
    products.executeQuery();
    ProductsRow product = (ProductsRow)products.first();
    printAllAttributes(products,product);
    testSomethingOnProductsRow(product);
    // 1. You can use parent Products interface for ProductsByName
    products = (Products)am.findViewObject("ProductsById");
    // 2. Or cast it to its more specific ProductsByName interface
    ProductsByName productsById = (ProductsByName)products;
    productsById.setProductName("Ice");
    productsById.executeQuery();
    product = (ProductsRow)productsById.first();
    printAllAttributes(productsById,product);
    testSomethingOnProductsRow(product);
    am.getTransaction().rollback();
    Configuration.releaseRootApplicationModule(am,true);
  }
  private static void testSomethingOnProductsRow(ProductsRow product) {
    try {
      // 3. Test if row is a ProductsByNameRow before casting
      if (product instanceof ProductsByNameRow) {
        ProductsByNameRow productByName = (ProductsByNameRow)product;
        productByName.someExtraFeature("Test");
      }
      product.setName("Q");
      System.out.println("Setting the Name attribute to 'Q' succeeded.");
    }
    catch (ValidationException v) {
      System.out.println(v.getLocalizedMessage());
    }
  }
  private static void printAllAttributes(ViewObject vo, Row r) {
    String viewObjName = vo.getName();
    System.out.println("Printing attribute for a row in VO '"+
                       viewObjName+"'");
    StructureDef def = r.getStructureDef();
    StringBuilder sb = new StringBuilder();
    int numAttrs = def.getAttributeCount();
    AttributeDef[] attrDefs = def.getAttributeDefs();
    for (int z = 0; z < numAttrs; z++) {
      Object value = r.getAttribute(z);
      sb.append(z > 0 ? "  " : "")
        .append(attrDefs[z].getName())
        .append("=")
        .append(value == null ? "<null>" : value)
        .append(z < numAttrs - 1 ? "\n" : "");
    }
    System.out.println(sb.toString());
  }
}

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

Printing attribute for a row in VO 'Products'
ProdId=100
  Name=Washing Machine W001
  Checksum=I am the Product Class
Setting the Name attribute to 'Q' succeeded.
Printing attribute for a row in VO 'ProductsById'
ProdId=119
  Name=Ice Maker I012
  Checksum=I am the Product Class
  SomeExtraAttr=SomeExtraAttrValue
## Called someExtraFeature of ProductsByNameRowImpl
Setting the Name attribute to 'Q' succeeded.

注意:

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

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

拡張コンポーネントを作成するとき、拡張コンポーネントの「Java」ページの「クラスの拡張」ボタンは無効になります。これは、JDeveloperが親コンポーネントの適切なクラスを自動的に拡張するため、別のクラスをユーザーが選択できるようにしても意味がないためです。

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

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

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

ビュー・オブジェクト

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

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

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

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

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

public class ProductsRowImpl extends ViewRowImpl
                             implements ProductsRow {
  public static final int PRODID = 0;
  public static final int NAME = 1;
  public static final int CHECKSUM = 2;
  //etc.
}

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

public class ProductsByNameRowImpl extends ProductsRowImpl
                                   implements ProductsByNameRow {
  public static final int MAXATTRCONST =
    ViewDefImpl.getMaxAttrConst("devguide.advanced.extsub.Products");
  public static final int SOMEEXTRAATTR = MAXATTRCONST;

追加属性の索引値は、MAXATTRCONST+1MAXATTRCONST+2などになります。

25.9.3.5 作成後のExtendsの変更に関する設計時の制限

拡張コンポーネントを定義した後で、拡張コンポーネントが継承している親コンポーネントを変更できます。その場合、次の方法での変更が可能です。

  • アプリケーション・ナビゲータで拡張コンポーネントを選択

  • プロパティ・インスペクタを使用してExtendsプロパティを変更

ただし、現時点では、この手法を使用して、どの親からも継承しないように拡張コンポーネントを変更することはできません。この制限に対する例外はエンティティ・オブジェクトです。エンティティ・オブジェクトのコンポーネント・エディタの「名前」ページには拡張フィールドがあり、必要な場合には空白にできます。他のすべての拡張コンポーネントについては、親コンポーネントを拡張しないようにするには、削除してから作成しなおす必要があります。

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

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


注意:

この項の例では、AdvancedExamplesワークスペースのBaseProjectおよびExtendAndSubstituteプロジェクトを参照します。ダウンロード方法については、この章の最初にある注意を参照してください。

25.10.1 コードの変更よりコンポーネントの拡張と置換えの方がよい理由

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

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

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

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

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

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

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

グローバルなコンポーネント置換を定義するには、基になるアプリケーションからインポートしたコンポーネントを基にして拡張コンポーネントを作成したプロジェクトの、「プロジェクト・プロパティ」ダイアログの「ビジネス・コンポーネント: 置換」ページを使用します。図25-10で示されているように、各コンポーネントの置換を定義するには、次のようにします。

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

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

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


注意:

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

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

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

25.10.3 置換時に行われる処理

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

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

例25-17 プロジェクトのJPXファイルに保存されるコンポーネント置換リスト

<JboProject
   Name="ExtendAndSubstitute"
   SeparateXMLFiles="true"
   PackageName="" >
   <Containee
      Name="anotherpkg"
      FullName="devguide.advanced.anotherpkg.anotherpkg"
      ObjectType="JboPackage" >
   </Containee>
   <Containee
      Name="extsub"
      FullName="devguide.advanced.extsub.extsub"
      ObjectType="JboPackage" >
      <DesignTime>
         <Attr Name="_LocationURL"
               Value="../../BaseProject/deploy/BaseProjectCSMT.jar" />
      </DesignTime>
   </Containee>
   <Substitutes>
      <Substitute OldName="devguide.advanced.extsub.Product"
                  NewName="devguide.advanced.anotherpkg.CustomizedProduct" />
      <Substitute OldName="devguide.advanced.extsub.Products"
                  NewName="devguide.advanced.anotherpkg.CustomizedProducts" />
   </Substitutes>
</JboProject>

25.10.4 基のアプリケーションでの置換コンポーネントの有効化

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

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

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

Factory-Substitution-List Javaシステム・プロパティを定義し、値ExtendsAndSubstitutesを設定して、例25-16とまったく同じテスト用のクライアント・クラスを実行すると、サンプルの出力は、置換されたコンポーネントの使用を反映して次のように変化します。

Printing attribute for a row in VO 'Products'
ProdId=100
  Name=Washing Machine W001
  Checksum=I am the CustomizedProduct Class
  ExtraViewAttribute=Extra Attr Value
The name cannot be Q!
Printing attribute for a row in VO 'ProductsById'
ProdId=119
  Name=Ice Maker I012
  Checksum=I am the CustomizedProduct Class
  SomeExtraAttr=SomeExtraAttrValue
## Called someExtraFeature of ProductsByNameRowImpl
The name cannot be Q!

例25-16からの出力と比較すると、ファクトリ置換リストがあるため、元のテスト・プログラムのProductsビュー・オブジェクトにExtraViewAttributeが追加され、Checksum属性の値I am the CustomizedProduct Classが表示されるようになり、製品名として値Qを割り当てられなくなっています。これらのコンポーネント動作の変更は、提供されたコンポーネントの元のJavaまたはXMLソース・コードを変更することなく行われました。