ヘッダーをスキップ
Oracle Application Development Framework開発者ガイド
10g(10.1.3.0)
B40012-02
  目次
目次
索引
索引

戻る
戻る
 
次へ
次へ
 

12 検証と変換の使用

この章では、アプリケーションに検証と変換の機能を追加する方法について説明します。また、エラー(検証に起因しないエラーも含む)を処理および表示する方法についても説明します。

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

12.1 検証と変換の概要

ADF Facesの入力コンポーネントには、組込み検証機能が備わっています。コンポーネントに検証を設定するには、required属性を設定するか、事前構築済のADF Facesバリデータの1つを使用します。また、ADFアプリケーションではモデル・レイヤーに検証機能もあるため、属性へのバインディングに検証を設定できます。さらに、ビジネス・ニーズに合せて独自のADF Facesバリデータを作成することもできます。

ADF Facesの入力コンポーネントには、組込み変換機能も備わっています。このため、ユーザーは情報をStringsとして入力できます。アプリケーションは、この情報をDateなどの別のデータ型に自動的に変換できます。逆に、String以外のデータとして格納されているデータを、表示および更新のためにStringに変換することもできます。

selectInputDateなどの多くのコンポーネントでは、自動的にこの機能が提供されます。また、inputTextのように、コンバータが存在するタイプの属性をデータ・コントロール・パレットからドラッグ・アンド・ドロップすると自動的に組込みADF FacesまたはJSFの参照実装コンバータが追加されるようなコンポーネントもあります。

バリデータまたはコンバータが失敗した場合、関連付けられたエラー・メッセージをユーザーに表示できます。これらのメッセージは、クライアント側で検証できるようにポップアップ・ダイアログ内に表示することも、同じページ内で検証または変換に失敗したコンポーネントの横に表示することもできます。

この章では次の内容について説明します。

12.2 検証、変換およびアプリケーション・ライフサイクル

図12-1は、統合されたJSFおよびADFのライフサイクルにおいて検証と変換がどのように機能するかを示しています。

図12-1 ライフサイクルにおける検証と変換

検証と変換を処理するフェーズ

データを含むフォームが送信されると、ブラウザは、value属性がバインドされているUIコンポーネントごとにリクエスト値をサーバーに送信します。リクエスト値は、最初に、JSFのリクエスト値の適用フェーズでコンポーネントのインスタンスに格納されます。値が変換を必要とする場合(たとえば、DateTimeオブジェクトとして格納されている値をStringとして表示する場合)、データは適切なタイプに変換されます。データが格納されているコンポーネントのいずれかに対してADF Faces検証を設定した場合、値はモデルに適用される前に、検証処理フェーズで定義済の規則に対して検証されます。

検証または変換が失敗した場合、ライフサイクルはレスポンスのレンダリング・フェーズに進み、対応するエラー・メッセージがページに表示されます。検証および変換が正常に完了した場合は、UpdateModelフェーズが開始され、検証済および変換済の値を使用してモデルが更新されます。

この時点で、ADFモデル検証規則が存在する場合は、ADFのモデル更新検証フェーズで値がそれらの規則に対して検証されます。ADF Faces検証の場合と同様に、検証が失敗すると、ライフサイクルはレスポンスのレンダリング・フェーズに進みます。詳細は、6.2.3項「実行時に行われる処理: JSFおよびADFのライフサイクル」を参照してください。

検証または変換のエラーが発生すると、検証または変換に失敗したコンポーネント(JSF検証または変換の場合)あるいは属性(ADFモデル検証の場合)により、関連付けられたエラー・メッセージがキューに挿入され、そのコンポーネントまたは属性は無効化されます。次に現在のページが、エラー・メッセージとともに再表示されます。ADF FacesコンポーネントとADFモデルでは両方とも、宣言的にこれらのメッセージを設定する方法が提供されています。ADF Facesアプリケーションで他のエラーが処理される方法の詳細は、12.8項「ADFアプリケーション内の例外の処理と表示」を参照してください。

12.3 検証の追加

ユーザーがフィールド内のデータを編集または入力してフォームを送信したときに、設定した規則および条件に対してデータが検証されるように、検証を追加できます。検証が失敗すると、アプリケーションによりエラー・メッセージが表示されます。

これらの規則および条件は、次のいずれかのレイヤーで設定できます。

12.3.1 検証の追加方法

JSFページでADF Faces検証を設定し、ページ定義ファイルでADFモデル検証を設定します。どちらの検証も、メッセージ表示はJSFページ上で処理されます。検証エラーにより作成されるメッセージの表示方法の詳細は、12.7項「エラー・メッセージの表示」を参照してください。

12.3.1.1 ADF Faces検証の追加

デフォルトでは、ADF Faces検証はクライアント側とサーバー側の両方で行われます。構文検証およびセマンティク検証はいずれもクライアント側およびサーバー側で実行されますが、クライアント側は、サーバー側で実行される検証のサブセットのみを実行します。クライアント側の検証では、バリデータは、サーバーとの間をラウンドトリップしなくてもデータを取得して表示できます。


注意:

JSFページでJavaScript form.submit()関数がコールされた場合、ADF Facesのクライアント側検証のサポートは省略されます。ADF Facesでは、かわりに使用できるsubmitForm()メソッドが用意されています。あるいは、ADF Faces入力コンポーネントのautoSubmit属性を使用することもできます。

クライアント側検証が実行されないようにADF Facesを設定するには、adf-faces-config.xml内に<client-validation-disabled>要素を追加し、それをtrueに追加します。

ADF Facesでは、次のタイプの検証を実行できます。

  • UIコンポーネント属性: ADF Faces入力コンポーネントには、データの検証に使用できる属性が備わっています。たとえば、ADF Faces入力コンポーネントのrequired属性を使用して簡単な検証を提供すると、値の入力を必須にするかどうかを指定できます。trueに設定した場合、コンポーネントは値を持つ必要があります。コンポーネントが値を持たない場合、アプリケーションによりエラー・メッセージが表示されます。詳細は、12.3.1.1.1項「検証属性の使用」を参照してください。

  • デフォルトのADF Facesバリデータ: ADF FacesおよびJSFの参照実装に付属のバリデータでは、日付範囲の検証や入力されたデータの長さの検証など、共通の検証チェックを実行できます。詳細は、12.3.1.1.2項「JSFおよびADF Facesのバリデータの使用」を参照してください。

  • カスタムのADF Facesバリデータ: 独自のバリデータを作成して、UIコンポーネントに対して使用するように選択できます。詳細は、12.4項「カスタムJSF検証の作成」を参照してください。

12.3.1.1.1 検証属性の使用

多くのADF FacesのUIコンポーネントには、簡単な検証を提供する属性が備わっています。表12-1に、これらの属性、その検証ロジックの説明、およびそれらの属性が含まれるUIコンポーネントを示します。

表12-1 ADF Facesの検証属性

属性 説明 対象となるコンポーネント

maxValue

Date値に使用可能な最大値。

chooseDate

minValue

Date値に使用可能な最小値。

chooseDate

required

trueに設定した場合(またはtrueに評価されるEL式に設定した場合)、コンポーネントは、少なくとも1つの文字に対して非NULL値またはString値を持つ必要がある。

表選択コンポーネント(7.6項「表での行選択の有効化」を参照)では、required属性をtrueに設定した場合、表内の少なくとも1行が選択される必要がある。

すべての入力コンポーネント、すべての選択コンポーネント、tableSelectManytableSelectOne

maximumLength

入力できる最大文字数。この値はcolumns属性に設定された値には依存しないことに注意。表12-3「ADF Facesのバリデータ」ByteLengthValidatorも参照。

inputText


データ・コントロール・パレットを使用して入力コンポーネントを作成した場合、required属性は、次のEL式に示すとおり、関連付けられたバインディングのmandatoryプロパティにバインドされます。

<af:inputText required="#{bindings.problemDescription.mandatory}"

EL式では、バインド先のオブジェクトの属性がnullになることができるかどうかが評価されます。この式をそのまま保持するように選択することも、手動でrequired属性をtrueまたはfalseに設定することもできます。


ヒント:

UIコンポーネントがバインドされるオブジェクトは、入力コンポーネントが作成された方法によって異なります。たとえば、パラメータ・フォームを作成するためのメソッドを使用してフォームが作成された場合は、属性値がまだ存在していないため、入力コンポーネントは通常、変数にバインドされます。デフォルトのEL式を使用する場合は、変数にisNotNullプロパティを設定する必要があります。メソッドにより返されたコレクションを使用してフォームが作成された場合は、入力コンポーネントは通常、エンティティ・オブジェクトの属性にバインドされます。この場合は、そのオブジェクトのisNotNullプロパティを設定する必要があります。

