Oracle ADF UIX開発者ガイド | ![]() 目次 |
![]() 前へ |
![]() 次へ |
このトピックでは、Webページのグループ化およびページ・フローの制御を可能にするOracle ADF UIXサーブレットの概要を示します。 UIXサーブレットを使用してイベントを処理し、ページをブラウザに戻す方法を説明します。 つまり、UIXサーブレットを使用すると、UIXのすべての要素を使用してアプリケーションを作成することができます。
注意: Oracle ADF UIXの以前のリリースには、J2EE Webアプリケーションの構築用に「UIX Controller」というUIXサーブレット・テクノロジが採用され、推奨されていました。 UIX Controllerは、Model-View-Controllerデザイン・パターンに基づいて、完全な機能を備えたUIXアプリケーションを構築するためのページ・フロー・エンジンとして推奨されていました。 JDeveloper 10gでは、Strutsを優先するため、ページ・フロー・エンジンとしてのUIXサーブレットの役割は重視されていません。 イベント処理、部分ページ・ユーティリティなどのADF UIXサーブレットの他の機能は、Strutsに同等のものが存在しないためこれまでどおり推奨しています。このヘルプでは引き続き、WebアプリケーションのコントローラとしてのUIXサーブレットの役割について説明します。 今後のバージョンには、UIXテクノロジの中核機能として継続して使用されるUIXサーブレットの機能に関する、更新されたドキュメントが付属する予定です。
ここでは、次の項目について説明します。
UIXサーブレットとは何でしょうか。 UIXサーブレットは、Model-View-Controller(MVC)デザイン・パターンに基づくWebアプリケーション・テクノロジです。 UIXビュー・レイヤーであるUIX Componentsテクノロジについてはすでに紹介しました。 UIXでモデル・レイヤーのために用意されている汎用抽象化機能についても説明しました。これはデータ・バインディングと呼ばれ、任意のモデル・コードをUIX XMLに連結できます。 UIXサーブレットはコントローラ・レイヤーです。ここでビューのイベントがモデルを変更し、モデルのデータがビュー間のフローを制御します。 UIXサーブレットでは、個々のWebページをグループ化してアプリケーションを形成し、プログラマがページ・フローを制御するためのインタフェースを提供します。
図4-1: Webアプリケーション
図4-1: Webアプリケーションは、単純なアプリケ―ションの概要図です。 ここでは、5つのページからWebアプリケーションが形成されています。 矢印はページ間のリンクを表しています。 これはHTMLハイパーリンクを使用して簡単に実行できます。では、UIXサーブレットはどこに使用するのでしょうか。 多くの場合、ユーザーがリンクをクリックして表示されるページは、ユーザーが入力したデータ、またはサーバーの状態によって変わります。 たとえば、ユーザーが入力したデータが無効な場合は、エラー・ページを表示する必要があります。 次のステップに進む前に、ユーザーの入力データの処理が必要な場合もあります。 たとえば、ショッピング・カート・アプリケーションでは、ユーザーが次に進む前に、入力データをサーバーに保存する必要があります。 ここでUIXサーブレットが必要になります(図4-2: UIXサーブレットのページ・フローを参照)。 UIXサーブレットは、サーバーに常駐して各リクエストを調べます。 ユーザーが入力したデータ、サーバー側の状態(データベースなど)、および開発者提供のハンドラから返される結果に基づいて、UIXサーブレットで(処理中のデータを操作して)次に表示するページが決定されます。
図4-2: UIXサーブレットのページ・フロー
UIXサーブレットはJavaで作成され、Javaサーブレットで動作するように設計されています。 独自のスタンドアロンUIXServlet
、またはその他のサーブレット(JSPなど)で使用できます。
各HTMLページを作成するために使用するテクノロジは、UIXサーブレットとは関係ありません。 UIXサーブレットでは、JSPを使用してHTMLを作成できます。または、素のHTMLファイルも使用できます。 UIXサーブレットでは、HTML以外のページ(GIFイメージなど)も使用できます。 ただし、UIXサーブレットは、UIX ComponentsおよびUIX XMLをサポートし、統合できるように特別に設計されています。 UIXファイルの解析やキャッシュを自動的に実行したり、UIX Componentsをコールして、UIX ComponentsやUIX XMLの文書をHTMLに変換できます。 このトピックでは、UIXサーブレットをUIX Componentsと組み合せて使用し、UIXページをレンダリングします。
次に、単純なUIXサーブレットおよびUIX XMLページを示します。 UIXサーブレットのすべてのページは、page
要素で開始する必要があります(この要素はUIXサーブレット・ネームスペースにあることに注意してください)。 このpage
要素にはcontent
子要素があります。 UIX ComponentsおよびUIX XMLの要素は、UIXサーブレットのこのcontent
要素に子として追加されます(デフォルトのネームスペースがUIXサーブレットからUIX Componentsに変わることに注意してください)。
<page xmlns="http://xmlns.oracle.com/uix/controller"
expressionLanguage="el">
<content>
<header xmlns="http://xmlns.oracle.com/uix/ui"
text="UIX Components Header Bean">
<contents>
<stackLayout>
<contents>
UIX Components Stuff
<link text="This is a link" destination="www.cnn.com"/>
</contents>
</stackLayout>
</contents>
</header>
</content>
</page>
補足として、データ・バインディングを含む例も次に示します。
<page xmlns="http://xmlns.oracle.com/uix/controller">
<content>
<header xmlns="http://xmlns.oracle.com/uix/ui"
text="UIX Components Header Bean">
<contents>
<dataScope>
<contents>
<link text="${uix.data.dat1.text1}" destination="${uix.data.dat1.dest1}"/>
<link text="${uix.data.dat1.text2}" destination="${uix.data.dat1.dest2}"/>
</contents>
<provider>
<data name="dat1">
<inline text1="Oracle" dest1="http://www.oracle.com"
text2="Cnn" dest2="http://cnn.com" />
</data>
</provider>
</dataScope>
</contents>
</header>
</content>
</page>
厳密には、UIXサーブレットのページはpage
要素で始まる必要はありません。UIX Componentsの要素で開始することもできます。 ただし、UIXサーブレットのページ・フロー・システムを最大限に活用するには、page
要素を使用する必要があります。
UIXでこれらの例を実行する場合は、filename.uix
などのファイルを作成し、コンテンツを追加して、サーブレット・エンジンの所定のサブフォルダにあるWebアプリケーション・ディレクトリ(demos
など)にそのファイルを入れます。 たとえば、サーブレット・エンジンがOC4Jで
/servletEngine/demos/somepath/filename.uix
にファイルを入れた場合、ブラウザでhttp://myHostName:8888/somepath/filename.uix
にアクセスするとファイルを表示できます。
従来、静的HTMLページのリンクは、ハードコードされたHTMLリンクに対して設定されていました。 つまりページ・フローが静的であり、1つのリンクをクリックすると常に同一のHTMLページが表示されます。 しかし、ほとんどのWebアプリケーションでは、リンクがアクティブになった際、またはフォームが送信された際に、表示するページを動的に選択する必要があります。 この選択は、ユーザーが送信したデータ、またはサーバーの状態に基づいて行われます。 この選択時に、サーバーの状態が変わる場合があります。
UIXサーブレットを使用すると、特殊な処理(前述を参照)を必要とするリンク(またはフォーム送信)を簡単に指定できます。 すべてのクライアント・リクエストはUIXサーブレットによって受信され、デフォルトで、クライアント・リクエストで指定されたページがUIXサーブレットによって提供されます。 ただし、UIXサーブレットでは、イベントと呼ばれる特殊な信号を認識します。 リンク(またはフォーム送信)は、イベントを起動するようにエンコードできます。 ユーザーがそのようなリンクをクリックすると、UIXサーブレットでは、アプリケーション固有のコードを実行してイベントを処理し(処理中のサーバー側の状態も変更され)、結果を使用して、イベントに応答してレンダリングするページを決定します。
イベントは、アプリケーションがUIXサーブレットと対話して、ユーザーの入力に基づいて状態を変更するための手段です。 UIXサーブレット・フレームワークでのサーバー側の状態の変更はすべて、イベント・ハンドラによって実行されます。 UIXサーブレットのイベントはサーバー側で発生します。クライアント側のJavaScriptイベントとは異なりますので注意してください。
UIXサーブレットのイベントには名前と0以上のパラメータが付いています。 イベント名は文字列です。パラメータは単純な名前と値のペア(通常の形式のパラメータと同様)で、文字列名と文字列値を関連付けます。 UIXサーブレット・イベントをカプセル化するJavaインタフェースはPageEvent
です。 イベント名は(このクラスの)getName()
メソッドによって返され、パラメータ値はgetParameter(String key)
メソッド(key
はパラメータ名)をコールして取得できます。 次のセクションでは、イベントの生成方法について説明します。
UIXサーブレットのイベントのURLは、event
フォーム・パラメータを使用したフォーム送信に似ています。 通常、イベントを構築するには、次のとおりsubmitButton
を使用します。
<!-- This is pageName.uix ---->
<page xmlns="http://xmlns.oracle.com/uix/controller"
xmlns:ctrl="http://xmlns.oracle.com/uix/controller">
<content>
<header xmlns="http://xmlns.oracle.com/uix/ui"
text="Enter Your Name">
<contents>
<form name="form1">
<contents>
<messageTextInput name="txt1" prompt="Enter Name" text="YourName" />
<submitButton event="StoreName" text="Submit" />
</contents>
</form>
</contents>
</header>
</content>
</page>
上のUIX XMLで作成されるページには、2つのフォーム要素を持つフォームが使用されています。 1つはmessageTextInput
要素で、もう1つはsubmitButton
です。 form
要素にdestination属性がないことに注意してください。 宛先の設定がない場合、UIXサーブレットでは現在のページが自動的に使用されます。 ユーザーが送信ボタンを押すと、ブラウザによって次のURLが生成されます。
http://hostname:port/pageName?event=StoreName&txt1=YourName
UIXサーブレットではこのリクエストを、起動されたイベントと解釈します。 イベントの名前はStoreName
で、名前がtxt1
、値がYourName
(ユーザーがテキスト・フィールドに入力した任意の値)のパラメータが1つあります。 生成されたイベントのサーバーでの処理方法については後述しますが、その前に、イベントを生成するその他の方法を説明します。
イベントを起動するためにsubmitButton
要素を使用する必要はありません。 URLの"destination"
属性をサポートするすべてのUIX要素では、UIXサーブレットのイベントもサポートされます。 次に例を示します。
<link text="Trigger Event Foo"
destination="${ctrl:eventUrl(uix,'Foo')}" />
上のリンクをクリックすると、名前がFoo
であるイベントが起動されます。 eventUrl
関数については後述します。
次の例のように、イベントを直接エンコードする可能性も考えられます。 これはお薦めしません。ページ・イベントの抽象化が損なわれるため、一部のエンコーディング・メカニズムが実行されないからです。
<!-- ******* Do not try to fire events like this!! ********* -->
<formValue name="event" value="StoreName" />
かわりに、次の形式を使用します(encodeParameter
関数については後のセクションを参照)。
<formValue name="${ui:encodeParameter(uix,'event')}"
value="StoreName" />
UIX Componentsの特定の要素によってイベントが生成されることに注意してください。たとえば、NavigationBarBean
ではgoto
という名前のイベント、TotalRowBean
ではtotal
イベント、SortableHeaderBean
ではsort
イベント、そしてAddTableRowBean
ではaddRows
イベントが生成されます。 これらはUIXサーブレットを使用せずに処理できますが、UIXサーブレット・フレームワークにこれらすべてを適切に統合できます。
イベントの生成方法を理解したところで、UIXサーブレットでのイベントの処理方法に進みます。 まずUIXのイベント・ハンドラの登録について説明し、次に、Javaでのイベント・ハンドラの作成について説明します。
イベント・ハンドラはイベントを処理するコード部分です。 UIXサーブレットでは、特定のイベントを処理するためにどのイベント・ハンドラをコールするかを認識している必要があります。 このために、各イベント・ハンドラをUIXサーブレットに登録します。 次のUIX XMLファイルは、UIXでの登録例を示しています。
<!-- ********* This is FirstPage.uix ********-->
<page xmlns="http://xmlns.oracle.com/uix/controller"
xmlns:ctrl="http://xmlns.oracle.com/uix/controller">
<content>
<header xmlns="http://xmlns.oracle.com/uix/ui"
text="Enter Your Name">
<contents>
<form name="form1">
<contents>
<messageTextInput name="txt1" prompt="Enter Name" text="YourName" />
<submitButton event="StoreName" text="Submit" />
</contents>
</form>
</contents>
</header>
</content>
<handlers>
<event name="StoreName">
<method class="MyClass" method="handleStoreNameEvent" />
</event>
</handlers>
</page>
このコードでは、UIXサーブレットのpage
要素の新しい子要素、handlers
要素が使用されています。 このページのすべてのハンドラは、この要素の子としてリストされます。 現在、UIXサーブレットのハンドラは、イベント・ハンドラの1種類のみです。
イベント・ハンドラは、UIXサーブレットのevent
要素を使用して定義されます。 event
要素にはname
属性があり、この属性にはハンドラで処理できるUIXサーブレット・イベントを指定します。 上の例では、このページで発生するすべてのStoreName
イベントを処理するようにハンドラを登録しています。
event
要素の内部に、イベント・ハンドラ要素を追加してハンドラ・コードを指定します。 そのような要素の1つが、UIXサーブレットのmethod
要素です。 この要素では、完全修飾されたJavaクラス名とメソッド名を使用してこのメソッドをコールし(Java Reflection APIを使用)、それぞれのイベントを処理します。 上の例では、メソッドMyClass.handleStoreNameEvent(...)
は、このページで発生するすべてのStoreName
イベントを処理するためにコールされます。
イベント・ハンドラ・コードを参照するもう1つの方法は、UIXサーブレットのinstance
要素を使用することです。 次の例では、メソッドMyClass.getStoreNameEventHandler
を使用して、(イベントを直接処理するのではなく)イベント・ハンドラを生成しています。 このメソッドによって返されるイベント・ハンドラが、実際にイベントを処理するために使用されます(詳細は次のセクションを参照してください)。 method
属性は省略される場合があります。この場合、UIXサーブレットではデフォルトのメソッド名sharedInstance
を使用するか、最終的にはデフォルトの(引数なし)コンストラクタをコールします。
<handlers>
<event name="StoreName">
<instance class="MyClass" method="getStoreNameEventHandler" />
</event>
</handlers>
UIXサーブレットのnull
要素は、イベントは生成されるがイベント処理コードが記述されていないことを示す場合に便利です。 UIXサーブレットで、受信したイベントのイベント・ハンドラを検出できない場合、UIXサーブレットではUnhandledEventException
をスローして、開発者にエラーを警告します。 このような場合、null
イベント・ハンドラを使用して、サーバー側で処理することなくユーザーは該当ページを見つけることができます。 次の例のように使用します。
<handlers>
<event name="someEvent">
<null/>
</event>
</handlers>
UIXは完全に拡張可能です。また、UIX XMLに新しい要素を追加して、前述したもの以外のイベント・ハンドラの登録をサポートすることもできます。 UIXの拡張の詳細は、「ADF UIXの拡張」を参照してください。
ここでは、イベント・ハンドラを登録するその他の方法について説明します。 次のイベント・ハンドラは"*"の下に登録されます。 このイベント・ハンドラは、明示的ハンドラを持たないイベント名で、イベントがページで起動されるたびにコールされます。 このタイプのイベント・ハンドラは、デフォルト・イベント・ハンドラとも呼ばれます。 次の例では、UIXサーブレットのnull
ハンドラがデフォルト・ハンドラとして登録されます。 対応するハンドラがないすべてのイベントが、null
によって処理(つまり無視)されます(UnhandledEventException
は隠されますが、コーディング・エラーもマスクされてしまいます)。
<page xmlns="http://xmlns.oracle.com/uix/controller" >
<handlers>
<event name="*">
<null/>
</event>
</handlers>
</page>
イベント・ハンドラは、イベント名null
の下に登録することもできます。 このキーワードは特別であり、イベント・ハンドラがnullイベント・ハンドラとして登録されます(UIXサーブレットのnull
要素とは異なります)。 nullイベント・ハンドラがコールされるのは、ページでイベントが起動されなかった(つまり、ページのURLにイベント信号がエンコードされなかった)場合です。
<page xmlns="http://xmlns.oracle.com/uix/controller" >
<handlers>
<event name="null">
...
</event>
</handlers>
</page>
イベントが生成されるとき、イベントを起動したコンポーネントは通常、source
イベント・パラメータで識別されます。 コンポーネントによってsource
パラメータを異なる方法で設定します。たとえば、table
のname
属性や、hideShow
のid
属性(不一致についてはお詫びします)は、これらのコンポーネントで生成されたすべてのイベントのsource
パラメータとなります。 source
パラメータの設定方法については各コンポーネントのドキュメントを参照してください。
特定のコンポーネントで生成されたイベントのイベント・ハンドラを、このsource
パラメータを使用して登録することが可能です。 次の例では、table1
コンポーネントで生成されたgoto
イベントを処理するように、ハンドラが登録されています。
<page xmlns="http://xmlns.oracle.com/uix/controller" >
<handlers>
<event name="goto" source="table1">
<!-- handle the "goto" event generated by "table1" -->
...
</event>
<event name="goto">
<!-- handle all other "goto" events -->
...
</event>
</handlers>
</page>
前述の例が示すとおり、名前およびソースの両方で登録されたイベント・ハンドラは、名前のみで登録されたイベント・ハンドラより優先順位が高くなります。
次の例のように、複数のイベント・ハンドラを異なるイベント名の下に登録したり、同一のイベント・ハンドラで異なるイベントを処理するように登録できます。
<page xmlns="http://xmlns.oracle.com/uix/controller" >
<handlers>
<event name="goto" source="searchResults">
<!-- This handler performs the record navigation for the 'searchResults'
table -->
<method class="MyClass" method="navigateResults" />
</event>
<event name="goto" source="navBar">
<!-- This handler responds to the event generated by a navigationBar -->
<method class="MyClass" method="navigatePageFlow" />
</event>
<event name="total">
<method class="MyClass" method="submitHandler" />
</event>
<event name="submit">
<method class="MyClass" method="submitHandler" />
</event>
</handlers>
</page>
複数のソースにおける複数のイベントの単一イベント・ハンドラを、イベント名およびソースの空白区切りリストを使用して登録することが可能です。 次の例では、hgrid1およびhgrid2の2つのコンポーネントにおける4つのイベント(expand、focus、expandAllおよびcollapseAll)を処理するように、handleHGridEvent method
ハンドラが登録されています。
<handlers>
<event name="expand focus expandAll collapseAll"
source="hgrid1 hgrid2">
<!-- Handle the HGrid events for 'hgrid1' and 'hgrid2'
components -->
<method class="MyClass" method="handleHGridEvent" />
</event>
</handlers>
グローバルなイベント・ハンドラを登録することもできます。これは、どのページから起動されたかにかかわらず、指定の名前のイベントを処理します。 これらは特定のページに所属しないため、ページのUIX XMLから登録することができません。 かわりにJavaコードを使用して登録します(oracle.cabo.servlet.AbstractPageBroker
を参照してください)。 これらの登録メカニズムは、UIXサーブレットのJava APIを使用して、すべてUIX XMLの外側から使用できます。
イベント・ハンドラを登録する様々な方法を見てきました。次は、一般的なイベントの処理方法について説明します。 説明は次のセクションにあります。
前のセクションでは、メソッドMyClass.handleStoreNameEvent(...)
を、FirstPage.uixのイベント・ハンドラの例として紹介しました。 次に、このクラスのJavaソース・コードを示します。
import javax.servlet.http.HttpSession;
import oracle.cabo.servlet.Page;
import oracle.cabo.servlet.BajaContext;
import oracle.cabo.servlet.event.PageEvent;
import oracle.cabo.servlet.event.EventResult;
public class MyClass
{
/** This is handleStoreNameEvent version 1 */
public static EventResult handleStoreNameEvent(BajaContext context,
Page page,
PageEvent event)
{
String userName = event.getParameter("txt1");
HttpSession session = context.getServletRequest().getSession(true);
session.putValue("User Name", userName);
Page nextPage = new Page("NextPage");
return new EventResult(nextPage);
}
}
最初に注意する必要があるのはメソッドのシグネチャです。 UIXサーブレットのmethod
要素には次のシグネチャが必要です。
public static EventResult methodName(BajaContext context,
Page page,
PageEvent event) throws Throwable;
context
は現在のBajaContext
です(BajaContext
オブジェクトでは、リクエストごとのアプリケーション状態のデータすべてをカプセル化し、サーブレット・ベースのアプリケーションを作成するために必要なグローバル・サービスへのアクセスを提供します)。page
はイベントが発生したページです(Page
オブジェクトについては後のセクションを参照)。event
では、起動されたイベントをカプセル化します(例では、PageEvent
についてgetParameter(String)
メソッドをコールして、イベント・パラメータへのアクセス方法を示しています)。
メソッドでは、EventResult
を返す必要があります。これは、現在のPageFlowEngine
(後のセクションを参照)によって使用され、次に表示するページが決定されます。 メソッドでjava.lang.Throwable
をスローする場合があります。UIXサーブレットではThrowable
を捕捉し、エラー・ページにリダイレクトします。
MyClass.handleStoreNameEvent
に戻ります。このメソッドは、ユーザーが入力した名前(txt1
というテキスト入力フォーム要素)を取得することによって開始し、このユーザー名をHttpSession
に保存します。 その後で、次のPage
(ページ・フロー内)をEventResult
オブジェクトでラップし、それを返します。 現在のPageFlowEngine
がこの結果オブジェクトを調べ、含まれているPage
をレンダリングします(ページ・フロー・エンジンについては後述します)。
イベント・ハンドラでは、HttpServletRequest
ではなく、PageEvent
でgetParameter
メソッドを使用して、パラメータにアクセスします。 (HttpServletRequest
は、getServletRequest()
メソッドを使用して、現在のBajaContext
から取得できます。) パラメータにアクセスする際は、PageEvent
を抽象オブジェクトを利用することで、UIXによるファイルのアップロードが正常に機能し、パラメータが適切に正しいキャラクタ・セットにデコードされることが保証されます。 また、PageEvent
抽象オブジェクトによって、キーと値のペアの追加、削除、またはマージなど、パラメータの変換を1つの場所で集中的に実行できます。この際、イベント処理コードを編集する必要はありません。
UIXサーブレットのinstance
要素の例を思い出してください。 この例では、メソッドMyClass.getStoreNameEventHandler()
をコールして、EventHandler
を生成しています。 この(ページの解析時に1回のみコールされる)staticメソッドでは引数を使用できません。また、次のコード例と同じシグネチャがあります。
import oracle.cabo.servlet.event.EventHandler;
public class MyClass
{
public static EventHandler getStoreNameEventHandler()
{
...
}
}
返されるEventHandler
は、イベントを処理するために使用されます。 また、EventHandler
インタフェースによって次のメソッドが公開されます(UIXサーブレットのmethod
イベント・ハンドラのシグネチャと似ています)。
public interface EventHandler
{
public EventResult handleEvent(
BajaContext context,
Page page,
PageEvent event) throws Throwable;
}
UIXサーブレットのinstance
ハンドラの方が、対応するmethod
イベント・ハンドラよりも若干速く実行される場合があります。これは、instance
ハンドラがJava Reflection APIのイントロスペクション・コストの影響を受けないためです。 ただし、差はごくわずかであり、ほとんどのアプリケーションでは認識されません。
さらに複雑なイベント処理に進む前に、UIXサーブレットのPage
オブジェクトについて説明する必要があります。 次のセクションで説明します。
Page
オブジェクト UIXサーブレットでは、URIのようにリソースを識別するためにPage
オブジェクトが使用されます。 各Pageには(getName()
メソッドによって取得される)名前があり、アプリケーション内のUIXサーブレットのページを一意に識別します。 UIXサーブレットに送信されるすべてのリクエストが自動的にデコードされ、リクエストのソースを識別するPageオブジェクトが作成されます。
Page
にはテキスト・プロパティも設定できます。 これらのプロパティは、単純な文字列の名前と値のペアです。 プロパティの設定と取得は、setProperty(String key, String value)
メソッドおよびgetProperty(String key)
メソッドで行われます。 プロパティ値は必ずしも文字列である必要はありません。文字列としてエンコードできる値であれば、プロパティとして設定できます(Page
のJava APIを参照)。
Page
プロパティはURLに直接エンコードできます。そのURLのデコードによって生成される、対応するPage
オブジェクトには、それらのプロパティがすでに設定されています。 つまり、プロパティの状態は、クライアント・ブラウザとの送受信の前後も維持され、ブックマーク済リンクとしても保存されます。 この機能により、ページ・プロパティを使用して、サーバー側の記憶域を使用せずにユーザー固有の状態を保持できます。ユーザーの状態はプロパティとしてURLに保存されます。 次の例では、イベントとページのプロパティを使用して、サーバー側の状態を使用せずにユーザーの情報を蓄積します。
import oracle.cabo.servlet.Page;
import oracle.cabo.servlet.BajaContext;
import oracle.cabo.servlet.event.PageEvent;
import oracle.cabo.servlet.event.EventResult;
public class MyClass
{
/** This is handleStoreNameEvent version 2 */
public static EventResult handleStoreNameEvent(BajaContext context,
Page page,
PageEvent event)
{
String userName = event.getParameter("txt1");
Page nextPage = new Page("NextPage");
nextPage.setProperty("UserName", userName);
return new EventResult(nextPage);
}
public static EventResult handleStoreAgeEvent(BajaContext context,
Page page,
PageEvent event)
{
String userName = page.getProperty("UserName");
String age = event.getParameter("age");
Page nextPage = new Page("FinalPage");
nextPage.setProperty("UserName", userName);
nextPage.setProperty("Age", age);
return new EventResult(nextPage);
}
}
メソッドMyClass.handleStoreNameEvent(...)
がイベント・ハンドラとしてFirstPage.uixで使用されたことに注意してください。 上のコードは、最初のhandleStoreNameEvent(...)
メソッドとは少し異なります。 まず、新しいコードではHttpSession
が使用されていません。 このコードでは、状態がサーバー側に格納されません。 かわりに、ページ・プロパティを使用してURLに状態をエンコードします。
これは、ページ・フロー内の次のページを参照する新しいPage
を作成することにより行われます。 UserName
プロパティは、このPage
に設定され、Page
はEventResult
として返されます。
この新しいPage
ではNextPage.uixを参照します。form
要素のdestination
属性は(このUIX XMLファイルには)設定されていません。 このため、UIXサーブレットでは、UserName
プロパティが設定される現在のPage
をデフォルトで参照します。 これによって、このフォームの送信時、宛先URLにはUserName
プロパティがエンコードされます。 また、このフォームを送信すると、StoreAge
イベントが起動され、MyClass.handleStoreAgeEvent(...)
によって処理されます。
<!-- ********* This is NextPage.uix ******** -->
<page xmlns="http://xmlns.oracle.com/uix/controller"
xmlns:ctrl="http://xmlns.oracle.com/uix/controller">
<content>
<header xmlns="http://xmlns.oracle.com/uix/ui"
text="Enter Your Data">
<contents>
<form name="form1" >
<contents>
<messageTextInput name="age" prompt="Enter your age" />
<submitButton event="StoreAge" text="Submit" />
</contents>
</form>
</contents>
</header>
</content>
<handlers>
<event name="StoreAge">
<method class="MyClass" method="handleStoreAgeEvent" />
</event>
</handlers>
</page>
ここで、MyClass.handleStoreAgeEvent(...)
について考えてみます。 ユーザー名は、現在のページのプロパティから入手されます。 これは(Age
プロパティとともに)フローの次のページに追加され、そのまま継続します。 これは、ページ・プロパティを使用して、サーバー側のメモリーを使用せずにクライアントの状態を保持する例です。 1つの大きな短所は、この方法で保持できる状態はテキスト形式の状態のみであることです。 もう1つの短所は、プロパティの名前と値が暗号化されないため、機密情報(パスワードやクレジット・カード情報など)の格納に使用できないことです。 また、URLは長さが制限されています。Internet Explorer 5は各URLを約2000バイトまでサポートしていますが、他のブラウザはわずか255バイトしかサポートしていない場合があります。 このため、一度に使用できるページ・プロパティの数は厳しく制限されます。
上の例では、エンコードされたPage
URLがフォーム送信の宛先として使用されています。 UIX XMLでは、その他の要素(リンクなど)の宛先としてPage
URLを使用することが必要な場合があります。 この場合、次の例のようにctrl:pageUrl
関数を使用します。
<page xmlns="http://xmlns.oracle.com/uix/controller"
xmlns:ctrl="http://xmlns.oracle.com/uix/controller">
<ctrl:content xmlns="http://xmlns.oracle.com/uix/ui" >
<link text="Go to the next page"
destination="${ctrl:pageUrl(uix,'NextPage')}" />
</ctrl:content>
</page>
同様に、次のコード例のように、要素のsource
属性をPage
URLに設定できます。
<frame source="${ctrl:pageUrl(uix,'Tree')}" name="TreeFrame" />
いくつかのページ・プロパティとページ・パラメータの組合せを含むイベントを設定する場合、<ctrl:pageURL>
BoundValue要素を使用する必要があります。 次に例を示します。
<link text="Approve and go to next" >
<boundAttribute name="destination">
<ctrl:pageURL event="approve">
<ctrl:properties>
<ctrl:property key="uid" value="1234"/>
<ctrl:property key="gid" value="643"/>
</ctrl:properties>
<ctrl:parameters>
<ctrl:parameter key="nextStep=" value="shipping"/>
</ctrl:parameters>
</ctrl:pageURL>
</boundAttribute>
</link>
Javaを使用して、データ・プロバイダ内にURLをエンコードすることもできます。
Page
のURLエンコーディングは、PageEncoder
を使用して取得できます。 現在のPageEncoder
にアクセスするには、現在のBajaContext
についてgetPageEncoder()
をコールします。 次の例について考えてみます。
import oracle.cabo.ui.RenderingContext;
import oracle.cabo.ui.data.DataObject;
import oracle.cabo.servlet.Page;
import oracle.cabo.servlet.BajaContext;
import oracle.cabo.servlet.ui.BajaRenderingContext;
public class MyDataProvider
{
public static DataObject getProductIDEncoder(RenderingContext context,
String namespace,
String name)
{
return _DATA_OBJECT;
}
private static final DataObject _DATA_OBJECT = new DataObject()
{
public Object selectValue(RenderingContext context, Object key)
{
BajaContext bajaContext = BajaRenderingContext.getBajaContext(context);
Page page = new Page("Products"); // refers to Products.uix
if (key != null)
page.setProperty("productID", key.toString());
String encoding = bajaContext.getPageEncoder().encodePageURL(page);
return encoding;
}
};
}
上のコードでは、UIX Componentsのデータ・プロバイダが実装されています。 その基本機能は、指定のkey
を特定のプロパティとしてPage
にエンコードし、そのPage
のエンコーディングを返すことです。 このメソッドでBajaRenderingContext.getBajaContext(...)
をコールしていることに注意してください。 UIX Componentsを使用して構築したページをUIXサーブレット環境で実行している場合、このメソッドを使用して、現在のBajaContext
を取得できます(同様に、BajaRenderingContext.getPage(...)
を使用して現在のPage
を取得できます)。
次のUIX XMLコードでは、前述のデータ・プロバイダを使用してリンクの宛先を求める方法を示します。 最初のリンクには、オラクル社内でのUIX Componentsの製品コードがエンコードされ、2番目のリンクにはJEWTの製品コードがエンコードされます。 この例の宛先ページ(Products.uix
)は、Page
オブジェクトの正確な製品IDにアクセスでき、適切なデータをレンダリングします。
<dataScope xmlns="http://xmlns.oracle.com/uix/ui">
<contents>
<link text="Goto UIX Components product page"
destination="${uix.data.productID.768}" />
<link text="Goto JEWT product page"
destination="${uix.data.productID.927" />
</contents>
<provider>
<data name="productID">
<method class="MyDataProvider" method="getProductIDEncoder" />
</data>
</provider>
</dataScope>
ページ・プロパティには状態を保存するための優れた面があります。 これはURL内部で直接指定されるため、サーバー上のどの状態を保持する必要もありません。 また、ユーザーが「戻る」および「進む(次)」ボタンをクリックするとそれに応じてURLも変わるため、それらのクリックについて懸念する必要もありません。
ただし、これらの利点は同時にページ・プロパティの制限事項でもあります。 前述したように、ページ・プロパティは文字列である必要があり、重要な情報は保存しない方が賢明で、またURLの長さの制限という問題もあります。 これらの問題はすべて、値をHttpSession
に入力することだけで解決できます。 ただし、その場合、後で従来の「戻る」/「進む(次)」の問題に直面します。ユーザーがWebブラウザで「戻る」ボタンをクリックしても、サーバーの状態は戻ることはありません。 なんらかの処理を元に戻したとユーザーは考えますが、サーバーの状態はそのように機能していないのです。 ページのState
APIによってこれらの問題をすべて処理できます。
値を直接Page
オブジェクトやHttpSession
内に入力するかわりに、State
オブジェクト内に入力した後、そのState
オブジェクトをPage
にアタッチします。 UIXサーブレットがこのState
オブジェクトを管理し、短いID文字列をURLにエンコードして、UIXサーブレットがStateオブジェクトを再配置できるようにしています。
State
オブジェクトはStateManager
によって作成および管理されます。 現在のStateManager
を取得するには、BajaContext.getStateManager()
をコールします。 その次に、getNewState()
を使用してState
オブジェクトを取得します。
BajaContext context = ...;
// Pass "true" to force the StateManager to be created
StateManager stateManager = context.getStateManager(true);
// Create a mutable State object.
MutableState state = stateManager.getNewState();
// Store some state - any Object, not just Strings
state.putValue(someKey, someValue);
// And attach the State
page.setState(state);
State
オブジェクトには、可変および不変の2つの形態があります。 State
オブジェクトが最初にStateManager以外で作成されると、State
オブジェクトとして返され、これによってPage
にアタッチする前に値を設定できます。 その次に続くリクエストで、ページからState
オブジェクトを取り出し、値を取得できますが、値を変更することはできません。 不変のState
オブジェクトの値を変更する必要がない場合は、StateUtils.cloneState()
関数を使用することもできます。
// We want to modify a pre-existing State object; clone it
// into a MutableState object
State oldState = page.getState();
MutableState newState = StateUtils.cloneState(stateManager, oldState);
state.putValue(someKey, someValue);
page.setState(newState);
handleStoreNameEvent()
コードを変更して、State
APIを使用するようにします。
/** This is handleStoreNameEvent version 3 */
public static EventResult handleStoreNameEvent(BajaContext context,
Page page,
PageEvent event)
{
String userName = event.getParameter("txt1");
Page nextPage = new Page("NextPage");
// Store the user name inside a State object instead of
// as a page property
StateManager stateManager = context.getStateManager(true);
MutableState state = stateManager.getNewState();
state.putValue("UserName", userName);
nextPage.setState(state);
return new EventResult(nextPage);
}
アプリケーションは通常、State
オブジェクトの欠落を正常に処理します。 後述する理由のため、ユーザーが「戻る」ボタンをクリックしすぎるとState
オブジェクトが使用できなくなる可能性があります。
このアーキテクチャの実装を理解しておくと役に立つ場合があります。 State
オブジェクトの実装方法に関心がない場合はスキップしてください。 StateManagers
はデフォルトでHttpSession
に保存されています。 これは、サーバー上でステートフルな動作が必ず必要なことを意味しています。 また、ユーザー間でStateオブジェクトを共有できないことも意味しており、これは各ユーザーが個別のStateManager
と個別のIDを持っているためです。 またセキュリティ面では、HttpSession
IDをスプーフィング(偽造)できないように設定することは、State
IDもスプーフィング不可能になることを意味しています。
デフォルトのStateManager
実装では、ユーザーに対して作成されたすべてのState
オブジェクトが保持されるわけではありません。 保持していると、ユーザーのログイン時間が長くなればなるほど、より多くのメモリーがサーバー上で必要になるという結果になります。 かわりに、固定された長さの、State
オブジェクトの先入れ先出しキューが保持されます。 デフォルトでは、このキューは20個の状態を保持できます。つまり、21番目のState
オブジェクトが指定したユーザーに対して作成されると、1番目のオブジェクトは削除されます。 実作業では、問題が発生する前に、ユーザーは「戻る」ボタンを19回までクリックできることを意味しています。 State
は、そのIDが要求されたもののみがキューに入るため、作成された後すぐに削除されたState
オブジェクトはこの制限には含まれません。 キューの長さはBaseBajaContext.createStateManager()
をオーバーライドすることで調整できます。
ここまでで、アプリケーションのページ間を移動しながら、イベントやページのプロパティおよび状態を使用して、操作を実行したり、状態を保持する方法を学びました。 次のセクションでは、UIXサーブレットのPageFlowEngine
について説明します。これは、ログインの実行などさらに複雑なフローをサポートします。
PageBroker
はUIXサーブレットの最上位インタフェースです。 各UIXアプリケーションにはPageBroker
インタフェースを実装するオブジェクトのインスタンスが1つあり、それは、アプリケーション・ロジックへの主なエントリ・ポイントとして動作します。 PageBroker
は次の操作を処理します。
アプリケーションの最初のエントリ・ポイントとなるため、各リクエストの開始および終了方法のみでなく、アプリケーションの初期化および停止方法も提供されます。
UIXで提供されたデフォルト・サーブレット(UIXServlet
)は、サーブレット構成パラメータのoracle.cabo.servlet.pageBroker
を使用してPageBroker
を取得します。 次に、WEB-INF/web.xml
ファイルを使用する場合の構成例を示します。
<servlet>
<servlet-name>uix</servlet-name>
<!-- We'll use the default UIX servlet -->
<servlet-class>oracle.cabo.servlet.UIXServlet</servlet-class>
<!-- And we'll use a custom page broker -->
<init-param>
<param-name>oracle.cabo.servlet.pageBroker</param-name>
<param-value>yourPackage.YourPageBroker</param-value>
</init-param>
</servlet>
PageBroker
はいつでも直接実装できますが、UIXには有用な基本実装も用意されています。
oracle.cabo.servlet.AbstractPageBroker
クラスは、UIXサーブレットを使用するユーザーのための有用な抽象ベース・クラスです。 PageBroker
全体に対する多数のデフォルト実装が提供されます。 たとえば、これを使用するとファイル・アップロードのサポートが容易になります。必要なのはdoUploadFile()
メソッドのオーバーライドのみで、このメソッドは、サーブレットに送られる各ファイルごとに1回コールされます。
PageBroker
の基礎的な役割をすべてサポートする有用なベースの提供に加えて、AbstractPageBroker
は重要な抽象の1つ、PageDescription
をUIXサーブレットに追加します。 UIXサーブレットではイベント処理とユーザー・インタフェースを、純粋なModel-View-Controllerアーキテクチャとして完全に分離できますが、多くの開発者にとっては、ページのイベント処理とそのユーザー・インタフェースを関連付けた方が便利であり、PageDescription
はこの結合を担います。
PageBrokers
の機能はこの他にもありますが、このセクションでは最後に、UIXサーブレットに用意されている、おそらく最も有用なPageBroker
実装であるUIXPageBroker
について簡単に説明します。 このクラス(oracle.cabo.servlet.xml.UIXPageBroker
が正式名)は、UIX XMLファイルをUIXサーブレットに結び付ける役割を担当します。 XMLファイルを探し、それをPageDescription
に解析してから結果をキャッシュすることをサポートし、UIXの処理のカスタマイズに必要なすべてのツールを提供します。 UIXサーブレットでUIX XMLを使用していると、アプリケーションではほとんどの場合、UIXPageBroker
が使用されるか、それがサブクラス化されます。
ページ・フロー・エンジンは、ページ・アクセスおよびページ・フローを制御します。 ここで、クライアント・ブラウザのリクエストに対してどのページをレンダリングする必要があるかを決定します。 フロー・エンジンでは、イベント・ハンドラによって生成されるEventResult
の解釈も行います。
ページ・フロー・エンジンは交換可能です。ユーザーは、oracle.cabo.servlet.event.PageFlowEngine
インタフェースを実装することによってエンジンをプラグインできます。 また、次の要素をアプリケーションのweb.xml
ファイルに追加する必要があります。
<init-param>
<param-name>oracle.cabo.servlet.event.PageFlowEngine</param-name>
<param-value>mypackage1.mypageFlowEngine</param-value>
</init-param>
mypackage1はエンジンのパッケージ名で、mypageFlowEngineはエンジンのクラス名です。
このセクションでは、UIXサーブレットに含まれているデフォルトのページ・フロー・エンジン、TrivialPageFlowEngine
を紹介します。
EventResult
オブジェクト TrivialPageFlowEngine
では、EventResult
に格納されているオブジェクトを宛先のPage
として解釈します。 このPage
がイベントに応答してレンダリングされます。 EventResult
のこのような使用例は、前のセクションで示しています(MyClass.handleStoreNameEvent(...)
を参照)。
スーパークラス(BasePageFlowEngine
)でも、プロパティとしてEventResult
が現在のBajaContext
に格納されます(すべてのPageFlowEngine
でこれを実行する必要があります)。 EventResult.getEventResult(BajaContext)
を使用してこの結果を取得します。 たとえば、EventResult
には、次のコードを使用してUIX Componentsのデータ・プロバイダからアクセスできます。
public static DataObject getDataObject(RenderingContext context,
String namespace,
String name)
{
BajaContext bajaContext = BajaRenderingContext.getBajaContext(context);
EventResult result = EventResult.getEventResult(bajaContext);
...
}
上の例では、UIXサーブレットのイベント・ハンドラからUIX Componentsのデータ・プロバイダにデータを送信する方法を示しています。 EventResult
を使用して、UIXサーブレットのイベント・ハンドラとUIXファイルの間で情報を直接交換することもできます。 UIXファイルは、暗黙変数であるuix.eventResult
を使用して、現在のEventResult
にアクセスできます。 このMap
の各キーと値は、対応するEventResult
プロパティの名前と値にマップされます。 次に例を示します。
<page xmlns="http://xmlns.oracle.com/uix/controller"
xmlns:ctrl="http://xmlns.oracle.com/uix/controller">
<content>
<header xmlns="http://xmlns.oracle.com/uix/ui"
text="Update The Counter">
<contents>
<form name="form1">
<contents>
<messageStyledText prompt="Counter is at"
text="${uix.eventResult.counter}" />
<submitButton event="inc"
text="Increment Counter" />
</contents>
</form>
</contents>
</header>
</content>
<handlers>
<event name="inc">
<method class="MyClass" method="doIncCounter" />
</event>
</handlers>
</page>
上の例では、messageStyledText
のtext
属性は、UIXサーブレットの現在のEventResult
のcounter
プロパティにデータ・バインドされています。 ユーザーが送信ボタンをクリックすると、inc
イベントが起動され、次のイベント・ハンドラがコールされます。
import javax.servlet.http.HttpSession;
import oracle.cabo.servlet.Page;
import oracle.cabo.servlet.BajaContext;
import oracle.cabo.servlet.event.PageEvent;
import oracle.cabo.servlet.event.EventResult;
public class MyClass
{
private static int _counter=0;
// Multiple threads may call this method and mutate _counter.
// Therefore, this method must be synchronized.
public static synchronized EventResult doIncCounter(BajaContext context,
Page page,
PageEvent event)
{
_counter++;
EventResult result = new EventResult(page);
result.setProperty("counter", new Integer(_counter));
return result;
}
}
このイベント・ハンドラは、内部の_counter
変数を増分し、それをEventResult
のプロパティとして設定します。 この結果は現在のPage
を指します。 このPage
では、イベントの起動と同じページを指定するため、そのページが再び表示されます。ただし、今回はカウンタが増分されています。
EventResult
オブジェクトでは、プロパティとして汎用Java Object
の名前と値のペアを指定できます(Page
オブジェクトでは、文字列の名前と値のペアしか指定できません)。 ただし、EventResult
のプロパティを使用できるのは、ページがレンダリングを終了するまでの間だけです。一方、Page
オブジェクトのプロパティは、次のイベント・ハンドラでも使用できます。
EventResult
で返されるPage
は、イベントを起動したPage
と異なる場合があります。 ただし、クライアント側のブラウザでは、ページが切り替えられていることを知る手段がないことに注意してください。 次に例を示します。
public static EventResult handleEvent(BajaContext context,
Page sourcePage,
PageEvent event)
{
// Render NextPage.uix in response to this event
Page nextPage = new Page("NextPage");
return new EventResult(nextPage);
}
この例では、ブラウザに表示されるURLはsourcePage
のURLで、nextPage
のURLではありません。 ブラウザに表示されるURLが、サーバーでレンダリングされたページと一致することが重要な場合は、クラスoracle.cabo.servlet.util.RedirectUtils
を使用して、ブラウザが正しいページをリダイレクトするようにします(これによってサーバーへの2回目のリクエストというコストが発生することに注意してください)。 次に例を示します。
public static EventResult handleEvent(BajaContext context,
Page sourcePage,
PageEvent event)
{
Page nextPage = new Page("NextPage");
// Force the browser to redirect to NextPage.uix in response to this
// event
Page redirectPage = RedirectUtils.getRedirectPage(context, nextPage);
return new EventResult(redirectPage);
}
TrivialPageFlowEngine
によるログイン・ページ
ページ・フロー・エンジンの非常に便利な機能は、特定のページへのアクセス制御です。 たとえば、クライアントがサーバーに認証されないかぎり、特定のページの表示が許可されない場合があります。 すべてのクライアント・リクエストは、イベントの有無にかかわらず、PageFlowEngine
を経由します。 このため、すべてのアクセス制御をここで集中管理して、多数のページに処理を分散する場合よりも性能を向上できます。 このインタフェースの実装は、OracleのSingle Sign-On Serverなどのログインまたはアクセス制御テクノロジと統合できます。
TrivialPageFlowEngine
では、ログイン・ページ・フローの非常に基本的な実装が提供されます。 TrivialPageFlowEngine
でメソッドsetLoginPage(Page loginPage)
をコールすると(またはサーブレット構成パラメータoracle.cabo.servlet.loginPage
を設定すると)、アクセス制御が使用可能になったことがこのエンジンに通知されます。認証されていないクライアントからのリクエストの場合、最初に要求されたURLに関係なくloginPage
がレンダリングされます。
loginPage
がレンダリングされ、クライアントが認証を受けると、TrivialPageFlowEngine
は最初のリクエストURLに対応するページにリダイレクトします。
TrivialPageFlowEngine
では、クライアントが認証されているかどうかをどのように認識するのでしょうか。 これは、特定のキーのHttpSession
値を調べることで行います。 値が存在する場合、対応するクライアントは認証されているとみなされます。 この値の取得に使用されるキーは、TrivialPageFlowEngine
でメソッドsetLoggedInKey(String)
を使用して(またはサーブレット構成パラメータoracle.cabo.servlet.loggedInKey
を設定して)設定されます。
単純な例を次に示します。 最初に、適切なサーブレット構成パラメータを設定する必要があります。 ここに示す例では、バージョン2.2の任意のサーブレットAPIエンジン(Tomcatなど)でサポートされるweb.xmlファイルが使用されています。 そのため、サーブレット構成パラメータは次のように設定されます。
<init-param>
<param-name>oracle.cabo.servlet.loginPage</param-name>
<param-value>Login</param-value> <!-- corresponds to Login.uix -->
</init-param>
<init-param>
<param-name>oracle.cabo.servlet.loggedInKey</param-name>
<param-value>MyClass.isLoggedIn</param-value>
</init-param>
上の構成では、Login.uixがログイン・ページとしてTrivialPageFlowEngine
に設定され、キーは文字列MyClass.isLoggedIn
に設定されています。 ここでUIXサーブレットでは、すべてのクライアント・リクエストについてすべてのHttpSession
でこのキーを調べます。 このキーに関連する値がnullの場合、またはHttpSession
が作成されていない場合、クライアントは一時的に次のログイン・ページにリダイレクトされます。
<!-- This is Login.uix ---->
<page xmlns="http://xmlns.oracle.com/uix/controller"
xmlns:ctrl="http://xmlns.oracle.com/uix/controller">
<content>
<header xmlns="http://xmlns.oracle.com/uix/ui"
text="Login">
<contents>
<!-- method=post is used here so that the password
is not displayed in the URL -->
<form name="form1" method="post">
<contents>
<messageTextInput name="username" prompt="Enter Name" />
<messageTextInput name="password" prompt="Enter Password"
secret="true" />
<submitButton event="login" text="Login" />
</contents>
</form>
</contents>
</header>
</content>
</page>
ユーザーがユーザー名とパスワードを入力してから送信ボタンをクリックすると、login
イベントがただちに起動され、次のイベント・ハンドラによって処理されます。 パスワードが正しい場合、なんらかのオブジェクトがHttpSession
HttpSession
のログイン・キーMyClass.isLoggedIn
に関連付けられます。 これによって、このクライアントが認証されていることがTrivialPageFlowEngine
に通知され、ユーザーが最初にリクエストしたページがエンジンによってレンダリングされます。
import javax.servlet.http.HttpSession;
import oracle.cabo.servlet.Page;
import oracle.cabo.servlet.BajaContext;
import oracle.cabo.servlet.event.PageEvent;
import oracle.cabo.servlet.event.EventResult;
public class MyClass
{
public static EventResult handleLogin(BajaContext context,
Page page,
PageEvent event)
{
String userName = event.getParameter("username");
String password = event.getParameter("password");
if (password.equals(getPasswordForUser(userName)))
{
HttpSession session = context.getServletRequest().getSession(true);
session.putValue("MyClass.isLoggedIn",
new Object()); /** this will be some object that holds
all of this user's server-side state */
}
return null;
}
}
HttpSession
の使用方法 サーバー上でクライアントの状態を保持するために、多数のアプリケーションでHttpSession
オブジェクトを利用します。 サーバーでは、どのHttpSession
がどのクライアントに関連付けられているかを認識する必要があります。一般的な方法としては、Cookieをクライアント・ブラウザに設定し、サーバーでクライアントを認識できるようにします。
URLエンコーディングは、ページのすべてのURLにIDを組み込むことでクライアントを識別するもう1つの方法です。この場合、ユーザーがリンクをクリックすると、IDを含むURLがサーバーに送信され、クライアントおよび関連付けられたHttpSession
をサーバーで識別できるようになります。 この方法は、Cookieをサポートしないブラウザ(またはCookieのサポートが無効になっているブラウザ)で使用されます。
クライアント・ブラウザでCookieがサポートされない場合、UIXサーブレットで使用されるURLEncoder
により、サーブレット・セッションIDがURLに自動的にエンコードされます。 UIXサーブレットではテストを実行して、URLエンコーディングが必要であることを確認します。URLエンコーディングが必要になるのは、クライアント・ブラウザでCookieをサポートせず、ユーザー・アプリケーションでHttpSession
を必要とする場合のみです。このため、URLエンコーディングが機能するためには、レンダリングが行われる前、つまりレンダリング・サイクルが開始される前に、ユーザーがjavax.servlet.http.HttpServletRequest
でgetSession(true)
をコールする必要があります(HttpSession
がレンダリング・サイクル中に作成されると、サイクルの最初にレンダリングされたURLがエンコードされない場合があります)。
URLエンコーディングが予定どおりに機能するためには、次の2つのルールに従う必要があります。
HttpServletRequest
でgetSession(true)
をコールしないでください。 セッションが必要な場合は、getSession(false)
をコールし、HttpSession
がnullの場合はデフォルト値を使用します。
getSession(true)
をコールして(必要に応じて)セッションを作成できます。 イベント・ハンドラはレンダリング・サイクルの一番始めにコールされるため、この方法は安全です。
すべてのページでHttpSession
が必要な場合、HttpSession
が必ず1つ作成されるように明示的にアプリケーションをコーディングできます。 最も単純な方法は、PageBroker
をサブクラス化することです。 この場合は、requestStarted()
メソッドが最適です。
public class YourPageBroker extends SomeBuiltInPageBroker
{
public void requestStarted(BajaContext context)
{
// Force the creation of an HttpSession
context.getServletRequest().getSession(true);
}
}
State
オブジェクトはHttpSession
に依存するため、レンダリング中にState
オブジェクトを作成することも不可能です。 つまり、DataProviders
でState
オブジェクトを作成することはできません。 イベント処理の最中か、レンダリングの直前に作成する必要があります。
UIXサーブレットによって、UIXの便利な拡張機能が提供されているため、UIXサーブレット・フレームワークに関連する重要なオブジェクトに、UIX XMLファイルからアクセスする場合があります。 このような拡張機能は、暗黙変数および関数という2つのカテゴリに分けられます。 このトピックのすべての例では、XMLネームスペースの接頭辞ctrl
はUIXサーブレットのネームスペース(http://xmlns.oracle.com/uix/controller)に対応し、接頭辞ui
はUIX Componentsのネームスペース(http://xmlns.oracle.com/uix/ui)に対応しています。
このセクションでは、UIXサーブレットのデータ・プロバイダによって実装される暗黙変数を示します。 次に、messageTextInput
要素のtext
を、UIXサーブレットの現在のPage
のusername
プロパティに設定する例を示します。
<messageTextInput prompt="Your Name"
text="uix.pageProp.username" />
uix.pageProp
Page
です。 各キーはPage
プロパティ名にマップされ、対応する値を返します。
uix.pageState
Page
に関連付けられているState
です。 各キーはState
選択キーにマップされ、対応する値を返します。
uix.eventResult
EventResult
です。 各キーはEventResult
プロパティ名にマップされ、対応する値を返します。 このデータ・オブジェクトの使用例は、「ページ・フロー・エンジン」のセクションを参照してください。
sessionScope
javax.servlet.http.HttpSession
オブジェクトです。 各キーは属性名にマップされ、それぞれの値を返します。 セッションが作成されなかった場合、すべてのキーがnullを返します。 「UIXサーブレットでのHttpSession
の使用方法」のセクションを参照してください。
requestScope
javax.servlet.ServletRequest
です。 各キーは属性名にマップされ、それぞれの値を返します。 ServletRequest
パラメータへのアクセスは提供されません。属性へのアクセスのみが提供されます。
applicationScope
これは、現在のjavax.servlet.ServletContext
です。Webアプリケーション全体に対してグローバルであり、すべてのユーザーに共有されます。 各キーは属性名にマップされ、それぞれの値を返します。
UIXファイルで使用可能なUIXサーブレットの関数を次に示します。
ctrl:destination
(UIImplicitObject uix, String pageName, String eventName)
<!-- Home.uix?event=init -->
<link text="Cancel"
destination="${ctrl:destination(uix,'Home','init')}" />
ctrl:page
(UIImplicitObject uix, String pageName)
Page
へのURLを作成します。
<frame source="${ctrl:pageUrl(uix,'Home')}" /> <!-- Home.uix -->
ctrl:eventUrl
(UIImplicitObject uix, String eventName)
<link text="Add Item"
destination="${ctrl:eventUrl(uix,'addItem')}" />
<button text="Remove" destination="${ctrl:eventUrl(uix,'delItem')}" />
ctrl:parsePage
(UIImplicitObject uix, String pageName)
UINode
ツリーのルートで取得を行うために使用されます。 これは、UIX Componentsのinclude
要素とともに使用して、次の例のようにUIXファイルを組み込むことができます。
<!-- includes TabBar.uix -->
<include node="${ctrl:parsePage(uix,'TabBar')}" />
UIXサーブレットでは、ファイルのアップロードもサポートされます。 サーブレットの初心者には意外かもしれませんが、サーブレットはGET HTTPリクエストおよびPOST HTTPリクエストを効率よく自動的に処理しますが、ファイルのアップロードは処理しません。 Servlet APIでは、ファイルのアップロードは各開発者が構成する必要があります。 ただし、ユーザーがmultipart/form-data
リクエスト形式を解析する必要はありません。これは自動的に処理されます。
UIからは、簡単にファイルのアップロードを起動できます。 次の2つの手順を実行するのみです。
<form>
で、usesFileUpload
をtrue
に設定します。<fileUpload>
要素または<messageFileUpload>
要素を追加します。多くの開発者にとってファイルのアップロードを理解する際の最大の問題は、ファイルのアップロードのパラメータが、アップロードするファイルと混合してしまうことです。 パラメータを抽出する唯一の方法は、リクエスト全体を読むことですが、これを行うにはファイルのアップロード時にそのファイルを処理する必要があります。 これはある重要な点を意味します。ファイルのアップロードは、イベント・ハンドラでは処理できないということです。 PageEvent
が作成され、すべてのパラメータが処理されると、アップロードされたファイルがすでに渡されたことを意味します。 このため、ファイルのアップロードの処理には、FileUploadManager
という異なる方法を使用する必要があります。
UIX PageBroker
にはFileUploadManager
が1つのみ存在します。 次のいずれかのものを使用して、マネージャを登録できます。
AbstractPageBroker.setFileUploadManager()
oracle.cabo.servlet.io.FileUploadManager
サーブレット初期化パラメータ
<init-param>
<param-name>oracle.cabo.servlet.io.FileUploadManager</param-name>
<param-value>yourPackage.YourFileUploadManager</param-value>
</init-param>
FileUploadManager
はサブクラス化できますが、大部分のUIX開発者は、BaseFileUploadManager
をサブクラス化します。 このサブクラスでは、次に示すとおり、単一のメソッドをオーバーライドする必要があるのみです。
protected abstract String doUploadFile(
BajaContext context,
Page page,
MultipartFormItem item) throws IOException;
UIXは、ファイルごとにdoUploadFile()
を1回コールします。 MultipartFormItem
インタフェースは、ファイルの読込みおよびファイル名とMIMEタイプの検出のためのメソッドを提供します。 doUploadFile
の戻り値は、すべてのファイルのアップロードの完了時に作成されるPageEvent
にマージされます。 このパラメータの名前は、(MultipartFormItem.getFilename()
と混同されないように)MultipartFormItem.getName()
の値になり、<fileUpload>
要素のname
から導出されます。
簡単な例として、ファイルを常に共通ディレクトリにアップロードするFileUploadManager
実装を示します。 このディレクトリ自体が、サーブレット構成パラメータを使用して構成されています(このパラメータを指定しない場合、ファイルが一時ディレクトリに格納されます)。 各ファイルの書込み後に、そのファイルのフルパスおよびファイル名が返されます。これによって、EventHandler
で、書込み直後のファイルを検出できます。
package yourPackage;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import javax.servlet.ServletConfig;
import oracle.cabo.servlet.BajaContext;
import oracle.cabo.servlet.Page;
import oracle.cabo.servlet.io.BaseFileUploadManager;
import oracle.cabo.share.util.MultipartFormItem;
public class YourFileUploadManager extends BaseFileUploadManager
{
public void init(
ServletConfig config)
{
// Get the directory for an initialization parameter
_directory = config.getInitParameter("UploadDirectory");
}
protected String doUploadFile(
BajaContext context,
Page page,
MultipartFormItem item) throws IOException
{
File file;
if ((_directory != null) && !"".equals(_directory))
{
file = new File(_directory, item.getFilename());
}
else
{
// If we haven't been told where to save the file,
// save it as a temporary file.
file = File.createTempFile(item.getFilename(), null, null);
}
// Write out the file
OutputStream stream = new BufferedOutputStream(
new FileOutputStream(file));
item.writeFile(stream);
stream.close();
// Return the file name, so that the file can be found after
//
return file.getName();
}
private String _directory;
}
これは単なる例であるため、いくつかの機能が省略されています。 たとえばこの例では、実行後にクリーンアップが行われず、アップロードされるファイルのサイズにも制限がありません。
UIXサーブレットを使用していない場合、またはファイルのアップロードを独自に処理する場合、oracle.cabo.share.util.MultipartFormHandler
クラスを使用する必要があります。 その場合、UIXサーブレット・アーキテクチャに簡単に接続することはできなくなりますが、解析コードおよびMultipartFormItem
APIはそのまま使用できます。
BC4Jを使用している場合、UIXには、interMediaをサポートするFileUploadManager
が含まれています。詳細は、「ADF UIXでのメディアの使用」のトピックを参照してください。
ここまでで、UIXサーブレットの各部分がどのように機能するかを詳しく説明してきました。次は、これらの部分をどのように組み合せるか(誰がページ・フロー・エンジンをコールするか)について説明します。 これらのUIX XMLページは、実際にはどのようにHTMLに変換されるのでしょうか。また、UIXサーブレットではJSPまたはJavaコードを使用してどのようにHTMLを作成するのでしょうか。 このセクションではこれらの質問に回答し、UIXサーブレットを構成するいくつかのJava APIを紹介します。
図4-3: UIXサーブレットの概要
すべてのリクエストは、クライアント・ブラウザでリクエストをWebサーバー(Apacheなど)に発行することで開始されます。リクエストはWebサーバーによりサーブレット・エンジン(TomcatまたはJServなど)に渡され、そこから最終的にサーブレット(またはJSP)に渡されます。 サーブレットは、このリクエストがUIXサーブレットのリクエストであることを認識し、リクエストをBajaContext
にカプセル化して、PageBrokerHandler
というUIXサーブレットの小さいクラスに転送します。
このハンドラにより、リクエストURIがUIRサーブレットのPage
オブジェクトおよびPageEvent
に変換され、PageBroker
に転送されます。 これによって、イベント処理とページ・レンダリングのためにページ・ブローカが2回コールされます。 (このように明示的に分かれているため、開発者はコントローラとビュー・コードを分割する必要があります。) 細かい点ですが、イベントがない場合にもイベント処理フェーズは発生します。 これにより、アクセス制御(ログイン・ページへの転送など)の一貫性がすべてのリクエストについて保たれます。
すべてのUIXサーブレット・アプリケーションには1つのPageBroker
があります。 これは、各リクエストのUIXサーブレットへの最初のエントリ・ポイントであり、UIXサーブレットのすべての永続オブジェクトはPageBroker
を経由します。 各ページのEventHandler
およびPageRenderer
(後者は後述を参照)は、PageBroker
によって検出されます。 ハンドラおよびレンダラの検出方法は、ページ・ブローカによって異なります。UIXPageBroker
クラスはUIX XMLファイルを検索します。
イベント処理フェーズでは、PageBroker
により、Page
、PageEvent
、および正しいEventHandler
がPageFlowEngine
に送信され、クライアント・リクエストに応じてレンダリングするPage
が決定されます。 この応答Page
はPageBrokerHandler
に返されます。 PageFlowEngine
では、エンド・ユーザーをあるページにアクセスさせない場合などに、イベントおよびイベント・ハンドラを無視できます。あるいは、ハンドラを使用してEventResult
を取得できます。 ただし、レンダリングするページを最終的に決定するのは、常にPageFlowEngine
です。
レンダリング・フェーズの場合、PageBrokerHandler
により、レンダリングされるPage
がPageBroker
に渡されます。 PageBroker
ではPageRenderer
を探して、ページをレンダリングするよう要求します。 レンダリングの方法は、PageRenderer
の実装によって異なります。 実装には次の処理を行うレンダラが含まれます。
... ただし、実装によっては動的にGIFイメージが作成され、XSLT変換の結果やその他の処理が実行される場合があります。 PageRenderer
の終了後、次のブラウザ要求が送信されるまでは、UIXサーブレットは終了し、処理が完了した状態になります。
ここまでで、UIXサーブレット・アプリケーションのほとんどの部分について説明してきましたが、全体図については説明していません。 開発チームはこれらの各部分をどのように組み合せてアプリケーション全体を作成するのでしょうか。 個々の開発者が果たす役割やアプリケーションのアーキテクチャは何でしょうか。 ここで簡単に全体図について説明します。
UIXサーブレットのUIX XMLアプリケーションは、次の3種類の開発者によって開発されます。
ユーザー・インタフェースの開発者は、アプリケーションのビューを作成します。 ユーザー・インタフェースを定義するUIX XMLページが出力されます。
モデルの開発者は、アプリケーションのモデルを定義します。 バックエンドを公開するJavaクラスが出力されます。 これらのクラスは他のアプリケーションで再利用できるように、UIXの影響を受けていないことが必要です。
最後がコントローラの開発者です。 ユーザー・インタフェースとモデルを統合します。 モデルをDataObject
に変換するUIX Componentsのデータ・プロバイダをコーディングし、モデルを更新し、ページ・フローを処理するUIXサーブレットのイベント・ハンドラをコーディングします。 エラー処理など、モデル・レイヤーに属さないページの一部に対して、カスタム・データ・プロバイダもコーディングします。 UIXサーブレットがあるのがモデル・レイヤーです。
もちろん、この3種類は実際の状況を単純化したものです。 開発チームには、国際化のための開発者や翻訳者も必要になります。 開発者が複数のレイヤーにわたって作業することもあります。たとえば、ほとんどのコントローラ開発者はデータ・バインディングをサポートするためにUIXを変更します。 ただし、チームのメンバー間でUIXの開発を分割する場合には適切な分類方法です。
Copyright © 2001, 2004, Oracle Corporation.
All rights reserved.