ここでは、次の内容を中心としてサンプル・アプリケーションの詳細について説明します。
| 注意 |
ここでは、 「
サンプル・アプリケーションのインストールと設定」の項の指示に従っていること、
|
図47で示すように、ADF ToyStoreサンプル・アプリケーションのビュー層の大部分は、JSPページを使用して実装されています。ただし、いくつかのページは、JSPの代替を利用できることを示す例として、XML/XSLTベースのアプローチを使用しています。 意図的に WEB-INFディレクトリのサブ・ディレクトリにページを配置したため、ユーザーはこのページを直接参照することはできません。 これは、ユーザーのすべてのリクエストが、必ずコントローラ層を経由するようにするための手法であり、ブックマークなどを使用して、コントローラ層を通過せずにJSPページに直行することは不可能にしています(J2EE仕様のルールにより、
WEB-INF以下のページは、コントローラ層から制御を転送することはできますが、直接参照することはできません)。 したがって、コントローラ層での完全な管理を保証するためには、これが安全でベスト・プラクティスなアプローチです。
toystore.viewパッケージには、
ToyStoreResources.propertiesファイルと
GlobalErrors.propertiesファイルが含まれています。これらのファイルには、ビュー層ページで使用される翻訳可能な文字列が、デフォルトの言語(英語)で保存されています。 名前に 「_it」および
「_de」が付いている、これらの2つのファイルの別のバージョンには、それぞれ同じ文字列のイタリア語とドイツ語の翻訳が含まれています。
DataBindings.cpxはADFのバインディング・コンテキスト・ファイルで、データ・コントロールに関する定義情報、およびバインディング・コンテナの名前が格納されています。
*UIModel.xmlファイルには、詳細なバインディング・コンテナの定義情報が含まれており、それぞれに含まれているバインディングについて詳しく記述しています。 JDeveloperのアプリケーション・ナビゲータで DataBindings.cpxをクリックし、構造ウィンドウで詳細を調べると、このサンプル・アプリケーションには、
ToyStoreServiceという名前のデータ・コントロールのみが定義されていることがわかります。
JSPページの1つのビュー層、
yourcart.jspページを例に、どのように構築されているかを調べてみましょう。「StrutsとADFを使用したWebページ・リクエストのライフサイクル」の項で説明したように、ADFBindingFilter、
DataAction、および
DataControlが連携して、アプリケーション・モジュールのインスタンスを取得し、それをコントローラ層とビュー層に対してデータ・コントロールとして利用可能にし、リクエストの最後に解放しています。 コントローラ層は、各リクエストの最初にDataAction が作成するDataActionContextオブジェクトを使用して、次の項目にアクセスできます。
一度データ・コントロールを取得すると、そのデータ・プロバイダにアクセスして、それをアプリケーション・モジュールのビジネス・サービス・インタフェースに型変換し、モデル・データ・マップ内のデータ転送オブジェクトのコレクションに直接、または間接的にアクセスすることができます。 また、バインディングを使用すれば、ビジネス・サービスを意識することなく処理することも可能です。コントローラ層とビュー層がMVCアーキテクチャとして機能すべき役割としては、コントローラ層がモデル・データを修正し、ビュー層は、表示のためにモデル・データを反復処理することだけであることを再認識しておきましょう。
JSP Standard Tag Library(JSTL)には、事実上すべてのJSP Webページの開発者が必要な便利なタグのセットが用意されています。 JSPページは、JSTLタグを使用して、ページ、リクエスト、セッション、およびアプリケーション・スコープから属性を読込み/書込みし、それらの値に対しての反復処理を行い、必要に応じて、ページにそれらの値を出力たり、ページのレンダリングの条件付けとして利用することができます。 もちろん、これらのタスクは、JSTLを使わずにJSPページのコードだけでも実現できますが、JSTLを使用すると、ページの管理を複雑にするJSPスクリプトレットに頼ることなく、これらのタスクを実行できます。
ユーザーはJSTLタグを使用して、JSTLのExpression Language(EL)と呼ばれる簡単なドット表記法によって操作対象のオブジェクトとプロパティを特定できます。EL式は、 ${expression}のように
${と
}で囲まれたJSTLタグの属性値で表現されます。
たとえば、
bindingsという名前の属性値を参照するには、
${bindings}という式を使用します。 返されたオブジェクトがプロパティを持つJavaBeanである場合、または名前付きのメンバーを持つ
Mapである場合は、ドット表記法を使用して、このメンバーを参照できます。 たとえば、
bindingsというオブジェクトが、
ShoppingCartというメンバーを含んでいるマップの場合は、
${bindings.ShoppingCart}の式を使用してこのメンバーを参照できます。
ADFバインディング・オブジェクトは、JavaBeanやJava Collections Frameworkを操作可能な任意のテクノロジーから簡単に使用することができます。JSTLタグ・ライブラリもそのようなテクノロジーの一つです。 それぞれのリクエストで、ADFフレームワークの DataActionは、現行のバインディング・コンテナを
bindingsという属性として参照できるようにし、現行のバインディング・コンテキストを dataという属性として参照できるようにします。これらのすべての情報の参照のために、JSTLタグのEL式を使用できます。
JSTLタグを使用する際には、タグ名およびタグ属性の入力支援機能である通常の「コード・インサイト」だけでなく、 図48のように、EL式の記述支援のためのEL用コード・インサイト機能も提供されます。
次の項では、ADF ToyStoreサンプル・アプリケーションのすべてのJSPページで、ADFバインディング層によって公開されるデータを表現するために、JSTLとEL式を使用していることについて確認します。
最初に、買い物かごがどのように機能するかを説明します。
/yourcart DataPageは、ビュー層のUI表示のために /WEB-INF/jsp/yourcart.jspページを使用するもので、次のように構成されています。
<action path="/yourcart"
className="oracle.adf.controller.struts.actions.DataActionMapping"
type="toystore.controller.strutsactions.YourCartAction"
name="DataForm"
parameter="/WEB-INF/jsp/yourcart.jsp"
unknown="false">
<set-property property="modelReference"
value="WEB_INF_jsp_yourcartUIModel"/>
<forward name="reviewcheckout" path="/reviewcheckout.do"/>
</action>
これは
YourCartActionアクション・クラスにマップされており、適切なモデル・データを取得するために必要な手順を実行します。
modelReferenceプロパティの値から、
/yourcart DataPageのバインディング・コンテナ名は、
WEB_INF_jsp_yourcartUIModel(ADFの設計時支援ツールによって生成された名前)であることがわかります。
yourcart.jspページがアクティブな状態で、構造ウィンドウの
「UIモデル」タブをクリックすると、
図49のように、ページのバインディング・コンテナの内容が表示されます。 ここには、1つのイテレータ・バインディング
(ShoppingCartIterator)と1つのレンジ・バインディング
(ShoppingCart)があります。
実行時には、DataForwardActionのデフォルト動作により、UI表示のために、付随しているJSPページに制御がフォワードされます。 この例では、yourcart.jspページへフォワードするように ShowProductDetailsアクションが構成されています。
ページでは、次の4つのタグ・ライブラリを宣言することから始まっています。
bean:という接頭辞が付きます。
html:という接頭辞が付きます。
c:という接頭辞が付きます。
adf:という接頭辞が付きます。
Struts Beanタグ・ライブラリを使用すると、便利な
<bean:message>タグを利用できます。これを使用すると、
details.title、
cart.addItem、
images.buttons.addtocartなどの文字列キーに基づいて、翻訳テキスト文字列をページに簡単に組み込むすることができます。
Struts HTMLタグ・ライブラリを使用すると、 <html:errors>タグを利用できます。これを使用すると、実行時の処理で発生したすべてのエラーを、標準の方法でページの最上部に簡単に表示できます。
JSTL Coreタグ・ライブラリを使用すると、データ・コレクションを反復処理し、属性値をページに組み込むことができます。このページで使用しているJSTLタグには、次のものがあります。
<c:choose>、
<c:when>、および
<c:otherwise>。これらのものは、
if/
then /
else 文のように機能して、買い物かごが空かどうかによって、条件に応じた表示を行います。
<c:choose>
<c:when test="${not empty bindings.ShoppingCart.rangeSet}">
<form action="yourcart.do" method="post">
<!-- etc. -->
</form>
</c:when>
<c:otherwise>
<br><br><bean:message key="cart.empty"/>
</c:otherwise>
</c:choose>
買い物かご内の商品を反復処理する <c:forEach>、およびこれらの商品の属性を表示する
<c:out>。
<c:forEach var="Row" items="${bindings.ShoppingCart.rangeSet}" >
<tr bgcolor="#f3f3f3">
<td>
<a href="yourcart.do?event=removeItem&id=<c:out value='${Row.Itemid}'/>"
><img src="<bean:message key='images.buttons.removefromcart'/>"
border="0" alt="<bean:message key="cart.removeItem"/>"></a>
</td>
<td><c:out value="${Row.Itemid}"/></td>
<!-- etc. -->
</tr>
</c:forEach>
ADFタグ・ライブラリは、 <adf:render>タグを利用するために使用しています。このタグを利用すると、言語環境に応じた適切な書式マスクでフォーマットされた属性データを出力できます。ここで利用される書式マスクは、ビジネス・コンポーネントの定義情報の中で指定できます。
これらにより、ブラウザに表示される出力は、 図50のようになります。
買い物かごの中身は、一時的なビュー・オブジェクト行に保持されており、ユーザーが買い物かごに対して商品を追加および削除したときに、プログラムによって移入されることを思い出してください。 このため、前の項の
/yourcartの例では、ページの表示処理にデータベース問合せが含まれていません。
次に、問合せされたデータベース情報を表す、
/showproductdetails DataPageなどのページについて説明します。
/showproductdetails DataPageは、ビュー層のUI表示のために showproductdetails.jspページを使用するもので、次のように構成されています。
<action path="/showproductdetails"
className="oracle.adf.controller.struts.actions.DataActionMapping"
type="toystore.controller.strutsactions.ShowProductDetailsAction"
name="DataForm"
parameter="/WEB-INF/jsp/showproductdetails.jsp"
unknown="false">
<set-property property="modelReference"
value="WEB_INF_jsp_showproductdetailsUIModel"/>
<forward name="addToCart" path="/yourcart.do"/>
</action>
これは
ShowProductDetailsActionアクション・クラスにマップされており、適切なモデル・データを取得するために必要な手順を実行します。 このアクションには、
例9に示すようなコードが含まれています。
/* From: toystore.controller.strutsactions.ShowProductDetailsAction */
|
DataActionの拡張クラスを用意して、このような
initializeModelForPage()メソッドをライフサイクルに追加するよう、カスタマイズしました。 さらに、DataActionの
prepareModel()ライフサイクル・フェーズで問合せが実行される前に 、ビュー・オブジェクトの問合せのバインド変数値をセットするように、アクションのメソッドをオーバーライドしました。 これは、追加のライフサイクル・メソッドによって処理が容易になることを示した代表的なユースケースです。
| 注意 |
これは、ベースとなる次のデータベース・エラー・メッセージによって発行されます。
|
ToyStoreDataForwardActionを見てみると、既存の
prepareModel()メソッドをオーバーライドし、イベントを処理していない場合は、最初に
initializeModelForPage()をコールするようデフォルト動作を補強することによって、この新しいライフサイクル・メソッドを導入していることがわかります。
/* From: toystore.fwk.controller.ToyStoreDataAction */
/**
* Overridden method.
*
* Add two new overrideable methods into the ADF DataAction lifecycle as
* part of the prepareModel() processing to make handling the page
* initialization use case easier.
*/
protected void prepareModel(DataActionContext ctx) throws Exception {
if (!handlingEvents(ctx)) {
initializeModelForPage(ctx);
}
super.prepareModel(ctx);
if (!handlingEvents(ctx)) {
initializeBindingsForPage(ctx);
}
}
例9の
initializeModelForPage()メソッド本体では、DataActionContextを使用して
HttpServletRequestから
idパラメータを取得し、それを引数として
ToyStoreServiceビジネス・サービス・インタフェースの
prepareToShowProductDetails()メソッドに渡しています。 層に依存しない ToyStoreServiceインタフェースの背後にある ToyStoreServiceImpl実装クラスでは、 prepareToShowProductDetails()メソッドは次のように書かれています。
/* From: toystore.model.services.ToyStoreServiceImpl */
public void prepareToShowProductDetails(String id) {
getFindItems().setItemToFind(id);
getFindItems().executeQuery();
}
これは、検索対象となる商品IDの設定と、ビュー・オブジェクトの問合せの再実行の処理をカプセル化しています。
getFindItems()メソッドはビュー・オブジェクト・インスタンスを取得するメソッドで、ユーザーがアプリケーション・モジュールのデータ・モデルに対して FindItemsというビュー・オブジェクト・インスタンスを追加したときに、ADFの設計時支援ツールによって生成されたものです。
ビュー層は、適切なコントロール値バインディング・オブジェクトを使用することによって、ビュー・オブジェクトの問合せ結果を表すデータ転送オブジェクトにアクセスできます。 このようなコントロール値バインディングは、イテレータ・バインディングに関連付けられて、そのイテレータ・バインディングによって、モデル・データ・マップの FindItemsコレクションからデータが取得されます。
もう一度
例9を見てみると、
ToyStoreServiceDataForwardActionスーパークラスの
getToyStoreService()ヘルパー・メソッドを使用していることがわかります。これは、
ToyStoreServiceというビジネス・サービスのカスタム・サービス・インタフェースを返します。
/* From toystore.controller.strutsactions.ToyStoreServiceDataForwardAction */
protected ToyStoreService getToyStoreService(DataActionContext ctx) {
return (ToyStoreService) getApplicationModule(DATACONTROLNAME, ctx);
}
次にこのコードは、そのスーパークラス
ToyStoreDataForwardActionから、
getApplicationModule()ヘルパー・メソッドをコールします。 次に示すように、このメソッドは名前によってデータ・コントロールを検索し、それがADFアプリケーション・モジュールに基づくデータ・コントロールの型である場合、 ApplicationModuleインタフェースとして機能するサービスのインスタンスを返します。
/* From toystore.fwk.controller.ToyStoreDataForwardAction */
protected ApplicationModule getApplicationModule(String dataControlName,
DataActionContext ctx) {
DCDataControl dc = ctx.getBindingContext().findDataControl(dataControlName);
if ((dc != null) && dc instanceof DCJboDataControl) {
return (ApplicationModule) dc.getDataProvider();
}
return null;
}
実行時には、 initializeModelForPage()メソッドが適切なバインド変数値を設定した後で、
DataForwardActionのデフォルト動作によって、UI表示のために、付随しているJSPページに制御がフォワードされます。この例では、 例10に示すような
showproductdetails.jspページにフォワードするように ShowProductDetailsアクションが構成されています。 ここで使用されているタグ・ライブラリは、 yourcart.jspページで使用しているのと同じものです。 唯一、異なる点は、このページは
FindItemsIteratorの1行につきデータを表示しているため、バインディング・コンテナの中にレンジ・バインディングは必要なく、
<c:forEach>ループが必要ないことです。 条件による表示ロジックも使用していないため、このページには、
<c:choose>または
<c:if>タグもありません。
<%@page import="org.apache.struts.action.ActionErrors" %>
|
ブラウザに表示される出力は、 図51のようになります。
ADF ToyStoreサンプル・アプリケーションのすべてのJSPページは、この基本的なアプローチに従ってデータの反復処理やフォーマットをしています。
このサンプル・アプリケーションでは、次の3つのページを用意しており、それぞれ検索結果を表示することができます。
/search - 店全体の製品検索の結果を表示します。
/showcategory - あるカテゴリの製品を表示します。
/showproduct - 特定の製品タイプで有効な商品を表示します。
このようなページに対して 1-3 of 18 といった表示で現行のレコード位置を知らせたり、条件に応じて、 Previous や Next のリンクを表示するような、ページング・コントロールを作成し、かつ、再利用可能なものにしたいと考えていました。 これを作成するには、次の情報が必要です。
上記の3つのページには、それぞれのバインディング・コンテナ内にレンジ・バインディングが1つ含まれています。 レンジ・バインディングは、データ行を区切られた範囲ごとに表示し、一度に1ページ分(または1つの「範囲」分)データをナビゲートできるよう設計されているADFのバインディング・オブジェクトです。 ユーザーは、イテレータ・バインディングのレンジ・サイズを使って、一度に1ページに表示する行数を設定できます。 レンジ・サイズを -1の値に設定すると、データ・セット内のすべての行が一度に表示されます。
図52は、
/search DataPageのバインディング・コンテナ、およびその
FindProductsレンジ・バインディングと、レンジ・バインディングが関連付けられているFindProductsIteratorイテレータ・バインディングを示しています。
実行時には、レンジ・バインディングは、JSTLのEL式を介してアクセスできるプロパティを公開します。これらのプロパティによって、関連するイテレータとバインドされているデータ・コレクションの情報が取得できます。 これらのプロパティには、次のものがあります。
estimatedRowCount - コレクション内の推定行数
rangeStart - 行の現行範囲の最上位に表示する、ゼロから始まる行番号
rangeSize - 1ページに表示する行数
これらの3つの情報を使用して、必要なすべての情報を計算することができます。
例11は、
ToyStoreViewControllerプロジェクト内にある
pagingControl.jspのコードを示しています。 このコードでは、次のJSTLタグを利用して、EL式によって、必要なバインディング・オブジェクトにアクセスしていることがわかります。
<c:set> - ページのローカル変数の値を設定して、ページ内の後で使用できるようにします。
<c:choose> - スクリプトレット・コードを記述せずに、if/then/elseのロジックを実行します。
<c:when>および
<c:otherwise>タグとともに使用)
<c:out> - EL式の値を出力します。
条件によって表示される「Previous」および
「Next」のリンクは、それぞれのURLで、
event=Previousおよび
event=Nextパラメータを持つように生成されます。これによって、名前が PreviousおよびNextに一致するアクション・バインディングが、現行ページのバインディング・コンテナ内で宣言的に実行されます。
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean"%>
|
search.jspページのソース・コードを見てみると、 pagingControl.jspページを再利用していることがわかります。ここでは、 <jsp:include>を使用し、ネストされている
<jsp:param>タグによって、ページで必要なパラメータの組を渡しています。 ここでは、
<c:choose>タグを使用して、
not emptyというELの演算子によって、製品データが見つかったかどうかをテストしています。 製品データが見つからなかった場合は、結果として空の表を示すのではなく、「No matching products」というメッセージを出力します。
<c:choose>
<c:when test="${not empty bindings.FindProducts.rangeSet}">
<jsp:include page="pagingControl.jsp">
<jsp:param name="rangeBindingName" value="FindProducts"/>
<jsp:param name="targetPageName" value="search.do"/>
</jsp:include>
<table border="0" bgcolor="#003399">
<!-- etc. -->
</table>
</c:when>
<c:otherwise>
<br><br><bean:message key="search.nomatchingproducts"/>
</c:otherwise>
</c:choose>
pagingControl.jsp では、最初に、レンジ・バインディング・オブジェクトを検索するために必要な "名前" の情報を得るために、次の構文を使用して、渡される引数から rangeBindingNameパラメータの値を取得します。
<c:set var="rangeBinding" value="${bindings[param.rangeBindingName]}"/>
pagingControl.jspでは、EL式を使用してレンジ・バインディングのプロパティを参照し、ページ・コントロールの表示をレンダリングする作業を実行します。 この式では、 ${bindings.SomeRangeBinding}のようなELのドット表記法を使用せずに、角括弧を使用した配列インデックス・スタイルの表記法を使用していることに注意してください。 これによって、(この場合は bindingsオブジェクトに対して)アクセスしようとしているメンバー名を、そのまま名前で提供するのではなく、式値として提供することができます。
ユーザーは、「1ページに表示する行数をどこで設定するか」という疑問を持つでしょう。 これは重要なポイントです。
図52に示されている「UIモデル」タブで
FindProductsIteratorをクリックし、プロパティ・インスペクタを見てみると、
レンジ・サイズ プロパティの値が
3に設定されていることがわかります。 これは、実行時にこのイテレータが、一度に3行までの結果をページに提示することを意味しています。 一度に5つの結果を表示するよう変更するには、コードを変更するのではなく、このイテレータ・プロパティの値を
5に修正するだけで済みます。 ユーザーが、実行時に1ページに表示する行数を変更できるようにする機能を提供したい場合は、イテレータ・バインディングに対して setRangeSize()をコールすることができます。
/editaccount DataPageは、ユーザー・プロファイル情報を編集するためのデータ入力フォームを表示するページを表しています。 これは、使用する各コントロールをHTMLフォーム内に配置するという、通常のJSPページ・レイアウトのアプローチを使用しています。
EditAccountActionは、これまでに説明したアクションと同様に、initializeModelForPage() メソッド内でモデル層を設定します。 ここで、対象のユーザー名を引数として ToyStoreServiceインタフェースのカスタム・サービス・メソッド prepareToEditAccountInfoFor()をコールします。
ToyStoreServiceImplクラスにおけるこのメソッドの実装は、
例12のようになります。 ここでは、次の3つの基本的な手順を実行します。
oracle.jbo.Keyオブジェクトを作成します。
findByKey()メソッドに渡して、
Accountsビュー・オブジェクトの既存の行を参照します。
/* From: toystore.model.services.ToyStoreServiceImpl */
|
editexistingaccount.jspという名前の、対応するJSPページでは、Struts HTMLタグ・ライブラリから <html:form>タグを使用し、次のようにアクションの宛て先を自分自身のDataPageへ戻る(バック)ように送信(ポスト)することによって、「ポストバック・パターン」を実装します。
<html:form action="/editaccount.do" method="post">
実行時に、Strutsの <html:form>タグは、action属性値である
/editaccount.do を参照して、アクション・マッピング情報から、このフォームをレンダリングするために
DataFormというFormBeanが使用されていることを判断します。
DataFormフォームBeanは
struts-config.xmlで定義されており、ADFの
BindingContainerActionFormを使用しています。
ユーザー・アカウント情報の1行のみについて、データ入力フォームをレンダリングしているため、このページではJSTL
<c:forEach>を使用する必要はなく、バインディング・コンテナにもレンジ・バインディングは必要ありません。 通常のHTMLテーブル・タグを使用して、単純にそれぞれのフィールドをフォームにフォーマットし、ラベルと入力コントロールを一列に並べています。 このページは、いくつかの有用なテクニックの使用例でもあるため、次にそれぞれの重要なポイントを中心にして説明します。
図53は、
EditAcountページのバインディング・コンテナを示しています。 ここには、Countryを
除けば、
その他のAccountsの属性に対して、基本的な属性バインディングが用意されています。
Country に対して用意されているものはリスト・バインディングです(そのアイコンはポップリストを表しています)。また、ここには、2つのイテレータ・バインディングがあります。1つは、編集しているメインの Accounts情報の
AccountsIteratorで、もう1つは、ユーザーが
Country属性に対して選択できる有効な国の名前を示すポップリストを提示する
CountryListIteratorです。
ToyStoreServiceデータ・コントロールの組込みのCommit操作にバインドされている、
saveというアクション・バインディングもあります。
例13は、
Usernameプロパティに対するラベルとデータを含むHTMLテーブルの行を出力する、ページのタグを表しています。 このアプリケーションのユーザー名は一度作成すると更新できない仕様にしていますので、データに対して入力フィールドを表示する必要はありません。
<c:out>タグを使用すると、対応するバインディング・オブジェクトを使用して、表示用のフィールド値を簡単に出力することができます。
<bean:message>タグは、
ToyStoreResources.propertiesプロパティ・ファイルから翻訳可能な文字列を出力して、ツールチップとユーザー名のラベルを設定しています。
<%-- Username field --%>
|
データを入力または編集する必要がある場合は、StrutsのHTMLライブラリの他のタグを使用して、データバインド・コントロールを表現できます。
例14は、
<html:password>タグを使用して
Passwordプロパティを表示していることを示しています。
<html:password property="Password" size="25" maxlength="30"/>
ADFの
BindingContainerActionForm は、Struts(およびここでは特に、StrutsのHTMLタグ・ライブラリのタグ)に対して提示される
DynaActionForm Beanで、対象のバインディング・コンテナのバインディングと関連付けられ、同じ名前のプロパティを持っていることを思い出してください。このため、 <html:password>タグが、このフォームBean経由で Passwordプロパティの値を取得(get)および設定(set)すると、ADFは、内部でこのフォームBeanのプロパティと対象のバインディング・オブジェクトの値を調整します。
<%-- Password field --%>
|
この例では、StrutsのHTMLタグ <html:errors>を使用して、
Password属性に固有の検証エラーを表示していることも表しています。 もちろん、フォームが最初に出力されるときは検証エラーがないため、この表のセルは空になります。 ただし、ユーザーがフォームを送信し、モデル層で検証エラーがスローされた場合は、このページがもう一度出力されるときに、 Passwordに関連するエラーが、画面上の
Passwordフィールドの隣に表示されます。 また、このフィールドは必須であることがわかっているため、
<bean:message>タグを使用して、キー dataentryform.mandatoryに対応する文字列によって、このフィールドが入力必須であることをユーザーに示す視覚的なマーカーとして表示しています。 デフォルトでは、アスタリスクが出力されます。
ADF Business Componentsのエンティティ・オブジェクトおよびビュー・オブジェクトのコンポーネントには、ロケールによって異なるラベル、ツールチップ、書式マスクなどのコントロールに対する追加情報を開発者が定義するための多数の組込み機能が用意されています。 ADFのバインディング層には、ビュー層ページから簡単にアクセスできるよう、バインディング・オブジェクト上でこの情報を公開しています。
例15の
<c:out>タグは、ビジネス・オブジェクト属性またはビュー・オブジェクト属性に関連付けられているツールチップおよびラベルの情報に対するEL式を表しています。 あるエンティティ・オブジェクトの、たとえば Firstnameという名前の属性に対して、ツールチップが定義されている場合、 Firstname が含まれているすべてのビュー・オブジェクトでもこのツールチップ定義が継承されます。 もちろん、必要に応じて、個々のビュー・オブジェクトで、これらのコントロール・ヒントをオーバーライドできます。
<%-- Firstname field --%>
|
各バインディング・オブジェクトは、バインド対象のオブジェクトに関する実行時の定義情報を公開します。ユーザーは、バインドされているオブジェクトに対してEL式を使用して実行時にアクセスできます。 たとえば、属性にバインディングされているコントロール値は、ベースとなっているモデル層の属性の情報を公開します。
例15では、このメタデータを使用して、
Firstnameなどの属性が必須かどうかを実行時に検出しています。 JSTLタグの
<c:if>と組み合せると、この情報を使用して、必要なフィールドに、条件に応じて必須のマーカーを出力できます。
<c:if test="${bindings.Firstname.mandatory}">
<bean:message key="dataentryform.mandatory"/>
</c:if>
一つ覚えておくと便利なTipsとして、バインディング・オブジェクトで使用できるプロパティ情報の取得方法を紹介します。構造ウィンドウの「UIモデル」タブで対象のバインディングをクリックして、 F1キーを押します。 このようにすると、対象のバインディング・オブジェクトについてのオンライン・ヘルプのトピックが表示され、参照することができます。
最後に、データバインドされたフォーム・コントロールの例として、ユーザーが居住している国を表示するポップリストについて説明します。 図54に示すように、このコントロールには 2つの側面があります。
Countryバインディングの値。リストで選択した内容が反映されます。
ADFには、このように、データ・バインディングの要件に対して複数の局面を持つコントロールを処理するための、より高度なバインディング・オブジェクトが提供されています。 ADFリスト・バインディングは、ポップリスト・タイプのコントロール要件を満たすべく、現行のバインディング属性値と、ユーザーに提示するための有効な選択リストの両方を管理できます。また、階層的なデータに対しては、場合によってはさらに便利に使用できるツリー・バインディング・オブジェクトを提供しています。
editexistingaccount.jspがアクティブなときに、構造ウィンドウの
「UIモデル」タブをクリックすると、
図53のようなバインディング情報が表示されます。
CountryListIteratorを選択してプロパティ・インスペクタをみると、このレンジ・サイズが -1になっていることがわかります。 この値は、部分的な行だけを表示するのではなく、国のリストにすべての行を表示することを意味します。
| 注意 |
イテレータ・バインディングのレンジ・サイズは、デフォルトで
|
Countryバインディングをクリックし、マウスを右クリックしてメニューで
「編集...」を選択すると、
図55のような リスト・バインド・エディタ が表示されます。 ここでは、 Countryポップリストをサポートするために必要な、次の定義情報が表示されます。
CountryListIterator:使用できる選択リストのデータ・ソースは、ここから取得されます。
AccountsIterator:この現行の行データは、バインディング値として使用され、リストから項目が選択された場合には更新されます。
CountryListIteratorで選択された行の
Codeプロパティの値が、ターゲットの
AccountsIterator の現在行の
Countryプロパティに設定されることを意味します。
「LOVの表示属性」タブをクリックすると、
CountryListIteratorの
Description属性が、ユーザーに表示される値としてリストに示されることがわかります。
例16は、
<html:select>および
<html:optionsCollection>を使用して、
Countryリスト・バインディングでページにポップリストを表示する方法を表しています。
<html:select>は、フォームBeanの
Countryプロパティにバインドされており、リスト・バインディングに関連付けられています。
<html:optionsCollection>は、同じ
Countryバインディングの、ネストされたリスト値である displayDataからデータを取得します。 この表示用のデータ・コレクション内の各Beanは promptおよび
indexプロパティを持っており、リストの各選択項目の labelおよび
valueとして使用するように設定されています。
| 注意 |
ネットワーク最適化のために、ADFのバインディング層では、入出力されるリストのインデックスが、ゼロで始まる番号であることを想定しています。 ADFのリスト・バインディングは、バインディング値の読込みおよび書込みのときに、ベースとなる |
<%-- Country field --%>
|
ToyStoreサンプル・アプリケーションには、前に説明した「通常の」テクニックとは異なる、より汎用的なメタデータ駆動の手法でデータ入力フォームを出力するページも含まれています。 これらの2つの手法によるフォームは、両方とも、 Accountsデータに対する同じコントロール・セットを出力するため、2つのアプローチを比較して、自分のアプリケーションに適した方を簡単に選択できます。
例17は、
(/register DataPageで使用される)
registernewuser.jspページを表しています。このページは、データ入力フォームを出力して、最初のユーザー登録ができるようにします。 このページのブラウザで生成される結果は、前述の editexistingaccount.jspページのものとほとんど同じですが、この例からわかるように、フォーム全体は、1つの
<jsp:include page="formControl.jsp">タグによって出力されます。 このタグは、再利用可能コンポーネントのように formControl.jspページのコンテンツをページに組み込みます。 ネストされている <jsp:param>タグは、再利用可能なコンポーネント・ページに次の3つのパラメータを渡します。
dataPage - 現行のデータページの名前
saveButtonLabelKey -
「(Save)」ボタンの表示ラベルのためのメッセージ・バンドル・キー
saveButtonEvent -
「(Save)」ボタンに関連付けるイベントの名前
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
|
したがって、行われる実際の処理は、
formControl.jspコンポーネント・ページの中にあります。 このページは、現行のバインディング・コンテナのコントロール値バインディングそれぞれに対応するコントロールを持つ、データ入力フォームを作成します。
このページでは、最初に、 <c:choose>、
<c:if>、および
<c:set>タグを使用して、渡されたパラメータの値に応じて、eventName、
buttonLabel、および
buttonLabelKeyというページのローカル変数の値を設定するコードで始まっています。 これらのページの変数は、後に、生成されたフォームの下部の 「(Save)」ボタンを構成する際に使用します。
<c:choose>
<c:when test="${not empty param.saveButtonEvent}">
<c:set var="eventName" value="${param.saveButtonEvent}"/>
</c:when>
<c:otherwise>
<c:set var="eventName" value="Commit"/>
</c:otherwise>
</c:choose>
<c:if test="${not empty param.saveButtonLabel}">
<c:set var="buttonLabel" value="${param.saveButtonLabel}"/>
</c:if>
<c:if test="${not empty param.saveButtonLabelKey}">
<c:set var="buttonLabelKey" value="${param.saveButtonLabelKey}"/>
</c:if>
formControl.jspページは、次に入力フォームの「グローバル・エラー」セクション部分として <html:errors>タグを使用します。ここでは、属性固有ではない全般的なエラーが表示されます。
<center>
<table border="0">
<tr>
<td><html:errors bundle="GlobalErrors"
property="<%= ActionErrors.GLOBAL_ERROR %>"/></td>
</tr>
</table>
</center>
次にこのフォームは、
<html:form>の開始タグの一部として、 jsp:includeによって渡される
dataPageパラメータの値を使用します。
<html:form>タグのアクション属性の中ではEL式を直接使用できないため、
<c:set>を使用して、EL式の値を使用してnameというページ・ローカル変数を設定し、次にJSPスクリプトレットを使用して、このname変数の値を
action属性へ渡していることがわかります。
<c:set var="name" value="/${param.dataPage}.do"/>
<html:form action='<%= pageContext.getAttribute("name")%>'>
<!-- etc. -->
</html:form>
このフォームには、非表示フィールドが含まれています。ADFのコントローラ層はこのフィールドを使用して、ユーザーが連続して同じフォームを何度も送信しようとしたかどうかを判断します。
<input type="hidden" name="<c:out value='${bindings.statetokenid}'/>"
value="<c:out value='${bindings.statetoken}'/>"/>
次に、バインディング・コンテナ内の各コントロール値バインディングに対応するコントロールを作成するループを開始します。
<table>タグの中には、次の
<c:forEach>の反復処理があります。
<c:forEach var="curBinding" items="${bindings.ctrlBindingList}">
<% JUControlBinding cb =
(JUControlBinding)pageContext.getAttribute("curBinding");
if (cb instanceof JUCtrlValueBinding &&
!(cb instanceof JUCtrlRangeBinding) &&
!(cb instanceof JUCtrlHierNodeBinding)) { %>
<!-- Build control for current control value binding in here -->
<% } %>
</c:forEach>
<c:forEach>のループは、バインディング・コンテナ内のコントロール値バインディングのリストを反復処理します。 このリストには、アクション ・バインディングが含まれている可能性があるため、入力コントロールを出力する場合はこれらのバインディングをスキップする必要があります。 ここでは、簡潔にするために、レンジ・バインディングとツリー・バインディングもスキップしています。 EL式の言語には、組込みの instanceofオペレータがないため、JSPスクリプトレットを使用して、通常のJava言語の
if文を使用し、
instanceofによるチェックを実行しています。
| 注意 |
同じ手法によって、バインディング・コンテナで検出されるすべてのアクション・バインディングに対して汎用的にボタン・セットを出力することも可能です(アクション・バインディングは |
<c:forEach>タグに
var="curBinding"属性を指定したため、フォームの入力コントロールの汎用的な生成処理の一部として、ループ内でこの curBindingループ変数を参照することで、現在のコントロール値バインディングにアクセスすることができます。EL式の中で、
tooltip、
mandatory、
labelなどのバインディング・プロパティを使用して、現在のコントロール・バインディングから情報を取得している様子を確認してください。
<c:forEach var="curBinding" items="${bindings.ctrlBindingList}">
<% JUControlBinding cb =
(JUControlBinding)pageContext.getAttribute("curBinding");
if (cb instanceof JUCtrlValueBinding &&
!(cb instanceof JUCtrlRangeBinding) &&
!(cb instanceof JUCtrlHierNodeBinding)) { %>
<tr>
<th align="right" title="<c:out value='${curBinding.tooltip}'/>">
<c:if test="${curBinding.mandatory}">* </c:if>
<c:out value="${curBinding.label}"/>
</th>
<td>
<c:set var="name" value="bindings.${curBinding.name}"/>
<adf:inputrender model='<%= pageContext.getAttribute("name")%>'/>
</td>
<c:set var="name" value="${curBinding.name}"/>
<td>
<html:errors property='<%= pageContext.getAttribute("name") %>'/>
</td>
</tr>
<% } %>
</c:forEach>
HTMLフォーム・コントロールの実際の出力のために、 <adf:inputrender>タグを使用しています。このタグは、現在のバインディングの属性値よって適切なタグを出力します(後で説明しますが、追加の属性メタデータを使用することで、 <adf:inputrender>タグによる出力をカスタマイズすることができます)。 ここでも <c:set>によるテクニックを使用して、文字列 「 bindings.」と現在のバインディングの名前を連結して、
nameというローカル・ページ変数に設定しています。これは、 <adf:inputrender>タグの model属性の値として参照されています。
最後に、
<html:errors>タグを使用して、発生する可能性のある属性レベルの検証エラーを、該当するコントロールの隣に示しています。 ここでも
<c:set>のテクニックを使用して、
<html:errors>タグの
property属性の値を現在のバインディングの名前に設定しています。
また、
<c:choose>を使用して、適切にラベルが付加された
「(Save)」ボタンをフォームの下部に配置しています。 ユーザーがボタン・ラベル、またはボタン・ラベル・キーを指定したかどうかによって、指定されたラベル文字列を使用するか、または
bean:messageタグを使用してラベル・キーを参照するかを決定しています。 ページの上部に設定しているページのローカル変数
eventNameを使用して、ボタンに適切な名前を適用し、これによって、ユーザーがボタンをクリックしたときにイベントを生成しています。
<c:choose>
<c:when test="${not empty buttonLabel}">
<input name="event_<c:out value="${eventName}"/>" type="submit"
value='<c:out value="${buttonLabel}"/>'/>
</c:when>
<c:when test="${not empty buttonLabelKey}">
<input name="event_<c:out value="${eventName}"/>" type="submit"
value='<bean:message name="buttonLabelKey"/>'>
</c:when>
<c:otherwise>
<input name="event_<c:out value="${eventName}"/>" type="submit"
value='Submit'/>
</c:otherwise>
</c:choose>
ADF Business Components は、カスタム・プロパティの設定を機能があります。カスタム・プロパティは実行時に読み込むことが可能で、これを使用してメタデータ駆動の動作を実現することができます。 ADFのエンティティ・オブジェクトとビュー・オブジェクトでは、個々の属性レベルでもカスタム・プロパティを設定できます。
属性のプロパティを編集するには、それぞれのオブジェクト・エディタで「属性」を展開し、プロパティを編集したい属性の名前をクリックします。 次に、右側のエディタ・パネルで 「属性プロパティ」タブをクリックします。
図56は、
Accountsビュー・オブジェクトの
Country属性のカスタム属性プロパティを編集する際に、どのようになるかを示しています。
<adf:inputrender> タグの実装は、属性に対してカスタム・レンダラが指定されているかどうかの判断として、EditRendererという属性プロパティの値を確認します。 カスタム・レンダラを指定していない場合は、デフォルトのルールにより、適切なコントロールが選択されます。
この例では、ADFのリスト・バインディングである Country に対して、カスタマイズされたポップリスト・レンダラを実装するクラス名
toystore.fwk.view.ListBindingPoplistRendererを指定しました。 このカスタム・フィールド・レンダラはデフォルトの
oracle.jdeveloper.html.StaticPickListレンダラを拡張したもので、リスト・バインディング・オブジェクトから取得できる情報に基づいて、いくつかの追加プロパティを移入します。
例18に、カスタム・レンダラのソース・コード(サンプル・アプリケーションの
FwkExtensionsプロジェクトから取得できます)を示します。 このコードでは、データソースから
JUControlBindingオブジェクトにアクセスし、これが
JUCtrlListBindingであることをチェックした後で、リスト・バインディングで
getDisplayData()をコールしてリスト表示データにアクセスできることがわかります。 ラベルおよび値の
String[]変数にデータを移入するために、表示データのコレクションを反復し、コレクションの各Beanから、
prompt属性をラベルの配列に追加します。 ADFのバインディング層は、ページから戻ってくる値が数値の行番号であることを想定しているため、反復処理内で、ループ変数
zを文字列に変換し、値の配列にデータを移入しています。
このカスタム・レンダラによる最終的な利点は、汎用的な
formControl.jspコンポーネント・ページによって、現在のバインディング・コンテナのバインディングに対してHTMLフォームを出力するときに表面化します。このカスタム・レンダラによって、
Countryバインディングは、モデル・データ・マップの
CountryListという名前の(値オブジェクトによる)表示データのコレクションからデータを移入して構成される、データバインドされたポップリストとして出力されることになります。
package toystore.fwk.view;
|
アプリケーションで、このような汎用的なデータ・フォームのレンダリング・テクニックを採用すると、アプリケーション内のすべてのデータ入力フォームで外観を統一し、同じように動作することを簡単に保証できます。これは、すべての入力フォームが汎用的な同じコードによって出力されるためです。
JSPページのいくつかの例で、翻訳可能な文字列の使用や、翻訳可能な属性ラベル、ツールチップ、書式マスクへの参照について見てきました。 この項では、StrutsおよびADFのフレームワークが提供する機能による、多言語対応のユーザー・インタフェースをサポートするアプリケーションの作成について簡単に説明します。
Strutsには、標準のJava
*.propertiesファイルに格納されている、翻訳済のメッセージを使用するための基本的な機能があります。 デフォルトのメッセージ・リソースに加えて、ユーザー自身でセカンダリ・メッセージ・リソースを定義することもできます。 ADF ToyStoreサンプル・アプリケーションでは、次のメッセージ・ファイルを使用しています。
デフォルトのStrutsリソース・メッセージ・ファイル
これは
./ToyStoreView/src/toystore/view/ToyStoreResources.propertiesに格納されています。
キー
GlobalErrorsで特定されるグローバル・エラー用のセカンダリ・リソース・メッセージ・ファイル
これは
./ToyStoreView/src/toystore/view/GlobalErrors.propertiesに格納されています。
例19に示すように、
struts-config.xmlでメッセージ・リソース・ファイルの名前をStrutsに認識させます。
key属性を持たない
<message-resources>要素は、デフォルトのメッセージ・リソースの場所を定義しています。 セカンダリ・メッセージ・リソースは、
key属性の値を指定することによって、そのキーを表しています。
<struts-config>
|
メッセージは、文字列キーとテキスト・メッセージがペアになったプロパティとして格納されています。 たとえば、
ToyStoreResources.propertiesに次の2行があります。
:
cart.addItem=Add Item to Your Shopping Cart
cart.removeItem=Remove Item from Your Cart
:
ユーザーは、同じディレクトリ内に、ロケールの接尾辞を付加したファイル名でプロパティ・ファイルを作成して、リソース・ファイルのメッセージの翻訳版を提示することができます。 たとえば、
ToyStoreResources.propertiesのメッセージのイタリア語の翻訳は、同じディレクトリの
ToyStoreResources_it.propertiesファイルになります。翻訳版となるメッセージ・リソース・ファイルには、デフォルトの言語と同じ文字列キーで、メッセージ文字列を翻訳したものを含みます。 たとえば、前述の2つのメッセージ(英語版)は、メッセージ・リソース・ファイルのイタリア語版で次のようになります。
:
cart.addItem=Aggiungi al carrello
cart.removeItem=Togli dal carrello
:
翻訳が不要なメッセージは、翻訳版のメッセージ・リソース・ファイルで繰り返す必要はありません。 ロケール固有のメッセージ・バンドルでメッセージを見つけられない場合は、デフォルトのメッセージ・リソース・ファイルのものが使用されます。
| 注意 |
ロケールの言語
および国の組み合わせによって異なるメッセージを提供したい場合は、
|
前述の項のとおり、StrutsのBeanタグ・ライブラリの
<bean:message>タグを使用すると、文字列
keyを指定することで、メッセージ・リソース文字列をJSPページに含めることができます。
<bean:message key="cart.addItem"/>
実行時に、Strutsの
<bean:message>タグは、一致した最も有効なメッセージ・リソース・ファイルの文字列を返そうとします。 たとえば、ユーザーのロケールがスイス系ドイツ語(Swiss German:de_CH)の場合は、次のようになります。
ToyStoreResources_de_CH.propertiesがある場合は、ここから文字列を返します。次に、
ToyStoreResources_de.propertiesがある場合は、ここで文字列を検索します。次に、
Strutsは、ブラウザが各リクエストで送信したHTTPリクエストのヘッダー・プロパティを参照して、現行ブラウザのユーザーのロケールを推測します。これによってユーザーの注文リストがユーザーの優先言語で表示されます。より詳細に説明すると、Strutsの
RequestProcessorを介したそれぞれのリクエストで、
processLocale()メソッドのデフォルトの実装によって、Action.LOCALE_KEY定数で定義されている名前のHTTPセッション属性が存在するかどうかのチェックが行われます。 セッション属性が存在する場合は、処理を続行します。 存在しない場合は、Strutsはロケールを推測し、それを先のセッション属性として保存します。 このようにして、最終的に、Strutsによって1つのセッションにつきブラウザ・ユーザーの言語が1回特定されることになります。
| 注意 |
properties ファイルの日本語版を作成したい場合、 |
ADF Business Componentsのエンティティ・オブジェクトおよびビュー・オブジェクトは、Javaメッセージ・バンドルを関連付けることができます。 このコンポーネント用のメッセージ・バンドルには、ラベル、ツールチップ、書式マスクといった属性レベルのコントロール・ヒントの他に、コンポーネント用の例外メッセージなどの、ロケールによって異なる値を格納できます。 ADFの設計時ウィザードは、設定されるメッセージに対して、 デフォルトの言語用のコンポーネント・メッセージJavaクラスを自動的に作成し、そこで管理します。ユーザーは、名前の最後にロケール固有の接尾辞を付けて、翻訳版のメッセージ・バンドルを作成できます。
たとえば、
toystore.model.businessobjects.Accountというエンティティ・オブジェクトの場合は、メッセージ・バンドルは
AccountImplMsgBundle.javaという名前になり、
例20に示すようになります。 パッケージ
toystore.model.businessobjectsのコンポーネントの場合は、メッセージ・バンドル(およびカスタム・クライアント・インタフェース)は、
toystore.model.businessobjects.common サブパッケージに自動的に作成されます。 このパッケージのネーミングは、メッセージ・バンドル・クラスやクライアント・インタフェースがクライアント層とサーバー層の両方で共通
して使用されることを表しています。これに対し
toystore.model.*パッケージのすべての
*Impl.javaクラスおよびXMLファイルは、両方の層で共有する必要がありません。 これらの使用はサーバー層に制限されます。このように、クライアント層またはWeb層が、インタフェース(およびメッセージ・バンドルなどの最少数のクラス)のみで機能するようにすることは、ベスト・プラクティスのテクニックで、ADFの設計機能は、それを強力に促進すべく、一貫したネーミング・パターンとデプロイ/パッケージング・サポートを実施します。
package toystore.model.businessobjects.common;
|
ToyStoreサンプル・アプリケーションには、次のような、エンティティ固有の例外メッセージを、文字列の2次元配列として手動で追加しています。
static final Object[][] sMessageStrings = {
:
{ErrorMessages.ENTITY_ALREADY_EXISTS,
"Another user has already chosen this name. Please try another."},
:
}
Strutsのメッセージ・リソースのプロパティ・ファイルにおいて文字列キーの順序が意味を持たないのと同様に、
Object配列の
{String,String}要素の順序も意味がありません。
たとえば、このリソース・バンドルの翻訳バージョンをイタリア語で作成するには、
toystore.model.businessobjects.common パッケージに
AccountImplMsgBundle_itクラスを作成します。 簡単な方法としては、
AccountImplMsgBundle.javaをコピーして、
AccountImplMsgBundle_it.javaを作成します。
AccountImplMsgBundle_it.javaで、クラスの宣言名を変更し、デフォルトの言語メッセージ・バンドル・クラスを拡張した形に修正します。
public class AccountImplMsgBundle extends JboResourceBundle
この行を次のように変更します。
public class AccountImplMsgBundle_it extends AccountImplMsgBundle
AccountImplMsgBundle_it.javaファイルで、デフォルトのコンストラクタを修正します。
public AccountImplMsgBundle(){}
この行を次のように変更します。
public AccountImplMsgBundle_it(){}
これで、 例21のような、翻訳されたメッセージ・バンドルを作成できます。
package toystore.model.businessobjects.common;
|
メッセージ・バンドル・クラスは、フレームワークのベース・クラスとして用意されている
oracle.jbo.common.JboResourceBundleを拡張するため、メッセージの「マージ」機能を継承することができます。 マージは、メッセージ・バンドルの
getContents()メソッドから
super.getMergedArray()を呼び出したときに実施されます。 これによって、メッセージ・バンドル・クラスには翻訳が必要なメッセージのみを記述し、そのほかのメッセージは、スーパークラスから継承することができます。 メッセージのマージが確実に機能するように、翻訳版のリソース・バンドルには、オーバーライドされた
getContents()メソッドも含めるようにします。
| 注意 |
properties ファイルと同様に、メッセージ・バンドルの日本語版を作成する場合は、
|
ビュー・オブジェクトも、ロケールによって異なるコントロール・ヒントの値を格納するため、カスタム・メッセージ・バンドルをサポートしています。
some.pkg.ViewObjectNameというビュー・オブジェクトの場合は、カスタム・メッセージ・バンドルは
some.pkg.common.ViewObjectNameRowImplMsgBundle.javaという名前になります。
表3は、関連するカスタム・メッセージ・バンドルを持つADF ToyStoreサンプル・アプリケーションのコンポーネントを示しています。
| toystore.model.* コンポーネント | 含まれている内容 |
|---|---|
*.businessobjects.Account
|
|
*.businessobjects.Item
|
Listprice属性に対する書式マスク、および数値フォーマッタ・クラスを表します。
|
*.businessobjects.Orders
|
validateCreditCardExpiration()検証メソッドによってスローされる、カスタム検証エラー・メッセージが含まれています。
|
*.businessobjects.Signon
|
|
*.dataaccess.ShoppingCart
|
Listpriceおよび
ExtendedTotal属性に対する書式マスク、および数値フォーマッタ・クラスを表します。
|
*.dataaccess.Accounts
|
Statusおよび
Userid属性を非表示にするためのコントロール・ヒント。
|
モデル層をサポートしているビジネス・サービスは、アクセスしているWeb層からリモートに配置されることもありえるため、モデル層は、ユーザーのロケールを、ユーザー・セッションごとに追跡します。
ADFBindingFilterも、同じような言語の優先機能を実装しており、ADFバインディング・コンテキスト上に現在の言語情報を設定します。 そこで、ADFの各データ・コントロールは、そのバインディング・コンテキストのロケールを取得します。
Oracle XSQLページは、XMLベースの情報のフレキシブルな出力手段の一つで、 Oracle XML Developers Kit for Javaにバンドルされています。これを使用すると、XMLベースのページ・テンプレートを作成でき、その中にデータ連携をサポートする「アクション・ハンドラ」タグを組み込むことができます。 これには、SQL問合せや、ストアド・プロシージャ、Webサービス、および他のソースからXMLコンテンツを引き出すための、多数の組込みのアクション・ハンドラが付随しており、XSLT変換と組み合せることで、要求に応じたさまざまな種類のデータ・フォーマットを生成できます。 最も一般的なフォーマットはHTML、XML、またはシンプル・テキストですが、PDF(Apache FOPと組み合せた場合)、SVG、WML、および他のフォーマットにも対応できます。
組込みのアクション・ハンドラ・タグだけではニーズに合わない場合は、独自のカスタム・アクション・ハンドラを記述(『Oracle XML Developer's Kit プログラマーズ・ガイド』の「XSQL Pages パブリッシング・フレームワーク」章の「カスタムXSQL アクション・ハンドラの作成」を参照)して、それによって、要求に見合うようなXMLデータを取得したり、他のプログラム・タスクをXSQLページ処理の一部として実行するように、パブリッシング・エンジンを拡張することができます。
ADF Business ComponentsにはXMLメッセージの入出力サポート機能があるため、それを利用することで、ADFビュー・オブジェクトからXML形式でデータを出力する、XSQLのカスタム・アクション・ハンドラを簡単に作成できます。 例22は、この処理に必要なコードを示しています。
package toystore.fwk.xsql;
|
| 注意 |
この資料作成時点では、XMLの読み書きをサポートしているデータ・コレクションはADF Business Componentsのビュー・オブジェクトだけです。したがって、このコードでは、名前を指定したイテレータに関連付けられているデータ・コントロールが、ADF Business Componentsベースのデータ・コントロール (
|
このカスタム・アクション・ハンドラは、次の構文を使用してXSQLページ・テンプレートの内部で使用することができます。
<xsql:action handler="toystore.fwk.xsql.ADFViewObject" iterator="YourIteratorName"/>
以降の項では、このハンドラの面白い使い方についていくつか説明します。
たとえば、ユーザーにXMLフォーマットで注文の内容を確認して欲しい場合があります。 これは、ワークフローのような自動化プロセスの一環として使用される場合であり、この方式を使うと、発行した注文の監査をある程度自動化できます。 一般的に、2つのプログラムでXMLをやり取りしなければならない場合は、交換するXMLの構造を記述したXMLスキーマの認識が必要です。その後、実行時に、このスキーマに準拠したXMLドキュメントを交換します。
JDeveloperのXMLスキーマ設計ツールを使用して、 図57のような「Toystore Orders」XMLスキーマを設計しました。 これによって、XMLスキーマの低レベルの詳細についてほとんど知識がなくても、XMLスキーマを作成できました。
角の丸い長方形のボックスは、スキーマのXML要素を表しています。 Orderのid属性などの属性は、これらの要素のボックス内でネストされています。 ある要素に、サブ要素のシーケンスを含める必要がある場合は、コンポーネント・パレットから シーケンス・コネクタ
を図中にドラッグ&ドロップします。また、この中には、 PersonTypeや
ItemTypeなどのカスタム・タイプを定義してあります。これらのカスタム・タイプは、 OrderedBy要素(PersonTypeの再利用)や
Line要素(ItemTypeの再利用)で行ったように、スキーマの他の部分で再利用できます。
このXMLスキーマに準拠した注文確認のXMLデータを提供するために、次の処理を行いました。
/revieworderxml DataPageを作成します。
revieworderAsXML.xsqlを指定します。
/revieworderxml DataPageをもう一度クリックし、構造ウィンドウの「UIモデル」タブをクリックします。「<バインディングなし>」と表示されます。
ReviewOrderデータ・コレクションに対するイテレータ・バインディングを作成します。
<xsql:action>タグをXSQLページに追加し、それに対して、使用するイテレータの名前を指定します。
<Page xmlns:xsql="urn:oracle-xsql">
<xsql:action handler="toystore.fwk.xsql.ADFViewObject"
iterator="ReviewOrderIterator"/>
</Page>
ベースとなる
ReviewOrderビュー・オブジェクトには、バインド変数を使用するための問合せがあります。 前の例で行ったように、バインド変数を設定するためには、オーバーライドされた initializeModelForPage()メソッドを使用します。 そのため、 /revieworderxmlに対するDataForwardActionクラスをカスタマイズし、このサンプル・アプリケーションにある ReviewOrderActionクラスを作成しました。このクラスは、具体的には、次のようになります。
package toystore.controller.strutsactions;
import oracle.adf.controller.struts.actions.DataActionContext;
public class ReviewOrderAction extends ToyStoreServiceDataForwardAction {
/**
* Model initialization logic for this page.
*/
protected void initializeModelForPage(DataActionContext ctx) {
String id = ctx.getHttpServletRequest().getParameter("id");
getToyStoreService(ctx).prepareToShowReviewOrder(id);
}
/**
* Illustrate using an alternative mechanism, implemented in the
* base ToyStoreServiceDataForwardAction class, to release all
* data controls in use by the current binding container in
* stateless mode.
*/
protected boolean releaseStateless() {
return true;
}
}
これにより、次のURLを使用して /revieworderxmlデータページにアクセスしようとすると
http://localhost:8988/ADFToyStore/revieworderxml.do?id=1001
例23で見るような結果が表示され、ADFビュー・オブジェクトで生成される標準のXMLフォーマットが反映されます。
<Page>
|
これを、XMLスキーマの規約で期待されるフォーマットに変換するには、 例24に示されているようなXSLTスタイルシートを作成する必要があります。このスタイルシートでは、前述のXML出力を「Toystore Orders」のXMLスキーマ準拠の構文に変換します。
<xsl:transform version="1.0"
|
最後に、XSQLページ・テンプレートを補強して、このXSLTスタイルシートで、クライアントに返す前にスムーズにXSQLデータページを変換できるようにする必要があります。 このためには、次のように revieworderAsXML.xsqlページの最初に特別な行を追加することが必要です。
<?xml-stylesheet type="text/xsl" href="revieworderASXML.xsl"?>
テンプレートにこの行を追加すると、XMLの注文確認のリクエストによって、次のようなスキーマ準拠のデータグラムが生成されます。
<Order id="1001" xsi:schemaLocation="urn:oracle-toystore-order schemas/Order.xsd"
xmlns="urn:oracle-toystore-order"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<OrderDate>2004-06-15</OrderDate>
<OrderTotal>19.98</OrderTotal>
<OrderedBy>
<GivenName>Jay</GivenName>
<FamilyName>Tooey</FamilyName>
</OrderedBy>
<Lines>
<Line id="1">
<ItemId>EST-27</ItemId>
<Description>White Dice</Description>
<Quantity>1</Quantity>
<UnitPrice>3.99</UnitPrice>
<LineTotal>3.99</LineTotal>
</Line>
<Line id="2">
<ItemId>EST-18</ItemId>
<Description>Apollo-13 Rocket</Description>
<Quantity>1</Quantity>
<UnitPrice>15.99</UnitPrice>
<LineTotal>15.99</LineTotal>
</Line>
</Lines>
</Order>
ADF ToyStoreのWebサイトで注文を発行すると、ユーザーにはブックマーク可能なリンクが表示され、そこで自分の注文確認ができます。 この /revieworderページを、前述の
/revieworderxmlアクションに類似したXSQLテンプレートに関連付けられているDataPageとして設計しました。 モデル層で必要な設定は、前述のXMLを利用した注文確認の例と同じであるため、すでに学習した同じ ReviewOrderActionクラスを再利用できます。この設定のために、Strutsページフロー図で /revieworder DataPageに対して、ダブルクリックして新しいアクション・クラスを作成するのではなく、プロパティ・インスペクタを使用して、 typeプロパティの値を
toystore.controller.strutsactions.ReviewOrderAction(前述と同じクラス)に設定しました。
revieworder.xsqlテンプレートは、XSLTスタイルシートの名前が違うことを除けば、前項で学習したものと同じです。
<?xml-stylesheet type="text/xsl" href="revieworder.xsl"?>
<Page xmlns:xsql="urn:oracle-xsql">
<xsql:set-stylesheet-param name="orderId" value="{@id}"/>
<xsql:action handler="toystore.fwk.xsql.ADFViewObject"
iterator="ReviewOrderIterator"/>
</Page>
これを配置して、注文を発行した後でADF ToyStoreの「Thank You」ページからリンクをクリックすると、
図58のようなXML/XSLTベースの注文確認が表示されます。
./WEB-INF/xsqlディレクトリの
revieworder.xslスタイルシートについて調べると、HTMLを生成するスタイルシートは、XMLを他のXMLフォーマットに変換するために使用する場合と同じくらい簡単であることがわかります。 変換元のXMLドキュメントはADFビュー・オブジェクトによるXML出力で、例23で見たものと同じです。