検証を提供するUIコンポーネント属性を使用する手順:

  1. 構造ウィンドウで、UIコンポーネントを選択します。

  2. プロパティ・インスペクタで、検証属性の値を入力します。使用可能な検証属性のリストは、表12-1を参照してください。

  3. (オプション)ユーザーが正しいデータ(数値の場合は有効な範囲など)を入力できるように導くテキストを表示する場合は、tip属性を設定します。このテキストは、コンポーネントの下に表示されます。

  4. (オプション)required属性をtrueに設定した場合(または、trueに評価可能なEL式を使用した場合)は、RequiredMessageDetail属性の値も入力できます。ADF Facesでは、検証に失敗した場合、デフォルト・メッセージのかわりにこのメッセージが表示されます。

    選択コンポーネントがrequiredに設定されている表の場合、エラー・メッセージを表示するには、表のsummary属性にエラー・メッセージを挿入する必要があります。

    メッセージには、パラメータのオプションのプレースホルダ({0}、{1}など)を含めることができます。実行時、プレースホルダは適切なパラメータ値で置き換えられます。パラメータの順序は次のとおりです。

    • コンポーネント・ラベル入力値(存在する場合)

    • 最小値(存在する場合)

    • 最大値(存在する場合)

    • パターン値(存在する場合)

    例12-1に、パラメータを使用するRequiredMessageDetail属性を示します。

    例12-1 RequiredMessageDetail属性内のパラメータ

    <af:inputText value="#{bindings.productId.inputValue}"
                  label="Product ID"
                  requiredMessageDetail="You must enter a {0}."
                  required="true"
    </af:inputText>
    

    このメッセージは、You must enter a Product IDに評価されます。

    プロパティ・インスペクタでUIコンポーネント属性の追加のヘルプを表示するには、属性名を右クリックして「ヘルプ」を選択します。

12.3.1.1.2 JSFおよびADF Facesのバリデータの使用

JSFおよびADF Facesのバリデータでは、より複雑な検証ルーチンが提供されます。表12-2にJSFの参照実装バリデータを示し、表12-3に組込みのADF Facesバリデータを示します。

表12-2 JSFの参照実装バリデータ

バリデータ タグ名 説明

DoubleRangeValidator

f:validateDoubleRange

コンポーネント値が、指定された範囲内にあることを検証する。値は、浮動小数点タイプまたは浮動小数点に変換可能である必要がある。

LengthValidator

f:validateLength

コンポーネント値の長さが、指定された範囲内にあることを検証する。値は、タイプjava.lang.Stringである必要がある。

LongRangeValidator

f:validateLongRange

コンポーネント値が、指定された範囲内にあることを検証する。値は、任意の数値タイプか、longに変換可能なStringである必要がある。


表12-3 ADF Facesのバリデータ

バリデータ タグ名 説明

ByteLengthValidator

af:validateByteLength

Javaエンコーディングの使用時にString内のバイト数を検証する。たとえば、英語における6個の文字は、日本語における6個の文字とは異なるバイト記憶域を使用する。バリデータの属性として、使用するエンコーディングを指定する。

サーバーで文字列を格納するためのバイト数を制限する必要がある場合は、入力コンポーネントのmaximumLength属性を指定するかわりにこのバリデータを使用する。

DateTimeRangeValidator

af:validateDateTimeRange

入力された日付が、指定された範囲内にあることを検証する。範囲はバリデータの属性として指定する。

RegExpValidator

af:validateRegExp

Java正規表現構文を使用してデータを検証する。



注意:

ADF Facesには、コンポーネントのカスタム・バリデータの登録に使用できるaf:validatorタグもあります。カスタム・バリデータの使用方法の詳細は、12.4項「カスタムJSF検証の作成」を参照してください。

デフォルトでは、データ・コントロール・パレットから入力テキスト・コンポーネントとして属性をドロップすると、そのコンポーネントに自動的にaf:validatorタグが追加され、関連するバインディングのvalidatorプロパティにバインドされます。このバインディングにより、クライアント側で処理するADFモデル検証にアクセスできます。詳細は、12.3.2項「データ・コントロール・パレットを使用して入力フィールドを作成するときに行われる処理」を参照してください。ADFモデル検証の詳細は、12.3.1.2項「ADFモデル検証の追加」を参照してください。


ADF Facesバリデータを追加する手順:

  1. 構造ウィンドウで、バリデータを追加するコンポーネントを右クリックします。

  2. ポップアップ・メニューで「<UIコンポーネント>の中に挿入」「ADF Faces Core」を選択して、ADF Facesバリデータを挿入します。(JSFの参照実装バリデータを挿入するには、「<UIコンポーネント>の中に挿入」「JSF Core」を選択します。)

  3. バリデータ・タグ(ValidateDateTimeRangeなど)を選択します。

  4. プロパティ・インスペクタで、属性の値(検証エラーのメッセージなど)を設定します。追加のヘルプは、属性を右クリックして「ヘルプ」を選択すると表示されます。

    ADF Facesでは、検証エラー・メッセージの詳細部分をカスタマイズできます。XxxMessageDetail属性の値を設定することで(XxxmaximumMessageDetailなどの検証エラー・タイプ)、検証が失敗したときにデフォルト・メッセージのかわりにカスタム・メッセージを表示できます。

12.3.1.2 ADFモデル検証の追加

表12-4に、属性に対して構成できるADFモデル検証規則を示します。

表12-4 ADFモデル検証規則

バリデータ規則名 説明

比較

属性の値とリテラル値を比較する。

リスト

値が値リスト内にあるかどうかを検証する。

レンジ

値が値範囲内にあるかどうかを検証する。

長さ

値の文字またはバイト・サイズをサイズおよびオペランド(greater than or equal toなど)に対して検証する。

正規表現

Java正規表現構文を使用してデータを検証する。


ADFモデル検証規則を作成する手順:

  1. 規則を作成する属性が含まれるページ定義を開きます。

  2. 構造ウィンドウで、属性、リストまたは表バインディングを選択します。

  3. プロパティ・インスペクタで「検証規則の編集」リンクを選択します。

  4. 「検証規則エディタ」で、属性名を選択し、「新規」をクリックします。

  5. 「検証ルールの追加」ダイアログで、検証規則を選択し、それに応じて規則を構成します。様々なタイプの規則を作成する際の追加のヘルプは、「ヘルプ」をクリックすると表示されます。

12.3.2 データ・コントロール・パレットを使用して入力フィールドを作成するときに行われる処理

属性をデータ・コントロール・パレットからinputTextコンポーネントとしてドロップするなど、データ・コントロール・パレットを使用して入力テキスト・フィールドを作成すると、JDeveloperにより、次のようにしてADF Faces検証コードがJSFページに自動的に提供されます。

  • af:messagesタグがafh:bodyタグの子として追加されます。

    デフォルトでは、globalOnly属性はfalseに設定され、message属性とtext属性は設定されません。詳細は、12.7項「エラー・メッセージの表示」を参照してください。

  • 次のEL式に示すように、入力フィールドのrequired属性が、関連付けられた属性バインディングのmandatoryプロパティにバインドされます。

    <af:inputText required="#{bindings.problemDescription.mandatory}"
    

    式では、関連付けられたビジネス・オブジェクトの属性に基づいてnull値が許可されるかどうかが評価されます。デフォルトでは、required属性がtrueに評価されるすべてのコンポーネントにはアスタリスクが表示されます。

  • af:validatorタグが入力コンポーネントの子として追加され、関連付けられたバインディングのvalidatorプロパティにこのタグが次のようにバインドされます。

    <af:inputText value="#{bindings.someAttribute.inputValue}"...>
      <af:validator binding="#{bindings.someAttribute.validator}"/>
    </af:inputText>
    

    このバインディングにより、クライアント側のJSFライフサイクルで、関連付けられた属性に対して設定したADFモデル検証にアクセスできます。ADFモデル検証を使用しない場合は、af:validatorタグを削除して任意の検証タグを挿入できます。また、どの検証も使用しない場合は、検証タグを削除できます。ADFモデル検証のみを使用する場合は、タグをそのままの状態で保持する必要があります。


    ヒント:

    af:validatorタグとそのバインディングを削除し、後からADFモデル検証を追加する場合、関連付けられた属性のvalidatorプロパティにバインドされたbinding属性を含むコードにタグを再び追加する必要があります。

たとえば、SRDemoアプリケーションでの製品の簡単な入力フォームを作成するには、データ・コントロール・パレットからパラメータ・フォームとして製品コンストラクタ・メソッドをドロップします。例12-2に、JDeveloperにより作成されるJSFコードを示します。

例12-2 製品作成ページのJSFコード

