GAE開発の落とし穴
Googleのクラウド環境をつかったGoogle App Engineによる開発するにあたり、初めての試みで苦悩する開発者達の経験をもとに、各開発フェーズにあわせて問題点やどう解決したかをご紹介します
ブックマークされました!
ブックマークがエラーになりました。もう一度お願いします。

作者 Ashish Sarin , 翻訳者 岡田 英久 投稿日 2007年10月31日
この記事ではPortletsとFaceletsを使ったリッチなWebアプリケーションを作成するためにどのようにJSF、DWR、DOJOを組み合わせることが可能か、そのアプローチを提示します。読者にはこれらのフレームワークとそれらが提供する機能について基礎的な知識があることを前提としています。
この記事で議論するサンプルはプロダクト管理アプリケーションで、以下に挙げる機能をユーザに提供します。
図1はユーザがプロダクトタイプやサブタイプを選択したとき画面にどのように表示されるべきかを示したものです。
図 1. プロダクトサブタイプ詳細画面
クロスブラウザなツリー構造、タブ付きペイン、スプリットペイン、ポップアップメニューなどを作成するにはかなりの時間が必要ですし、経験豊富な JavaScript/DHTMLプログラマであって初めて成し遂げられるものです。これらのクロスブラウザなUIウィジェットを提供するツールキットはたくさん存在しますが、リッチなイベントハンドリングモデルを提供しているものは多くありません。以下には、ユーザの要求があったときにサンプルアプリケーションが応答する必要のあるいくつかのユーザアクションを一覧しています。
| ユーザアクション | アプリケーションの対応 |
| ツリーノードを選択 | そのノードに属するプロダクトサブタイプの一覧を表示 |
| ツリーノードを右クリック | 子要素となるプロダクトサブタイプの追加や選択したプロダクトサブタイプの削除などの選択肢をもったポップアップメニューの表示 |
| タブ付きペインで 'Add Product Subtype' タブを選択 | 新規プロダクトサブタイプの情報の入力フォームを表示 |
| ツリーを展開して子要素を表示させるために [+] ボタンをクリック | データベースから子要素の情報を読み込んでツリーに表示 |
DOJOはリッチなUIウィジェット(ツリーやタブ付きペインやポップアップメニューを含むが、それにとどまらない)を、サンプルアプリケーションでの利用に適したイベントハンドリングモデルと共に提供してくれるJavaScript/DHTMLツールキットです。
DWR(Direct Web Remoting)はJavaでAJAXアプリケーションを簡単に構築するためのAJAXフレームワークです。DWRは多くの機能を提供してくれます。以下にいくつか挙げますが、機能はこれら以外にもたくさんあります。
コンバータはDWRにおいて非常に重要な役割を果たし、きれいなプログラミングモデルを提供します。たとえば、ユーザが新しいプロダクトサブタイプの情報を入力し、それを保存するようにアプリケーションに要求したとき、Webレイヤでその情報を取り出す方法は2つあります。
後者のほうがきれいなプログラミングモデルを提供します。リクエストパラメータの取得を行いJavaで利用するDTOを自分で生成する必要はありません。
AJAXリクエスト処理では、ほとんどの時間がステータスコードの取得とJavaScriptコールバックメソッドでのメッセージやデータの取得、そしてユーザに何を見せて何を隠すかの決定に費やされます。そのようなシナリオではbeanコンバータはとても役に立ちます。
DOJOのリッチなイベントモデルを、JavaアプリケーションでAJAXリクエストを処理するためのDWRのクリーンなアプローチと組み合わせることで、DOJOのコンポーネントが生成したイベントをDWRに渡して処理するという高度にインタラクティブなWebアプリケーション(サンプルアプリケーションのような)を作成することができます。
カスタムJSFコンポーネントはDOJOのTreeやSplitContainerに必要なHTMLを生成するために使用します。JSFコンポーネントに生成されたHTMLは常にブラウザによって解析されます。Portalによってテキストとして出力されるのではありません。次に示すXHTMLファイルの断片は、DOJOのツリーやタブ付きペインのようなウィジェットを生成するためにカスタムJSFコンポーネントがどのように使用されるのかを記しています。
<div xmlns="http://www.w3.org/1999/xhtml"
...
...
xmlns:dojo="http://dojotoolkit.org/"
xmlns:mytree="http://mytree.com/tree"
xmlns:mytab="http://mypane.com/tabPane">
<ui:composition>
<ui:define name="body">
<f:view>
<h:form styleClass="form" id="formId">
<div dojoType="SplitContainer" orientation="horizontal" sizerWidth="5" activeSizing="false" style="overflow:
auto; whitespace: nowrap; height: 550px; background: transparent; padding: 5px;" >
<div dojoType="ContentPane" sizeShare="20"
style="overflow: auto; whitespace: nowrap;">
<mytree:treeComponent backingBeanName="treeBackingBean"></mytree:treeComponent>
</div>
<div dojoType="ContentPane" sizeShare="80" style="overflow: auto; white-space nowrap;">
<mytab:tabPaneComponent/>
</div>
</div>
...
...
ツリーノードの数が大きい(200以上)場合、カスタムJSFコンポーネントは200を超えるノードのためのコードを生成すべきではありません。ノード数が200を超えると、IEはページのロード時にこれらのウィジェットを生成するのにかなりの時間を要します。サンプルアプリケーションのカスタムJSFコンポーネントは(ルートレベルにおいて)100個のツリーノードしか生成しません。そして最後に「もっと見る」というオプションを表示します。ユーザが「もっと見る」を選択したら、DWRがデータベースから残りのノードに関する情報を取得します。この情報は、プログラムでTreeNodeウィジェットを生成するためにJavaScriptのコールバックメソッドに渡されます。
MyFacesもDOJOのTreeを生成するコンポーネントを提供していますが、MyFacesのコンポーネントは一度に全てのノードを生成します。ツリーノードが1,000個に達するとこのコンポーネントは使い物にならなくなるので、これはよいアプローチではありません。
DWRではJavaのクラスを作成してdwr.xmlファイルにそのクラスを設定することが要求されます。DWRはdwr.xmlで設定された名前に基づいてJavaScriptファイル(.js拡張子をもつ)を生成します。
dwr.xmlから抜粋した次の設定情報は、どのようにJavaのクラスを設定すればよいかを示しています。
<create creator="jsf" javascript="AjaxBean" scope="request">
<param name="managedBeanName" value="ajaxBean" />
<param name="class"
value="com.somebean.AjaxBean" />
</create>
creator="jsf"
これはJavaのクラスがJSF managed beanとして設定されているということを表します。JavaのクラスにはJavaScriptによって実行されることになる全てのAJAXメソッドが定義されています。
<param name="managedBeanName" value="ajaxBean" />
これはmanaged beanの名前がfaces-config.xmlファイル内でajaxBeanと設定されていることを表します。
<param name="class"
value="com. somebean.AjaxBean" />
これは実際のJavaのクラスを参照しています。
javascript="AjaxBean"
これはJavaのクラスがJavaScriptコードによって参照されるときの名前です。
JavaScriptコード内でAjaxBeanを利用するには、このJavaScriptを<script>タグを使ってインポートする必要があります。
DWRで利用されるJavaのクラス(ただのプレーンなJavaのクラスです。これらのクラスはDWR独自のインターフェースやクラスを何も実装していません)は、AJAXリクエストを受け取るたびにインスタンス化されます。ということは、アプリケーションがAjaxBeanクラス内に状態を保持しなければならないとしても、新しいリクエストが来るとそれは消えてしまいます。AjaxBeanのメソッドは事実上ステートレスであることが要求されており、対話状態はどこか別の場所に保持されなくてはなりません。
AJAXリクエストはPortletRequestではありません。これは、DWRで利用されるJavaのクラスはPortletSessionオブジェクトにはアクセスできないということです。AJAXリクエストは単なるHTTPリクエストなのでDWRで利用されるJavaのクラスは HttpSessionオブジェクトにアクセスすることが可能です。Portlet仕様によると、HttpSessionとPortletSession は同期がとられていなくてはなりません。たとえば、PortletSessionオブジェクトに属性を追加したら、それをHttpSessionオブジェクトにも追加しなくてはなりません(同じ名前である必要はないです)。
たとえばJBoss ASの場合、managed beanがsomeManagedBeanという名前でPortalSessionに追加されると、同じオブジェクトがjavax.portlet.p.
サンプルアプリケーションでは対話状態をsomeManagedBeanに保持します。DWRで利用されるJavaのクラスはHttpSessionから someManagedBeanのインスタンスを取得し、そのプロパティを設定してユーザのその時点での対話状態を反映させます。
ビジネスサービス(たとえばSpringレイヤ内の)にアクセスし相互に作用を及ぼすのにAjaxBeanはService Locatorパターンを使うことができます。
注意: JSF managed beanはそれがfacesリクエストを受け取った後でのみインスタンス化されますので、対話状態を保存するのに利用しようとしているJSF managed beanがすでにfacesリクエストを受け取り済みであることを確認するようにしてください。そのmanaged beanのスコープはsessionでなくてはなりません。
AJAXを使って作業する際に直面する問題の一つは、ユーザアクションに基づく複雑なHTMLフラグメントの生成です。通常これはメンテナンスコストの高いアプリケーションを生む結果になり、時には、長期にわたってアプリケーションの要求に磨きをかける際のユーザインターフェースの変更さえも非常に困難にします。HTMLはtilesの概念をもちませんが、サンプルアプリケーションではtilesに似たものを実現可能です。
<table style="height: 80%; width: 100%; padding-bottom: 100px; visibility: {0};">
<tr valign="top">
<td>
<table align="left" valign="top">
<tr>
<td class="formLabel">Product Category:</td>
<td class="formField">{1}</td>
</tr>
<tr>
<td class="formLabel"><span class="required">*</span>Description:</td>
<td class="formField"><textarea rows="2" id="desc_field" cols="80"
name="desc_field"></textarea></td>
</tr>
<table>
<tr>
<td>{dataTable}</td>
</tr>
<tr>
<td>{dataScroller}</td>
</tr>
</table>
<tr valign="bottom">
<td align="right" style="padding-right: 50px;">
<table>
<tr>
<td><input type="button" class="inputButton" onclick="saveDetails('{2}');" value="Save"/></td>
</tr>
</table>
</td>
</tr>
...
...
上記のHTMLテンプレートファイルには2種類のプレースホルダが使われています。
public static String getStringWithValues(String template, Object[] values) throws IncorrectNumberOfValues {
for(int i = 0; i < values.length; i++) {
int index = template.indexOf("{" + i + "}");
if(index == -1) {
throw new IncorrectNumberOfValues("The number of values passed is : " + values.length + "
which doens't match the number of placeholders in : " + template);
} else {
if(values[i] != null) {
template = StringUtils.replace(template, "{" + i + "}", values[i].toString());
} else {
template = StringUtils.replace(template, "{" + i + "}", "");
}
}
}
return template;
}
ここでtemplateは解析してプレースホルダをvalues配列の値で置き換える必要のあるHTMLテンプレートです。つまり、データプレースホルダの{0}はvalues配列の最初の要素で置き換えられ、{1}は二番目の要素で置き換えられます。以降も同様です。
{dataTable}と{dataScroller}はHTMLプレースホルダで、HTMLフラグメントによる置き換えを想定しています。 {dataTable}プレースホルダは、プロダクトカテゴリに含まれる各プロダクトに対応するレコードを表示するデータテーブルによって置き換えられることになっています。{dataTable}に対応するHTMLは別のHTMLテンプレート内にあります。HTMLプレースホルダを対応するHTMLで置き換えるアプローチには次の2つがあります。
実行時にはこれらのプレースホルダは対応するテンプレートで置き換えられます。このアプローチでは、propertiesファイルを読みHTMLプレースホルダを対応するHTMLファイルで置き換えるためにHTMLテンプレートを解析する小さなフレームワークが必要です。
Webアプリケーションは通常DTOを使って情報をWebレイヤからServiceレイヤに渡します。サンプルアプリケーションでは情報を JavaScriptからWebレイヤに渡すというレベルの異なったDTOを使います。たとえばプロダクト検索時、ユーザはプロダクト名・プロダクトコード・プロダクトIDという情報や検索方法(部分一致か完全一致か)を入力することができます。これらのパラメータはDTOを使ってサンプルアプリケーションのWebレイヤに渡されます。JavaScriptにおける連想配列の概念がこれらのパラメータをセットするために使われ、DWRはこれらの連想配列から対応するDTOへと変換を行います。連想配列内の名前はDTOのプロパティと一致しなくてはなりません。
以下はdwr.xmlにおけるDTOの設定方法を示しています。
<convert match="com.search.product.SearchCriteria" converter="bean"/>
このアプローチを利用するとサーバサイドのJavaコードが一段ときれいになります。なぜならAJAXリクエストに付いてくる各リクエストパラメータに対応する値を明示的に取得する必要がなくなるからです。
JSFのHTMLタグは全てrendered属性を持ち、そのHTMLウィジェットをユーザに見せるかどうかを決定するboolean型の値をとります。 HTMLテンプレートでこれと同じ機能性を実現可能です。これには、HTML内の式を解析してHTMLフラグメントを表示または隠すフレームワークの作成が求められます。
<exp:if value="someManagedBean.permissions.save">
<td><input type="button" class="inputButton" onclick="removeProduct('{0}');" value="Remove" /></td>
</exp:if>
ユーザがプロダクト詳細ページでBackボタンをクリックしたら、プロダクト検索ページが表示されなければなりません。その際、もしBackボタンのクリックがAJAXリクエストを使って処理されているとしたら、アプリケーションはfaces-config.xmlで定義されたナビゲーションルールを活用していません。ですので、プロダクト詳細ページには、別のページに遷移する際にサーバにHttpServletRequest(やAJAXリクエスト)を投げるHTMLボタンよりもJSFのHTMLコンポーネントを使うほうが得策です。
Ajax4jsfはJSFコンポーネントでAJAXを用いる方法を提供してくれますが、Portletのサポートはバージョン1.1.1で導入されました。サンプルアプリケーションの作成時点ではPortletはサポートされていませんでした。
HTMLの要素とCSSを連携させて用いてツリー構造を作成することが可能です。ですが、これらのツリー構造はイベントハンドリングモデルを全面的に欠いているため高度にインタラクティブなツリーウィジェットという要求には適しません。
RichFacesはAJAXの機能性をもつリッチなJSFコンポーネントを提供してくれます。RichFacesはAJAXの機能性を実現するのにAjax4jsfを使っています。
Ashish SarinはJavaによるWebアプリケーションの開発およびデザインに8年を超える経験をもつ。
DOJO
DOJOはJavaScriptで書かれたオープンソースのDHTMLツールキットです。更に詳しく知りたい方はhttp://www.dojotoolkit.org/aboutを参照してください。
DWR
DWR(Direct Web Reporting)はJavaアプリケーションでAjaxを簡単に扱えるようにしてくれます。更に詳しく知りたい方はhttp://getahead.org/dwr/documentationを参照してください。
RichFaces
http://labs.jboss.com/jbossrichfaces/
Ajax4jsf
http://labs.jboss.com/jbossajax4jsf/
Facelets
https://facelets.dev.java.net/
原文はこちらです:http://www.infoq.com/articles/jsf-dojo-dwr
Googleのクラウド環境をつかったGoogle App Engineによる開発するにあたり、初めての試みで苦悩する開発者達の経験をもとに、各開発フェーズにあわせて問題点やどう解決したかをご紹介します
去る1月12日、定理証明支援系ツールCoqの初心者向けチュートリアルが開催さ れた(http://kokucheese.com/event/index/23667/)。今後も2月2日 (http://kokucheese.com/event/index/23744/)、2月9日、2月16日と引き続き開 催されていく予定である。本記事では、開催の様子をレポートする。
Neal Gafter氏はOracleによるJava買収の影響に関する議論、Javaにセグメンテッドスタックやメタオブジェクトプロトコルを追加することについての主張、そしてJavaとC#との比較について話をしてくれた。
GoogleはVMをともなう新しい言語であり、JSコンパイラでもあるDartをプレビューした。 InfoQはDartのアプリの構築に貢献する文法の裏側を探った:スナップショット、Isolate、モジュール方式
本記事ではCSPベースの「マルチドメイン・モデル検査ツール」である、PAT(Process Analysis Toolkit)について紹介する。モデル検査は、形式手法(Formal Method)という方法論を基礎とする技術であり、複雑さが増大しながらも安全性を求められる、現在のソフトウェア開発の状況に対する処方箋の1つとして注目されている手法である。
前回まで、Jenkinsの幾つかの側面に注目して解説をしてきました。シリーズ最後の今回は、Jenkinsをサービスとして使う方法を紹介します。
Alloyは、MITにて開発された仕様記述言語であり、ツールによる自動解析を使い、インクリメンタルに形式仕様が書けることが特長である。筆者らはAlloy開発者による、Alloyを使った形式手法入門書を翻訳、今夏にオーム社より刊行した。本記事では、Alloyの簡単な概要と、翻訳書『抽象によるソフトウェア設計』(「Alloy本」)を紹介する。
スマートフォンを中心としたマルチデバイスにおけるタッチユーザーインターフェイスへの対応は、既に必須の項目となりつつある。本記事では、Windows デバイスにおける UX のベースとなっている「メトロ」というデザイン言語を掘り下げながら、既存環境を意識しつつもどのようにタッチユーザーインターフェイス開発に取り組んでいくべきであるかについて解説していく。
No comments
スレッド表示 返信