<afh:body>
  <af:messages/>
  <h:form>
    <af:panelForm>
      <af:inputText value="#{bindings.productId.inputValue}"
                    label="#{bindings.productId.label}"
                    required="#{bindings.productId.mandatory}"
                    columns="#{bindings.productId.displayWidth}">
        <af:validator binding="#{bindings.productId.validator}"/>
        <f:convertNumber groupingUsed="false"
                         pattern="#{bindings.productId.format}"/>
      </af:inputText>
      <af:inputText value="#{bindings.name.inputValue}"
                    label="#{bindings.name.label}"
                    required="#{bindings.name.mandatory}"
                    columns="#{bindings.name.displayWidth}">
        <af:validator binding="#{bindings.name.validator}"/>
      </af:inputText>
      <af:inputText value="#{bindings.image.inputValue}"
                    label="#{bindings.image.label}"
                    required="#{bindings.image.mandatory}"
                    columns="#{bindings.image.displayWidth}">
        <af:validator binding="#{bindings.image.validator}"/>
      </af:inputText>
      <af:inputText value="#{bindings.description.inputValue}"
                    label="#{bindings.description.label}"
                    required="#{bindings.description.mandatory}"
                    columns="#{bindings.description.displayWidth}">
        <af:validator binding="#{bindings.description.validator}"/>
      </af:inputText>
    </af:panelForm>
    <af:commandButton actionListener="#{bindings.createProducts.execute}"
                      text="createProducts"
                      disabled="#{!bindings.createProducts.enabled}"/>
  </h:form>
</afh:body>

inputTextコンポーネントのrequired属性が、関連付けられたバインディングのmandatoryプロパティにバインドされていることに注意してください。EL式では、バインド先のオブジェクトの属性がnullになることができるかどうかが評価されます。

属性に対してADFモデル検証規則を作成した場合、JDeveloperによって属性バインディングにその検証規則が追加されます。すると、関連付けられた検証Beanが参照され、検証の実行に必要なプロパティ値が提供されます。例12-3に、Length検証規則をproductDescription属性に追加し、この属性の最大サイズを20に設定した場合に作成されるページ定義コードを示します。

例12-3 ページ定義の検証規則

<attributeValues id="description" IterBinding="variables"
                 ApplyValidation="true">
  <LengthValidationBean xmlns="http://xmlns.oracle.com/adfm/validation"
             OnAttribute="createProducts_description"
             DataType="CHARACTER" CompareType="LESSTHAN"
             ResId="description_Rule_0" Inverse="false"
             CompareLength="20"/>
  <AttrNames>
     <Item Value="createProducts_description"/>
  </AttrNames>
</attributeValues>

12.3.3 実行時に行われる処理

ユーザーがページを送信すると、ADF Facesのvalidate()メソッドにより最初に、送信された値でコンポーネントのrequired属性がtrueであるかどうかが確認されます。値がnullまたはゼロ長の文字列であった場合、コンポーネントは無効化されます。この時点では、クライアント側の検証が有効になっているかどうかによって実行される処理が異なります。

クライアント側の検証が有効になっている場合、エラー・メッセージがキューに挿入されます。他のバリデータがコンポーネントに登録されている場合、それらのバリデータはコールされず、現在のページが、エラー・メッセージを示すダイアログとともに再表示されます。


注意:

JSFの参照実装バリデータはクライアント側では実行されません。

例12-2では、image属性は必須ではありません。ただし、これ以外の列はmandatoryプロパティによって設定されているため、必須です。これは、Webページでアスタリスク・アイコンによって示されます。図12-2は、クライアント側検証が有効になっていない場合、製品IDのデータが入力されなかったときに表示されるエラー・メッセージを示しています。

図12-2 クライアント側での必須の値のエラー

ポップアップ・ダイアログにエラー・メッセージが表示されます。

送信された値がNULL以外の値か、少なくとも1つの文字からなる文字列値であった場合、検証プロセスは続行され、コンポーネントに設定されているすべてのバリデータが1つずつコールされます。コンポーネントのaf:validatorタグはバインディングのvalidatorプロパティにバインドされるため、モデルに設定されている検証ルーチンもこのときにアクセスされて実行されます。

続いて、プロセスは次のコンポーネントに進みます。すべての検証が正常に完了すると、モデル値更新フェーズが開始され、ローカル値を使用してモデルが更新されます。いずれかの検証が失敗すると、現在のページがエラー・ダイアログとともに再表示されます。

クライアント側検証が無効になっている場合は、すべての検証がサーバー側で実行されます。まず、検証処理フェーズでADF Faces検証が実行されます。エラーが発生した場合、値は無効化され、関連付けられたメッセージがFacesContext内のキューに追加されます。コンポーネントに対してすべての検証が実行された後、モデル・レイヤーに制御が渡されます。次に、モデル・レイヤーによってモデル更新検証フェーズが実行されます。検証処理フェーズと同様に、エラーが見つかった場合は値が無効化され、関連付けられたメッセージがFacesContext内のキューに追加されます(検証または変換以外のエラーが処理される方法の詳細は、12.8項「ADFアプリケーション内の例外の処理と表示」を参照してください)。

次に、ライフサイクルはレスポンスのレンダリング・フェーズにジャンプし、現在のページが再表示されます。ADF Facesにより自動的に、エラーを生成した入力コンポーネントのラベルの横にエラー・アイコンが表示され、関連付けられたメッセージが入力フィールドの下に表示されます。フィールドにヒントが関連付けられている場合は、ヒントの下にエラー・メッセージが表示されます。図12-3は、サーバー側の検証エラーを示しています。

図12-3 サーバー側の検証エラー

コンポーネントの下にエラー・メッセージが表示されます。

12.3.4 留意事項

1つのコンポーネントに対してrequired属性を設定し、かつバリデータも使用することができます。ただし、required属性をtrueに設定した場合、値がnullまたは長さが0の文字であると、コンポーネントは無効化され、そのコンポーネントに登録されている他のバリデータはコールされません。

コンポーネントが空でも有効になるケースが存在する場合は、この組合せでは問題があることがあります。たとえば、ページに「Cancel」ボタンが含まれている場合、ユーザーはデータを入力しなくても、そのボタンをクリックすればページを閉じることができるようにする必要があります。このようなケースを処理するには、「Cancel」ボタンのコンポーネントのimmediate属性をtrueに設定します。この属性により、リクエスト値の適用フェーズでこのアクションが実行されるため、アクションが実行されるたびに検証が省略されます。

12.4 カスタムJSF検証の作成

特定のビジネス・ニーズに合せて、独自の検証ロジックを追加できます。1つのページ上のコンポーネントに対してカスタム検証ロジックが必要な場合は、そのページのバッキングBeanに検証メソッドを作成できます。また、ページ上の他のフィールドにアクセスする際に検証が必要となる場合も、バッキングBeanに検証メソッドを作成すると効果的です。たとえば、個別の日付フィールド(月、日、年)それぞれが独自のバリデータを持つ場合は、ユーザーが「February 30, 2005」と入力してもエラーは表示されません。このような場合は、ページのバッキングBeanに日付全体を検証する検証メソッドを含めることができます。

アプリケーション内部の様々なページで再利用されるロジックを作成する必要がある場合や、クライアント側で検証が実行されるようにする場合は、JSFバリデータ・クラスを作成する必要があります。その後でADF Facesバージョンを作成すれば、クライアント側でバリデータが実行されます。

12.4.1 バッキングBeanの検証メソッドの作成方法

1つのページ上のコンポーネントに対してカスタム検証が必要な場合は、必要な検証を提供するメソッドをバッキングBean上に作成できます。

バッキングBeanの検証メソッドを追加する手順:

  1. 検証が必要となるコンポーネントをJSFページに挿入します。

  2. ビジュアル・エディタで、コンポーネントをダブルクリックして「バインドValidatorプロパティ」ダイアログを起動します。

  3. 「バインドValidatorプロパティ」ダイアログで、検証メソッドを収容するマネージドBeanを入力または選択するか、「新規」をクリックして新しいマネージドBeanを作成します。ロジックがすでに存在する場合は、提供されたデフォルトのメソッド・シグネチャを使用するか、既存のメソッドを選択します。

    ダイアログで「OK」をクリックすると、JDeveloperによりスケルトン・メソッドがコードに追加され、ソース・エディタ内にBeanが開きます。

  4. 必要な検証ロジックを追加します。このロジックには、適切な例外をスローするためのjavax.faces.validator.ValidatorExceptionと、対応するエラー・メッセージを生成するためのjavax.faces.application.FacesMessageを使用する必要があります。ValidatorインタフェースおよびFacesMessageの詳細は、javax.faces.validator.Validatorおよびjavax.faces.application.FacesMessageのJavadocを参照するか、http://java.sun.com/にアクセスしてください。

12.4.2 バッキングBeanの検証メソッドを作成するときに行われる処理

検証メソッドを作成すると、JDeveloperにより、選択したマネージドBeanにスケルトン・メソッドが追加されます。例12-4に、JDeveloperにより生成されるコードを示します。

例12-4 検証メソッドのマネージドBeanコード

public void inputText_validator(FacesContext facesContext,
                                 UIComponent uiComponent, Object object) {
        // Add event code here...
}

SRDemoアプリケーションの「SREdit」ページでは、入力された新しい日付が元の日付以降であることを確認する検証メソッドを使用しています。例12-5に、そのページのバッキングBeanの検証メソッドを示します。

例12-5 SREditの日付検証メソッド

public void assignedDateValidator(FacesContext facesContext,
                                  UIComponent uiComponent,
                                  Object newValue) {
    //The new value is passed into us
    Timestamp newAssignedDate = (Timestamp)newValue;

    //Get the start date for the SR which is already bound on this screen
    Timestamp requestDate =
        (Timestamp)ADFUtils.getBoundAttributeValue(getBindings(),
                                                   "requestDate");
    // Now compare and raise an error if the rule is broken
    if (newAssignedDate.compareTo(requestDate) < 0)
        {
          throw new ValidatorException(JSFUtils.getMessageFromBundle
           ("sredit.error.assignedBeforeStart", FacesMessage.SEVERITY_ERROR));
        }

 }

JDeveloperにより、EL式を使用してコンポーネントのvalidator属性がバッキングBeanの検証メソッドにバインドされます。例12-6に、JDeveloperによりSREditページに追加されるコードを示します。

例12-6 カスタム検証メソッドのJSFコード

<af:selectInputDate value="#{bindings.assignedDate.inputValue}"
                    label="#{bindngs.assignedDate.label}"
                    ...
                    validator="#{backing_SREdit.assignedDateValidator}">

ヒント:

JDeveloperにより、関連付けられたバインディングのvalidatorプロパティにバインドされるaf:validatorタグも追加されます。これにより、JSFライフサイクルで、関連付けられた属性に対して設定したADFモデル検証にアクセスできます。ADFモデル検証を設定しない場合は、このバインディングを削除してもかまいません。

入力コンポーネントを含むフォームが送信されると、validator属性がバインドされたメソッドが実行されます。

12.4.3 カスタムJSFバリデータの作成方法

カスタム・バリデータを作成するには、検証のビジネス・ロジックを記述する必要があります。これには、Validatorインタフェースのvalidateメソッドをオーバーライドするメソッドを含むValidator実装を作成してから、カスタム・バリデータをアプリケーションに登録します。また、バリデータのタグを作成したり、af:validatorタグを使用してカスタム・バリデータをそのタグのプロパティとしてネストすることもできます。

その後、クライアント側バージョンのバリデータを作成できます。ADF Facesのクライアント側検証は、サーバー側の標準的な検証と同じように機能します。ただし、クライアント側ではJavaScriptが使用されます。これは、JavaScriptのバリデータ・オブジェクトではValidatorExceptionsをスローでき、validate()メソッドをサポートしているためです。


注意:

JavaScript form.submit()関数がコールされた場合、ADF Facesのクライアント側検証のサポートは省略されます。ADF Facesでは、かわりに使用できるsubmitForm()メソッドが用意されています。あるいは、ADF Faces入力コンポーネントのautoSubmit属性を使用することもできます。

カスタムJSFバリデータを作成する手順:

  1. javax.faces.validator.Validatorインタフェースを実装するJavaクラスを作成します。実装には、引数なしのpublicコンストラクタ、属性に対するアクセッサ・メソッドのセット、およびValidatorインタフェースのvalidateメソッドをオーバーライドするvalidateメソッドを含める必要があります。

    あるいは、formatPatterns属性を設定するためのアクセッサ・メソッドを持つjavax.faces.FormatValidatorインタフェースを実装することもできます。この属性は、入力コンポーネントに入力されたデータの受入れな可能パターンを指定します。たとえば、クレジット・カード番号のパターンを検証する場合、使用可能なパターンに対してformatPatterns属性を作成します。実装には、コンストラクタ、属性に対するアクセッサ・メソッドのセット、およびValidatorインタフェースのvalidateメソッドをオーバーライドするvalidateメソッドを含める必要があります。

    どちらのインタフェースでも、validateメソッドはFacesContextインスタンス、UIコンポーネント、および検証の対象となるデータを取ります。たとえば、次のように入力します。

    public void validate(FacesContext facesContext,
                         UIComponent uiComponent,
                         Object object) {
    ..
    }
    

    これらのクラスの詳細は、Javadocを参照するか、http://java.sun.com/にアクセスしてください。

  2. 必要な検証ロジックを追加します。このロジックには、適切な例外をスローするためのjavax.faces.validator.ValidatorExceptionと、対応するエラー・メッセージを生成するためのjavax.faces.application.FacesMessageを使用する必要があります。ValidatorインタフェースおよびFacesMessageの詳細は、javax.faces.validator.Validatorおよびjavax.faces.application.FacesMessageのJavadocを参照するか、http://java.sun.com/にアクセスしてください。


    注意:

    ページ作成者がそのページから属性を構成できるようにするには、バリデータのタグを作成する必要があります。詳細は、手順5を参照してください。ページ上に属性を構成しない場合は、この実装クラス内に属性を構成する必要があります。

  3. アプリケーションでクライアント側の状態を保存する場合は、カスタム・バリデータ実装にSerializableインタフェースを実装するか、StateHolderインタフェースとStateHoldersaveState(FacesContext)メソッドおよびrestoreState(FacesContext, Object)メソッドを実装します。詳細は、javax.faces.componentパッケージのStateHolderインタフェースのJavadocを参照してください。

  4. faces-config.xmlファイル内でバリデータを登録します。

    • faces-config.xmlファイルを開き、エディタ・ウィンドウ内で「概要」タブを選択します。faces-config.xmlファイルは<View_Project>/WEB-INFディレクトリ内にあります。

    • ウィンドウ内で「バリデータ」を選択し、「新規」をクリックします。「ヘルプ」をクリックするか、[F1]を押すと、バリデータの登録に関する追加のヘルプが表示されます。

  5. オプションで、クラスの属性を設定するバリデータのタグを作成します。タグを作成するには、アプリケーションのタグ・ライブラリ定義ファイル(TLD)内にタグのエントリを追加します。この手順は次のとおりです。

    • アプリケーションのTLDを開くか、作成します。TLDの作成方法の詳細は、http://java.sun.com/にアクセスしてください。

    • faces-config.xmlファイル内に登録されているバリデータIDとクラスを定義します。

    • その構成ファイル内に登録されているプロパティまたは属性を定義します。


    注意:

    バリデータのタグを作成しない場合は、Validator実装内に属性を構成する必要があります。

クライアント側バージョンのバリデータを作成する手順:

  1. JavaScriptバージョンのバリデータを作成し、関連する情報をコンストラクタに渡します。

  2. 2つのメソッドを持つインタフェースoracle.adf.view.faces.validator.ClientValidatorを実装します。最初のメソッドは、JavaScriptのValidatorオブジェクトの実装を返すgetClientScript()です。2つ目のメソッドは、バリデータのインスタンスのインスタンス化に使用されるJavaScriptコンストラクタを返すgetClientValidation()です。

クライアント側検証をバリデータ実装に追加する方法の完全な例については、「Development Guidelines for Oracle ADF Faces Applications」の「Client-Side Converters and Validators」を参照してください。

JSFページでカスタム・バリデータを使用する手順:

  • JSFページでタグを持つカスタム・バリデータを使用するには、コンポーネントのタグ内部にそのバリデータを手動でネストする必要があります。

    例12-7に、inputTextコンポーネント内にネストされたカスタム・バリデータを示します。faces-config.xmlファイル内に宣言されていたバリデータのプロパティの値を提供するためにタグ属性が使用されていることに注意してください。

例12-7 JSFページ上のカスタム・バリデータ・タグ

<h:inputText id="empnumber" required="true">
  <hdemo:emValidator emPatterns="9999|9 9 9 9|9-9-9-9" />
</h:inputText>

カスタム・タグを持たないカスタム・バリデータを使用する手順:

カスタム・タグを持たないカスタム・バリデータを使用するには、af:validatorタグ内部に(faces-config.xmlファイル内に構成されている)バリデータのIDをネストする必要があります。

  1. 構造ウィンドウから、検証を追加する入力コンポーネントを右クリックし、「<コンポーネント>の中に挿入」「ADF Faces Core」「バリデータ」を選択します。

  2. ドロップダウン・リストからバリデータのIDを選択し、「OK」をクリックします。

    JDeveloperにより、バリデータIDをvalidatorタグのプロパティにするコードがJSFページに挿入されます。

例12-8に、JSFページでのaf:validatorタグを使用するバリデータのコードを示します。

例12-8 JSFページのコンポーネント内でネストされたカスタム・バリデータ

<af:inputText id="empnumber" required="true">
  <af:validator validatorID="emValidator"/>
</af:inputText>

12.4.4 カスタムJSFバリデータを使用するときに行われる処理

カスタムJSFバリデータを使用すると、アプリケーションは、カスタム・タグまたはaf:validatorタグ内で参照されているバリデータ・クラスにアクセスし、validateメソッドを実行します。このメソッドは、現在のFacesContext内のコンポーネントからデータにアクセスし、そのデータに対してロジックを実行してデータが有効かどうかを判断します。バリデータが属性を持つ場合、それらの属性もアクセスされ、検証ルーチンで使用されます。標準的なバリデータと同様に、カスタム検証が失敗した場合は、関連付けられたメッセージがFacesContext内のメッセージ・キューに挿入されます。

12.5 変換の追加

Webアプリケーションは、モデル・レイヤー内に多くのタイプのデータ(intlongdateなど)を格納できます。ただし、クライアント・ブラウザで表示されるとき、ユーザー・インタフェースには、ユーザーが読取りまたは変更できるような形式でデータが表示される必要があります。たとえば、フォーム内の「date」フィールドでは、java.util.Dateオブジェクトがmm/dd/yyyyという書式パターンのテキスト文字列として表示されるようにします。ユーザーが「date」フィールドを編集してフォームを送信した場合、文字列は、アプリケーションで必要とされるタイプに変換される必要があります。変換後のデータは、規則および条件に対して検証されます。

コンバータが存在するタイプの属性をドロップしてinputTextコンポーネントを作成すると、JDeveloperにより、そのコンバータのタグが入力コンポーネントの子として自動的に追加されます。このタグによりコンバータが起動され、ユーザーが入力したStringは、オブジェクトにより期待されるタイプに再び変換されます。

JSF標準コンバータは、Strings型と単純なデータ型の間の変換を扱い、javax.faces.convert.Converterインタフェースを実装します。次のJSF標準コンバータ・クラスが提供されています。

表12-5に、ADF Facesにより提供されるコンバータを示します。

表12-5 ADF Facesのコンバータ

コンバータ タグ名 説明

ColorConverter

af:convertColor

java.lang.Stringオブジェクトをjava.awt.Colorオブジェクトに変換する。カラー・パターンのセットはコンバータの属性として指定する。

DateTimeConverter

af:convertDateTime

java.lang.Stringオブジェクトをjava.util.Dateオブジェクトに変換する。日付のパターンとスタイルは、コンバータの属性として指定する。

NumberConverter

af:convertNumber

java.lang.Stringオブジェクトをjava.lang.Numberオブジェクトに変換する。数値のパターンとタイプは、コンバータの属性として指定する。


バリデータの場合と同様に、ADF Facesコンバータも、adf-faces-config.xmlファイル内でクライアント側検証が明示的に無効化されていないかぎり、クライアント側で実行されます。


注意:

JSFの参照実装コンバータはクライアント側では実行されません。

ADF Facesでは、色、日付および数値に対するJavaScript対応コンバータのみでなく、次のいずれかのJavaタイプにバインドされる入力テキスト・フィールドに対するJavaScript対応コンバータも提供されます。

表12-5にリストされているコンバータとは異なり、JavaScript対応コンバータは、必要に応じて自動的に使用されます。これらのコンバータには、コンポーネント内にネストできるタグは関連付けられていません。

12.5.1 コンバータの使用方法

ADF Facesコンバータが存在する属性をデータ・コントロール・パレットからドロップすると、JDeveloperにより、自動的にそのコンバータが入力コンポーネントに追加されます。手動でコンバータを挿入することもできます。

タグを持つADF Facesコンバータを追加する手順:

  1. 構造ウィンドウで、コンバータを追加するコンポーネントを右クリックします。

  2. ポップアップ・メニューで「<UIコンポーネント>の中に挿入」「ADF Faces Core」を選択してADF Facesコンバータを挿入するか、「JSF Core」を選択してJSFコンバータを挿入します。

  3. コンバータ・タグ(ConvertDateTimeなど)を選択します。

  4. プロパティ・インスペクタで、属性の値(変換エラーのメッセージなど)を設定します。追加のヘルプは、属性を右クリックして「ヘルプ」を選択すると表示されます。

    ADF Facesでは、変換エラー・メッセージの詳細部分をカスタマイズできます。XxxMessageDetail属性の値を設定することで(XxxconvertDateMessageDetailなどの変換エラー・タイプ)、変換が失敗したときにデフォルト・メッセージのかわりにカスタム・メッセージを表示できます。

12.5.2 データ・コントロール・パレットを使用して入力フィールドを作成するときに行われる処理

データ・コントロール・パレットを使用して、コンバータでサポートされているタイプの入力フィールドを作成すると、JDeveloperにより、次のようにしてADF Faces変換コードがJSFページに自動的に提供されます。

  • af:messagesタグがbodyタグの子として追加されます。デフォルトでは、globalOnly属性はfalseに設定され、message属性とtext属性は設定されません。詳細は、12.7項「エラー・メッセージの表示」を参照してください。

  • コンバータ・タグが入力コンポーネントの子として追加されます。

    デフォルトでは、pattern属性は、関連付けられたバインディングのformatプロパティにバインドされます。formatプロパティにより、Stringがフォーマットされる方法が決まります。たとえば、convertNumberコンバータの場合、このプロパティにより10進値が使用されるかどうかが決まります。このバインディングは、データ・コントロールそのものに設定されているformatプロパティに評価されます。

たとえば、findAllProductsメソッドからinputTextコンポーネントとしてのprodId属性をドロップすると、JDeveloperにより自動的にconvertNumberコンバータが入力コンポーネントの子として追加されます(例12-9を参照)。

例12-9 JSFページ内のコンバータ・タグ

<af:inputText value="#{bindings.productId.inputValue}"
              label="#{bindings.productId.label}"
              required="#{bindings.productId.mandatory}"
              columns="#{bindings.productId.displayWidth}">
  <af:validator binding="#{bindings.productId.validator}"/>
  <f:convertNumber groupingUsed="false"
                   pattern="#{bindings.productId.format}"/>
</af:inputText>

12.5.3 実行時に行われる処理

コンバータが含まれたページをユーザーが送信すると、ADF Facesのvalidate()メソッドによりコンバータのgetAsObject()メソッドがコールされ、文字列値が必要なオブジェクト・タイプに変換されます。コンバータがアタッチされておらず、コンポーネントがモデル内のbeanプロパティにバインドされている場合、JSFでは自動的に、beanプロパティと同じデータ型を持つコンバータが使用されます。変換が失敗した場合、送信された値に無効のマークが付けられ、JSFにより、FacesContextで保持されているキューにエラー・メッセージが追加されます。変換が正常に完了し、コンポーネントにアタッチされているバリデータがない場合、変換された値はローカル値として格納され、後でモデルの更新に使用されます。

12.6 カスタムJSFコンバータの作成

特定のビジネス・ニーズに合せて、独自のコンバータを作成できます。カスタムJSFバリデータを作成する場合と同様に、サーバー側で実行されるカスタムJSFコンバータを作成してから、クライアント側で実行できるJavaScriptバージョンを作成できます。ただし、カスタム・バリデータを作成する場合とは異なり、作成できるのはコンバータ・クラスのみです。変換を提供するためにメソッドをバッキングBeanに追加することはできません。

12.6.1 カスタムJSFコンバータの作成方法

カスタム・コンバータを作成するには、変換のビジネス・ロジックを記述する必要があります。これには、ConverterインタフェースのgetAsObjectおよびgetAsStringメソッドをオーバーライドするメソッドを含むConverterインタフェースの実装を作成してから、カスタム・コンバータをアプリケーションに登録します。次にf:converterタグを使用し、そのタグのプロパティとしてカスタム・コンバータをネストします。あるいは、そのコンバータにバインドする入力コンポーネントのconverter属性を使用することもできます。

また、クライアント側バージョンをコンバータを作成することもできます。ADF Facesのクライアント側コンバータは、サーバー側の標準的なJSF変換と同じように機能します。ただし、クライアント側ではJavaScriptが使用されます。これは、JavaScriptのコンバータ・オブジェクトではConverterExceptionsをスローでき、getAsObjectメソッドおよびgetAsStringメソッドをサポートしているためです。


注意:

JavaScript form.submit()関数がコールされた場合、ADF Facesのクライアント側変換のサポートは省略されます。ADF Facesでは、かわりに使用できるsubmitForm()メソッドが用意されています。あるいは、ADF Faces入力コンポーネントのautoSubmit属性を使用することもできます。

カスタムJSFコンバータを作成する手順:

  1. javax.faces.converter.Converterインタフェースを実装するJavaクラスを作成します。実装には、引数なしのpublicコンストラクタ、属性に対するアクセッサ・メソッドのセット、および(ConverterインタフェースのgetAsObjectメソッドとgetAsStringメソッドをオーバーライドする)getAsObjectメソッドとgetAsStringメソッドを含める必要があります。

    getAsObjectメソッドは、FacesContextインスタンス、UIコンポーネント、および指定されたオブジェクトに変換されるStringを取ります。たとえば、次のように入力します。

    public Object getAsObject(FacesContext context,
                            UIComponent component,
                            java.lang.String value){
    ..
    }
    

    getAsStringメソッドは、FacesContextインスタンス、UIコンポーネント、およびStringに変換されるオブジェクトを取ります。たとえば、次のように入力します。

    public String getAsString(FacesContext context,
                            UIComponent component,
                            Object value){
    ..
    }
    

    これらのクラスの詳細は、Javadocを参照するか、http://java.sun.com/にアクセスしてください。

  2. 必要な変換ロジックを追加します。このロジックには、適切な例外をスローするためのjavax.faces.converter.ConverterExceptionと、対応するエラー・メッセージを生成するためのjavax.faces.application.FacesMessageを使用する必要があります。ConverterインタフェースおよびFacesMessageの詳細は、javax.faces.converter.Converterおよびjavax.faces.application.FacesMessageのJavadocを参照するか、http://java.sun.com/にアクセスしてください。

  3. アプリケーションでクライアント側の状態を保存する場合は、カスタム・コンバータ実装にSerializableインタフェースを実装するか、StateHolderインタフェースとStateHoldersaveState(FacesContext)メソッドおよびrestoreState(FacesContext, Object)メソッドを実装します。詳細は、javax.faces.componentパッケージのStateHolderインタフェースのJavadocを参照してください。

  4. faces-config.xmlファイル内でコンバータを登録します。

    • faces-config.xmlファイルを開き、エディタ・ウィンドウ内で「概要」タブを選択します。faces-config.xmlファイルは<View_Project>/WEB-INFディレクトリ内にあります。

    • ウィンドウ内で「コンバータ」を選択し、「新規」をクリックします。「ヘルプ」をクリックするか、[F1]を押すと、コンバータの登録に関する追加のヘルプが表示されます。

クライアント側バージョンのコンバータを作成する手順:

  1. JavaScriptバージョンのコンバータを作成し、関連する情報をコンストラクタに渡します。

  2. 2つのメソッドを持つインタフェースoracle.adf.view.faces.converter.ClientConverterを実装します。最初のメソッドは、JavaScriptのConverterオブジェクトの実装を返すgetClientScript()です。2つ目のメソッドは、コンバータのインスタンスのインスタンス化に使用されるJavaScriptコンストラクタを返すgetClientConversion()です。

クライアント側変換をコンバータ実装に追加する方法の完全な例については、「Development Guidelines for Oracle ADF Faces Applications」の「Client-Side Converters and Validators」を参照してください。

JSFページでカスタム・コンバータを使用する手順:

  • コンバータ・クラスを入力コンポーネントのconverter属性にバインドします。

例12-10に、inputTextコンポーネントのconverter属性により参照されるカスタム・コンバータを示します。

例12-10 JSFページ上のカスタム・コンバータ

<af:inputText value="#{bindings.name.inputValue}"
              label="#{bindings.name.label}"
              required="#{bindings.name.mandatory}"
              columns="#{bindings.name.displayWidth}"
              converter="srdemo.MyConverter">
</af:inputText>

注意:

カスタム・コンバータがアプリケーション内の特定のデータ型のクラスの下に登録された場合、コンポーネントの値がカスタム・コンバータ・オブジェクトと同じタイプの値バインディングを参照するたびに、JSFでは自動的にそのクラスのコンバータを使用してデータが変換されます。その場合、次のコードSnippetに示すように、converter属性を使用してコンポーネントのカスタム・コンバータを登録する必要はありません。
<h:inputText value="#{myBean.myProperty}"/>

ここで、myPropertyの型はカスタム・コンバータと同じです。


12.6.2 カスタム・コンバータを使用するときに行われる処理

カスタム・コンバータを使用すると、アプリケーションは、converter属性内で参照されているコンバータ・クラスにアクセスし、適宜getAsObjectまたはgetAsStringメソッドを実行します。これらのメソッドは、現在のFacesContext内のコンポーネントからデータにアクセスし、変換ロジックを実行します。

12.7 エラー・メッセージの表示

デフォルトでは、ADF Facesの検証と変換はクライアント側で実行されます。コンポーネントのデータ検証が失敗すると、そのコンポーネントのエラー・メッセージが示されたアラート・ダイアログが表示されます。クライアント側のエラー・メッセージをこのように表示するために、追加の作業を行う必要はありません。図12-4は、required属性がtrueに設定された入力コンポーネントに対してデータが入力されなかった場合に表示されるメッセージを示しています。

図12-4 クライアント側のエラー・メッセージ

エラー・メッセージがポップアップ・ダイアログ内に表示されます。

ADF Facesでは、検証または変換が失敗したときに表示されるメッセージのデフォルト・テキストが用意されています。デフォルト・メッセージを独自のメッセージで置き換えるには、バリデータまたはコンバータのxxxMessageDetail属性(convertDateMessageDetailnotInRangeDetailMessageなど)にテキストを設定するか、EL式を使用してこれらの属性をリソース・バンドルにバインドします。リソース・バンドルの使用方法の詳細は、14.4.1項「アプリケーションを国際化する方法」を参照してください。

データ・コントロール・パレットを使用して入力コンポーネントを作成すると、JDeveloperによりページの最上部にaf:messagesタグが挿入されます。このタグにより、サーバー側で行われた検証に対するキュー内のすべてのエラー・メッセージを、色でオフセットされたボックス内に表示できます。ADF Facesのクライアント側検証を無効にするように選択した場合、これらのエラー・メッセージがADFモデルのエラー・メッセージとともに表示されます。ADFモデルのメッセージが最初に表示されます。メッセージは、af:messagesタグ内と、関連付けられたコンポーネントの傍に表示されます。

図12-5は、ADFモデルの検証規則に対する、説明が長すぎるというエラー・メッセージを示しています。同時に、ADF Facesコンポーネントのrequired属性の違反に対するエラー・メッセージも表示されています。

図12-5 ADF Facesのmessagesタグを使用したサーバー側エラー・メッセージの表示

メッセージがページの最上部とコンポーネントの下に表示されます。

12.7.1 ページ上にサーバー側エラー・メッセージを表示する方法

af:messagesタグを使用して、ページ最上部のボックス内にサーバー側のエラー・メッセージを表示できます。データ・コントロール・パレットから入力コンポーネントとしての項目をページ上にドロップすると、JDeveloperにより自動的にこのタグが追加されます。

エラー・ボックス内にエラー・メッセージを表示する手順:

  1. 構造ウィンドウでaf:messagesタグを選択します。

    このタグは、データ・コントロール・パレットから入力ウィジェットをドロップすると自動的に作成されます。ただし、タグを挿入する必要がある場合は、afh:bodyタグ内に次のコードを挿入するだけです。

    <afh:body>
      <af:messages globalOnly="false" />
      ...
    </afh:body>
    
  2. プロパティ・インスペクタで次の属性を設定します。

    • globalOnly: デフォルトでは、ADF Facesによってグローバル・メッセージ(コンポーネントに関連付けられていないメッセージ)が表示され、続いて個別のコンポーネント・メッセージが表示されます。ボックス内にグローバル・メッセージのみを表示する場合は、属性をtrueに設定します。その場合も、関連付けられたコンポーネントの傍にコンポーネント・メッセージが表示されます。

    • message: メッセージ・ボックス・タイトルのすぐ下、個別メッセージのリストの上に表示されるメイン・メッセージ・テキスト。

    • text: メッセージ・ボックスのデフォルト・タイトルをオーバーライドするテキスト。

  3. クライアント側の検証が無効になっていることを確認します。クライアント側の検証を無効化していない場合、サーバー側の検証は実行されないため、アラート・ダイアログにADF Faces検証エラーがあるかどうかが表示されます。


    ヒント:

    クライアント側検証を無効化するには、adf-faces-config.xml内に<client-validation-disabled>要素を追加し、それをtrueに設定します。

12.7.2 エラー・メッセージの表示を選択するときに行われる処理

ADF Faces入力コンポーネントの変換または検証エラーが発生すると、コンポーネントはFacesMessageオブジェクトを作成し、FacesContextインスタンスのメッセージ・キューにそれを追加します。レスポンスのレンダリング・フェーズで、ADF Faces入力コンポーネントの組込みメッセージ表示属性を使用して、バリデータまたはコンバータに関連付けられたメッセージが表示されます。この属性により、コンポーネントの横に詳細なエラー・メッセージが表示されます。メッセージはオプションのaf:messagesタグによっても表示されます。このタグを使用すると、メッセージ・ボックス内にすべてのサマリー・メッセージが表示されます。

12.8 ADFアプリケーション内の例外の処理と表示

ADFアプリケーションの一部によりスローされた例外も、JSFページ上で処理および表示されます。デフォルトでは、アプリケーション内でスローされた例外はすべてバインディング・コンテナにより捕捉されます。例外が発生すると、バインディング・コンテナは例外をアプリケーションのアクティブなエラー・ハンドラ(デフォルトではDCErrorHandlerImplクラス)にルーティングします。このクラスのReportException(BindingContainer, Exception)メソッドは、例外を処理のためにバインディング・コンテナに渡します。バインディング・コンテナは、その例外をキャッシュ内の例外リストに挿入することにより処理します。ページ・ライフサイクル中(たとえば、検証中)にページ上に例外が見つかると、それらの例外もバインディング・コンテナにより捕捉されてキャッシュされ、FacesContextに追加されます。

レンダリングの準備フェーズで、ADFライフサイクルはreportErrors(context)メソッドを実行します。このメソッドの実装の方法は、表示テクノロジごとに異なります。デフォルトでは、FacesPageLifecycleクラスのreportErrorsメソッドが次の処理を実行します。

このデフォルトのフレームワーク動作は、カスタマイズできます。たとえば、例外用のカスタム・エラー・ハンドラを作成したり、ライフサイクルで例外がレポートされる方法を変更できます。また、1つのページで例外が処理される方法をカスタマイズすることもできます。

12.8.1 例外処理の変更方法

デフォルトの例外処理を変更するには、デフォルトのエラー・ハンドラ(DCErrorHandlerImpl)を拡張します。また、モデル準備フェーズで新しいエラー・ハンドラをコールするカスタムADFライフサイクル・クラスを作成する必要もあります。

また、カスタムADFライフサイクル・クラスを作成して、reportErrorsメソッドをオーバーライドすることにより、ライフサイクルでエラーがレポートされる方法を変更することもできます。

1つのページに対して例外が作成される方法を変更するだけの場合は、そのページにのみライフサイクル・クラスを作成できます。

カスタム・エラー・ハンドラを作成する手順:

  1. DCErrorHandlerImplクラスを拡張するクラスを作成します。

  2. 新規クラスで、public void reportException(DCBindingContainer, Exception)メソッドをオーバーライドします。

    例12-11に、SRDemoアプリケーションでエラーの処理に使用されるSRDemoErrorHandlerクラスを示します。

    例12-11 SRDemoErrorHandlerクラス

    public class SRDemoErrorHandler extends DCErrorHandlerImpl{
      /**
       * Constructor for custom error handler.
       *
       * @param setToThrow should exceptions throw or not
       */
      public SRDemoErrorHandler(boolean setToThrow) {
        super(setToThrow);
      }
      public void reportException(DCBindingContainer bc, Exception ex) {
         //Force JboException's reported to the binding layer to avoid
         //printing out the JBO-XXXXX product prefix and code.
        disableAppendCodes(ex);
        super.reportException(bc, ex);
      }
    
      private void disableAppendCodes(Exception ex) {
        if (ex instanceof JboException) {
          JboException jboEx = (JboException) ex;
          jboEx.setAppendCodes(false);
          Object[] detailExceptions = jboEx.getDetails();
          if ((detailExceptions != null) && (detailExceptions.length > 0)) {
            for (int z = 0, numEx = detailExceptions.length; z < numEx; z++) {
              disableAppendCodes((Exception) detailExceptions[z]);
            }
          }
        }
      }
    }
    
  3. エラー・ハンドラをグローバルにオーバーライドします。これには、FacesPageLifecycleを拡張するカスタム・ページ・ライフサイクル・クラスを作成する必要があります。このクラス内で、エラー・ハンドラを設定するpublic void prepareModel(LifecycleContext)メソッドをオーバーライドします。エラー・ハンドラをカスタム・ハンドラに設定するには、メソッドを使用して、カスタム・エラー・ハンドラがバインディング・コンテキスト内で現行エラー・ハンドラになっているかどうかを確認します。現行エラー・ハンドラになっていない場合は、現行エラー・ハンドラに設定します。(デフォルトでは、ADFBindingFilterは常にエラー・ハンドラをDCErrorHandlerImplに設定するため、メソッドによりそれをカスタム・エラー・ハンドラに戻す必要があります。)次にsuper.prepareModelをコールする必要があります。

    例12-12に、FacesPageLifecycleクラスを拡張するframeworkExt.SRDemoPageLifecycleクラスからのprepareModelメソッドを示します。このメソッドは、エラー・ハンドラがSRDemoErrorHandlerのインスタンスであるかどうかを確認し、そうでない場合にはエラー・ハンドラを新規のエラー・ハンドラに設定することに注意してください。

    例12-12 preparedModelメソッド

    public void prepareModel(LifecycleContext ctx) {
        if (!(ctx.getBindingContext().getErrorHandler() instanceof
              SRDemoErrorHandler)) {
            ctx.getBindingContext().setErrorHandler(new SRDemoErrorHandler(true));
        }
        super.prepareModel(ctx);
    }
    
  4. 今度は、カスタム・ライフサイクルを返す新規のフェーズ・リスナーを作成する必要があります。手順については、この後の「新規のフェーズ・リスナーを作成する手順:」を参照してください。

ライフサイクルでエラーがレポートされる方法をカスタマイズする手順:

  1. FacesPageLifecycleを拡張するカスタム・ページ・ライフサイクル・クラスを作成します。

  2. public void reportErrors(PageLifecycleContext)メソッドをオーバーライドして、エラー・メッセージの表示をカスタマイズします。

    例12-13に、エラーがレポートされる方法を変更するための、FacesPageLifecycleクラスを拡張するframeworkExt.SRDemoPageLifecycleクラス内のreportErrorsメソッドと関連メソッドを示します。

    例12-13 SRDemoPageLifecycleクラス内のreportErrorsメソッド

    public void reportErrors(PageLifecycleContext ctx) {
        DCBindingContainer bc = (DCBindingContainer)ctx.getBindingContainer();
        if (bc != null) {
            List<Exception> exceptions = bc.getExceptionsList();
            if (exceptions != null) {
                Locale userLocale =
                    ctx.getBindingContext().getLocaleContext().getLocale();
                /*
                 * Iterate over the top-level exceptions in the exceptions list and
                 * call addError() to add each one to the Faces errors list
                 * in an appropriate way.
                 */
                for (Exception exception: exceptions) {
                    try {
                        translateExceptionToFacesErrors(exception, userLocale,
                                                        bc);
                    } catch (KnowErrorStopException stop) {
                        FacesContext fctx = FacesContext.getCurrentInstance();
                        fctx.addMessage(null,
                                        JSFUtils.getMessageFromBundle
                                        (stop.getMessage(),
                                         FacesMessage.SEVERITY_FATAL));
                        break;
                    }
                }
            }
        }
    }
    
    protected void translateExceptionToFacesErrors(Exception ex, Locale locale,
                                                       BindingContainer bc) throws
                                                  KnowErrorStopException {
        List globalErrors = new ArrayList();
        Map attributeErrors = new HashMap();
        processException(ex, globalErrors, attributeErrors, null, locale);
        int numGlob = globalErrors.size();
        int numAttr = attributeErrors.size();
        if (numGlob > 0) {
            for (int z = 0; z < numGlob; z++) {
                String msg = (String)globalErrors.get(z);
                if (msg != null) {
                    JSFUtils.addFacesErrorMessage(msg);
                }
            }
        }
        if (numAttr > 0) {
            Iterator i = attributeErrors.keySet().iterator();
            while (i.hasNext()) {
                String attrNameKey = (String)i.next();
                /*
                 * Only add the error to show to the user if it was related
                 * to a field they can see on the screen. We accomplish this
                 * by checking whether there is a control binding in the current
                 * binding container by the same name as the attribute with
                 * the related exception that was reported.
                 */
                ControlBinding cb =
                    ADFUtils.findControlBinding(bc, attrNameKey);
                if (cb != null) {
                    String msg = (String)attributeErrors.get(attrNameKey);
                    if (cb instanceof JUCtrlAttrsBinding) {
                        attrNameKey = ((JUCtrlAttrsBinding)cb).getLabel();
                    }
                    JSFUtils.addFacesErrorMessage(attrNameKey, msg);
                }
            }
        }
    }
    
    /**
    * Populate the list of global errors and attribute errors by
    * processing the exception passed in, and recursively processing
    * the detail exceptions wrapped inside of any oracle.jbo.JboException
    * instances.
    *
    * If the error is an attribute-level validation error, we can tell
    * because it should be an instanceof oracle.jbo.AttrValException
    * For each attribute-level error, we retrieve the name of the attribute
    * in error by calling an appropriate getter method on the exception
    * object which exposes this information to us. Since attribute-level
    * errors could be wrapping other more specific attribute-level errors
    * that were the real cause (especially due to Bundled Exception Mode).
    * We continue to recurse the detail exceptions and we only consider
    * relevant to report the exception that is the most deeply nested, since
    * it will have the most specific error message for the user. If multiple
    * exceptions are reported for the same attribute, we simplify the error
    * reporting by only reporting the first one and ignoring the others.
    * An example of this might be that the user has provided a key value
    * that is a duplicate of an existing value, but also since the attribute
    * set failed due to that reason, a subsequent check for mandatory attribute
    * ALSO raised an error about the attribute's still being null.
    *
    * If it's not an attribute-level error, we consider it a global error
    * and report each one.
    *
    * @param ex the exception to be analyzed
    * @param globalErrs list of global errors to populate
    * @param attrErrs map of attrib-level errors to populate, keyed by attr name
    * @param attrName attribute name of wrapping exception (if any)
    * @param locale the user's preferred locale as determined by the browser
    */
    private void processException(Exception ex, List globalErrs, Map attrErrs,
                                  String attrName,
                                  Locale locale) throws KnowErrorStopException {
        /*
         * Process the exceptions
         * Start with some special cases that are known bad situations where we
         * need to format some useful advice rather than just parroting the
         * exception text
         */
        if (ex instanceof EJBException) {
            String msg = ex.getLocalizedMessage();
            if (msg == null) {
                msg = firstLineOfStackTrace(ex, true);
            }
            Exception causeEx = ((EJBException)ex).getCausedByException();
            if (causeEx instanceof TopLinkException) {
                int toplinkErrorCode =
                    ((TopLinkException)causeEx).getErrorCode();
                switch (toplinkErrorCode) {
                case 7060:
                    {
                        throw new KnowErrorStopException("srdemo.topLinkError.7060");
                    }
                case 4002:
                    {
                        throw new KnowErrorStopException("srdemo.topLinkError.4002");
                    }
                }
            }
            globalErrs.add(msg);
        } else if (ex instanceof AdapterException){
            AdapterException causeEx = ((AdapterException)ex);
    
            int err = Integer.parseInt( causeEx.getErrorCode());
            switch (err){
                case 40010:{
                    throw new KnowErrorStopException("srdemo.urlDCError.40010");
                }
                case 29000:{
                    throw new KnowErrorStopException("srdemo.urlDCError.29000");
                }
                default:{
                    throw new KnowErrorStopException("srdemo.urlDCError.other");
                }
            }
    
        } else if (!(ex instanceof JboException)) {
            String msg = ex.getLocalizedMessage();
            if (msg == null) {
                msg = firstLineOfStackTrace(ex, true);
            }
            globalErrs.add(msg);
            /*
           * If this was an unexpected error, print out stack trace
           */
            reportUnexpectedException(ex);
            return;
        }
        if (ex instanceof AttrValException) {
            AttrValException ave = (AttrValException)ex;
            attrName = ave.getAttrName();
            Object obj = attrErrs.get(attrName);
            /*
             * If we haven't already recorded an error for this attribute
             * and if it's a leaf detail, then log it as the first error for
             * this attribute. If there are details, then we'll recurse
             * into the details below. But, in the meantime we've recorded
             * What attribute had the validation error in the attrName
             */
            Object[] details = ave.getDetails();
            if (obj != null) {
                /*
                 * We've already logged an attribute-validation error for this
                 * attribute, so ignore subsequent attribute-level errors
                 * for the same attribute. Note that this is not ignoring
                 * NESTED errors of an attribute-level exception, just the
                 * second and subsequent PEER errors of the first attribute-level
                 * error. This means the user might receive errors on several
                 * different attributes, but for each attribute we're choosing
                 * to tell them about just the first problem to fix.
                 */
                return;
            } else {
                /*
                 * If there aren't any further, nested details, then log first error
                 */
                if ((details == null) || (details.length == 0)) {
                    attrErrs.put(attrName, ave.getLocalizedMessage(locale));
                }
            }
        }
        JboException jboex = (JboException)ex;
        /*
         * It is a JboException so recurse into the exception tree
         */
        Object[] details = jboex.getDetails();
        /*
         * We only want to report Errors for the "leaf" exceptions
         * So if there are details, then don't add an errors to the lists
         */
        if ((details != null) && (details.length > 0)) {
            for (int j = 0, count = details.length; j < count; j++) {
                processException((Exception)details[j], globalErrs, attrErrs,
                                 attrName, locale);
            }
        } else {
            /*
             * Add a new Error to the collection
             */
            if (attrName == null) {
                String errorCode = jboex.getErrorCode();
                globalErrs.add(jboex.getLocalizedMessage(locale));
            } else {
                attrErrs.put(attrName, jboex.getLocalizedMessage(locale));
            }
            if (!(jboex instanceof ValidationException)) {
                reportUnexpectedException(jboex);
            }
        }
    }
    /**
    * Prints the stack trace for an unexpected exception to standard out.
    *
    * @param ex The unexpected exception to report.
    */
    protected void reportUnexpectedException(Exception ex) {
        ex.printStackTrace();
    }
    /**
    * Picks off the exception name and the first line of information
    * from the stack trace about where the exception occurred and
    * returns that as a single string.
    */
    private String firstLineOfStackTrace(Exception ex, boolean logToError) {
       if (logToError) {
            ex.printStackTrace(System.err);
        }
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        ex.printStackTrace(pw);
        LineNumberReader lnr =
            new LineNumberReader(new StringReader(sw.toString()));
        try {
            String lineOne = lnr.readLine();
            String lineTwo = lnr.readLine();
            return lineOne + " " + lineTwo;
        } catch (IOException e) {
            return null;
        }
    }
    
  3. 今度は、カスタム・ライフサイクルを返す新規のフェーズ・リスナーを作成する必要があります。

新規のフェーズ・リスナーを作成する手順:

  1. ADFPhaseListenerクラスを拡張します。

  2. protected PageLifecycle createPageLifecycle ()メソッドをオーバーライドして、新規のカスタム・ライフサイクルを返します。

    例12-14に、frameworkExt.SRDemoADFPhaseListenerクラス内のcreatePageLifecycleメソッドを示します。

    例12-14 SRDemoADFPhaseListener内のcreatePageLifecycleメソッド

    public class SRDemoADFPhaseListener extends ADFPhaseListener {
      protected PageLifecycle createPageLifecycle() {
        return new SRDemoPageLifecycle();
      }
    }
    
  3. faces-config.xmlファイル内でフェーズ・リスナーを登録します。

    • faces-config.xmlファイルを開き、エディタ・ウィンドウ内で「概要」タブを選択します。faces-config.xmlファイルは<View_Project>/WEB-INFディレクトリ内にあります。

    • ウィンドウ内で「ライフ・サイクル」を選択し、「新規」をクリックします。「ヘルプ」をクリックするか、[F1]を押すと、フェーズ・リスナーの登録に関する追加のヘルプが表示されます。

1つのページの例外処理をオーバーライドする手順:

  1. FacesPageLifecycleクラスを拡張するカスタム・ページ・ライフサイクルを作成します。

  2. public void reportErrors(PageLifecycleContext)メソッドをオーバーライドして、エラー・メッセージの表示をカスタマイズします。このメソッドのオーバーライドの例は、前述の「ライフサイクルでエラーがレポートされる方法をカスタマイズする手順:」を参照してください。

  3. ページのページ定義を開きます。構造ウィンドウで、ページ定義ノードを選択します。プロパティ・インスペクタで、ControllerClass属性の値として新規のクラスを入力します。

12.8.2 デフォルトのエラー処理を変更するときに行われる処理

独自のエラー・ハンドラを作成すると、アプリケーションではDCErrorHandlerクラスではなくそのクラスが使用されます。新規のライフサイクルを作成して登録したため、そのライフサイクルがアプリケーションに使用されます。この新規のライフサイクルによって、カスタム・エラー・ハンドラがインスタンス化されます。

以降にエラーが見つかると、バインディング・コンテナがエラーをカスタム・エラー・ハンドラにルーティングします。次に、reportException(BindingContainer, Exception)メソッドが実行されます。

カスタム・ライフサイクル・クラス内のreportErrorsメソッドをオーバーライドした場合は、レンダリングの準備フェーズでライフサイクルによって新規のreportErrors(context)メソッドが実行されます。