本文へジャンプ

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。プロフィールで選択した情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

  • 閉じる [x]

developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む


送信されたすべての情報は安全です。

  • 閉じる [x]

100 パーセント、サーブレット: ビューについて考え直す

Servlet API による設計の単純化

Jason Van Cleve (jason@vancleve.com), Web Developer, Else For If
Jason Van Cleve
Jason Van Cleve は、1990 年半ばから Web アプリケーションを開発してきました。若い頃からプログラミングを始めた彼は、教会の席でも方眼紙に 8 ビットのマシン言語ルーチンを作成して時間を過ごしていました。

概要: 動的コンテンツを持つ Web ページで開発者とUI 設計者のコンサーンを分け隔てる方法としては、JSP (JavaServer Pages) 技術が大々的に勧められていますが、残念ながら JSP は多くの設計者にとってあまりにも複雑すぎます。そのため Java 開発者が自ら JSP コードを処理するはめになりがちですが、その結果は大抵の場合、満足できないものです。そこでこの記事では、代わりとなる型破りな方法がもたらす利点を紹介します。その方法とは、単純なヘルパー・オブジェクトを使ってサーブレットにのみ基づいた Web インターフェースをビルドするというものです。

編注: この方法は、テンプレート設計者を専任で擁するチームには推奨できません。自ら HTML 出力コードを作成して管理する Java Web 開発者を対象としています。

日付:  2007年 3月 27日
レベル: 中級
アクティビティー: 1362 ビュー
お気軽にご意見・ご感想をお寄せください: 


JSP は、Web 開発者の作業と動的ページの UI を設計する開発者以外の作業とを切り離しておくために設計されました。ですが、動的コンテンツの多種多様な問題を解決するために何重にもレイヤーが追加される JSP は、多くの設計者にとってあまりにも複雑です (国際化対応を例にとると、テキストを別の場所に保管し、キーで参照する必要があります)。そのため大抵のプロジェクトでは、Java 開発者が JSP コードを使って設計者の作業を度々書き直し、そのコードにタグ・ライブラリーなどの手段を使うことで、Java コードとは切り離すようにしています。

その一方、現在正統とされている方法に反して単純なヘルパー・オブジェクトを使用すれば、通常のサーブレットをベースにした無駄のない簡潔な Web インターフェースを構築できます。この記事で推奨するのは、標準的な Java の形式で動的 Web ページのビュー出力を作成することです。この手法ならではの利点を、NCAA March Madness プールのために組み立てた採点アプリケーションを用いて説明します。

注: この記事で説明する方法は、自ら HTML 出力コードを作成して管理する Java Web 開発者を対象とした方法であり、専任のテンプレート設計者を擁するチームを対象とするものではありません。

HTML、その動的なもの

サーブレットのみを使った手法は単純さを実現するひとつの手段です。この手法に必要なのは、サーブレットの基本クラスと、サーブレットのサブクラスが出力を生成するために使うカスタムのライター・オブジェクトです。HTML のほとんどはヘルパー・オブジェクトのメソッドにカプセル化され、そのすべてを必要に応じてオーバーライドできるため、コードは簡潔になります。コードの再利用は常に望まれており、大抵の Web サイトはページ間でかなり多くの HTML を共有するため、コードを再利用できることは一番の関心事になってきます。そしてこの HTML 出力メソッドがもたらすのは、わかりやすくコンパクトなサーブレット・コードです。コードの維持費が多かれ少なかれコードの大きさに直接比例するという点から考えると、極めて維持しやすいコードと言えます。JSP インターフェースを純粋なサーブレットとして作成し直すことによって、私は 3 分の 2 までコードを縮小できました。

以下は、ユーザーのアクセス権に基づいてリンクを出力する冗長なコード構成の一例です。

  <c:if test="${user.permission[ sessionScope.ConstantMap[ EDIT_WIDGET ] ] != 0}">
  <c:url var="editUrl" value="/EditWidget.jsp"/>
  <div class="navigation"><a href="<c:out value="${editUrl}"/>">Edit
        this widget</a></div>
</c:if>

Java 構文を使うと、このコードは目に見えて簡潔になります。

if (user.getPermission(Constants.EDIT_WIDGET) != 0)
  out.printNavlinkDIV("/EditWidget.jsp", "Edit this widget");

また、ビジネス・オブジェクトを要求オブジェクトで渡す代わりに、同じ場所から取得して出力することで、どれだけのコードを節約できるかを考えてみてください。コードを減らすほどメリットは増えます。

JSP やその他のビュー技術を使用することは、Web 開発の最も信用できない側面になり得ます。JSP ページは、HTML や XML ではありません。Java コードや JSTL (JavaServer Pages Standard Tag Library) コード、あるいは式言語 (EL) でもありません。JSP ページはそのすべて、あるいはそれより多くのものを寄せ集めた複雑なものです。これは奇怪な融合であるだけでなく、抽象化層それぞれが開発への新たな障害をもたらします。JSP ページのデバックを例にとると、それはまるでダウジングをするようなものです。表面下のどこかにバグがあることはわかっていても、実際のコードと一致しない行番号を示す難解なエラー・メッセージでは、その場所はまず突き止められません。

JSP 技術では基本クラスを拡張することもできないため、コードの再利用は Bean、インクルード・ファイル、そしてカスタム・タグ・ライブラリーに委ねられることになります。しかし、タグ・ライブラリーは再利用の効果的手段と呼ぶにはあまりにも手間がかかりすぎます。「タグの設計は言語の設計」であることは言うまでもなく (「参考文献」の Noel Bergman の記事を参照)、API を変更するたびに XML ファイルを対応させるのは骨の折れる作業で、すでに何層にもなっているインターフェースの層がさらに増えるのがおちです。

HTML/XHTML 準拠

HTML を Java メソッドとしてカプセル化すると、再利用を通じて一貫性をもたせることが可能になり、タイプ・ミスを最小限に抑えることができます。これはつまり、標準に準拠した HTML や XHTML 出力を生成するという価値ある目標への早道でもあります。私は March Madness サイトを XHTML 1.0 Strict として実装しましたが、フィーチャーが完成した時点で W3C Validation Service を満足させるのに必要だった調整はわずか 5、6 箇所ほどでした。

私たちは今、新しくて素晴しい World Wide Web を目の前にしています。Ajax が Web 開発を一新するかどうかとは関係なく、Web サイトはこの先もますますインテリジェントになり続けるはずです。しかも HTML 自体は常に宣言型ですが、HTML を生成するコードが宣言型でないことは確かです。それにも関わらず、JSP 技術やその他のテンプレート・システムは、元来動的な出力を宣言型で表現しようとするため、手に負えないほど複雑になってしまうのはやむを得ないことです。これがまさに、開発者が JSP ソースにスクリプトレットを組み込まざるを得なくなっている理由です。私たちが表現しようとしているものは、フォームに相当するだけのロジックなのです。

一方、HTML を Java コードとしてカプセル化すると出力ロジックを簡潔に表現できます。if 文と for ループはよくご存知の自然な形になります。ページ要素はメソッドに分解することができるため、理解するにも維持するにも容易です (大規模な JSP ページには十分にコメントが付けられていることがめったにないため維持するのが困難で、エラーの原因になるのがおちです)。サーブレットのみを使った手法では、ページ構成ごとに新しいクラスを作成する必要がないため、コードを最大限再利用できます。


突飛な設計

March Madness

アメリカのバスケットボール・ファンが毎年 3 月に熱狂するのは、NCAA (全米大学体育協会) のトップ・プレイヤー 64 人からなるチームが一堂に会し、1 週間しのぎを削る勝者総獲得形式のトーナメントです。大勢のファンは、対戦相手が変わって優勝候補が絞り込まれていくのに合わせ、試合前の予想と試合の結果の両方を記録して楽しみます。さらに大勢のファンのお目当てとなっているのが、非公式の特設プールです。このプールの賞金は、予想が最も的中していた人が獲得します。

サーブレットのみのコンセプトを実証するために私がビルドしたのが、NCAA March Madness トーナメント・プールの採点インターフェースです (「March Madness」および「ダウンロード」を参照)。ユーザーはサインオンして、トーナメントに出場する 64 のチームのなかから優勝候補として 20 のチームを選択し、それぞれのチームに優勝の可能性に応じた点数を付けます。試合が始まると選択内容は読み取り専用になり、試合に勝ったチームがわかった時点で、管理者がその結果を入力します。するとユーザーの合計点数が、選択したチームに基づいて自動的に集計され、ランキング形式で表示されます。

このプロジェクトには約 3 週間の余暇を注ぐことになりましたが、そのほとんどは、スタイルと画像をあれこれいじるのに費やした時間です (私はグラフィック・アーティストではないからです)。1 つの HTML ファイルとその他の静的リソースとは別に、21 の Java クラスからなる UI 層があります。JavaNCSS (「参考文献」を参照) の測定では、この 21 のクラスに相当する実際の Java ステートメントは、1,334 にのぼります。

MVC からの脱却

この記事で紹介するサーブレットのみの設計は、クライアントとビジネス・ロジック間の単一のビュー・レイヤーで構成されます。モデル・ビュー・コントローラー (MVC、あるいは Model 2) はその評判ほどの解決策ではなく、さらに MVC をサポートする Web フレームワークは手に負えなくなりがちです。Spring MVC と JSF (JavaServer Faces) は冗長すぎると言われていますが、同じことが Struts にも言えます。コントロール・ロジックに従うだけでも、Struts の膨れ上がった見苦しい構成ファイルを事あるごとに参照しなければなりません。N. Alex Rupp は (「参考文献」を参照) は、MVC をアンチパターンとまで呼んでいます。MVC はいわば、Web 技術の「賢者の石を求める、常軌を逸した追求」ということです。

例えば Struts では、開発者が Action モジュールの目的に惑わされてしまったがために、ビジネス・ロジックが行き詰ることがよくあります (そうでなければ、JSP 全体で行き詰るということです)。一方、サーブレットで焦点としているのは明らかにブラウザーとのインターフェースなので、ビューとコントローラーをサーブレットとして実装すれば、ビジネス・ロジックをそれが属する場所にとどめておきやすくなります。

このプロジェクトには、私の自作の elseforif-servlet ライブラリーからいくつかのクラスを抜粋して使用しています (「参考文献」を参照)。HTML を生成しやすくするインターフェースを提供するこのライブラリーは、設計の重要な鍵です。ただし、ここでの主役はライブラリーではありません。ライブラリーはあくまでもコンセプトの裏づけとして、この記事で紹介する手法が実行可能であることを示すに過ぎません。

図 1 は部分的なクラス図で、elseforif-servlet 要素は緑色で示しています。


図 1. 部分的なクラス図
図 1. 部分的なクラス図

ツリー構造の一番上にあるのは、HTML ストリング定数を含むインターフェースで、この HTML 定数を使用する HTML ライター・オブジェクトとサーブレットの両方に便利なようになっています (後ほど実際に使用例を記載)。次にあるのは HTMLWriter と HTMLFlexiWriter で、あらゆる Web サイトに役立つ基本的な下位レベルの HTML メソッドを実装します。この 2 つの違いは、HTMLWriter は直接出力にプリントする一方、HTMLFlexiWriter は出力をストリングとして返すこともできるという点です。この例のように、ある出力メソッドの結果を他の出力メソッドのパラメーターとして渡せると便利な場合はよくあります。

out.printA(URL_ELSEFORIF, out.IMG("/img/elseforif.gif", 88, 31));

MadnessWriter クラスは、この Web サイトに必要な上位レベルの出力機能を追加します。この出力機能とは、このサイトに固有であって、サイト全体で繰り返される共通要素 (ヘッダー、フッター、メニューなど) です。この軽量で非スレッド・セーフのオブジェクトは、抽象的なサーブレット基本クラス MadnessServlet からの要求ごとに、ファクトリー・メソッドによってインスタンス化されます。

MadnessServlet 基本クラスの役割は、コア・サーブレットのコントロール・ロジックを処理し、具体的なサブクラスがそれぞれのタスクに専念できるようにすることです。この基本クラスは、いくつかの標準 HTTP ヘッダーを設定し、ページ・レベルのセキュリティー・チェックを行った後、protected メソッドdoBoth() に MadnessWriter インスタンスを渡します。

 protected void doBoth(HttpServletRequest request, HttpServletResponse response,
      HttpSession session, MadnessWriter out) throws ServletException, IOException

MadnessServlet も同じく MadnessConstants を実装し、HTMLConstants に定義された静的な値に、サブクラスが簡単にアクセスできるようにします。つまり、サーブレットは MadnessWriter オブジェクトをこれらの定数と併せて使うことで、極めて簡潔な Java のような構文を実現できるというわけです。

パラメーターと検証

大型システムに頼らなくても、パラメーターを効果的に処理することは可能です。elseforif-servlet ライブラリーには、サーブレットに直接組み込めるヘルパー・クラスが含まれていて、このクラスのパラメーターを init() メソッドに定義するには、修飾子パターンを使用します。サーブレットを簡潔にするこの方法では、init() がある種のシグニチャーの形を取るため、要求されるパラメーターが一目でわかります。カスタム Map によってカプセル化される引数と検証結果は、セッションで維持して必要に応じてサーブレット間で共有することができます。

MVC の用語で説明すると、サーブレット (この場合、UI の基本単位) はビュー層とコントロール層を構成します。これは、HTTP のようなステートレス・インターフェースでは理屈に合いますが、ビューへの要求とデータ更新要求は基本フォームが同じであり、両者をはっきりと区別する線はありません。私はモジュール性を持たせるために、フォームのページを実装するサーブレット・クラスとそのハンドラーを実装するサーブレット・クラスを別にするようにしていますが、どのように区別するにしろ、HTML 出力ロジック、サーブレット・パラメーターの処理、そしてページ・フロー・ロジックはすべて密接な兄弟関係にあります。このそれぞれを抽象化するという MVC の試みは、機能不全の大きな原因となってきました。

ビジネス層の実装は、ビュー層とはほとんど関係ないようにしなければなりません。鍵となるのは、ビジネス・インターフェースを単純なものにすることです。そうすれば、UI コードは UI の処理のみを扱うコードにしておけます (サンプル・アプリケーションのビジネス層で作成した Apache Derby への CRUD インターフェースは、あまり洗練されていません)。

アプリケーションの実行

この Web アプリケーションには必要なものがほとんど揃っていますが、webapps ディレクトリーにドロップする前に、web.xml の記述子の環境プロパティーを変更しなければならない場合があります。少なくとも、組み込み Derby インスタンスには、データ・ファイルを作成して保管する場所が必要です。デフォルト設定は UNIX パス (/var/derby/) なので、Linux を実行しているのであれば、(サーブレット・コンテナーが書き込み可能な) ディレクトリーを作成するだけで済むこともあります。サイトにログインするには、ユーザー名に admin、パスワードに password を使用してください。詳細は、ダウンロード・パッケージの README ファイルに記載されています。


フォームとそのハンドラー

ここからは、コードの詳細に入ります。ユーザーは Picks ページ (図 2 を参照) に進んで、トーナメントの最初のラウンドが始まる前にお気に入りのチームを選びます。それ以降は、自分や他の参加者が選んだチームを読み取り専用の出力として表示できます。


図 2. Picks ページ
図 2. Picks ページ

Picks サーブレットがこのページを生成するときに最初に行うのは、ビジネス層からユーザー・オブジェクト (このシステムでは Player) を取得してセキュリティー・チェックを実行することです。

PlayerManager playerMan = PlayerManager.GetInstance();
Player player = playerMan.select(session.getAttribute(P_PLAYER_ID), true);
boolean readOnly = GetCutoffDateIsPassed() && !player.getAdmin();
String playerID = request.getParameter(P_PLAYER_ID);
if (playerID != null)
  if (readOnly || player.getAdmin())
    player = playerMan.select(playerID, true);
  else
    throw new ServletException("You may not view other players' picks"
          " until the cutoff date has passed:  " + CutoffDate + ".");
 

このチェックでは、一般ユーザーがビジネス・ルールに従って選択項目 (Picks) を表示または編集していることを確認し、ページの動作方法 (つまり、readOnly) を決定するローカル変数を準備します。次に、Team オブジェクトの配列を取得します。このオブジェクトはもちろん、トーナメントの公式出場チームです。ここでメソッドを呼び出し、この配列からアルファベット順にソートしたマップを派生させます。このマップは、ドロップダウン・コントロールに必要となります。

TeamManager teamMan = TeamManager.GetInstance();
Team[] teams = teamMan.selectAll();
Map selectTeams = getDropDownMap(teams);

この時点で出力を開始します。

 out.printPreContent(null, out.SCRIPTFile("/js/picks.js"));

上記のメソッドによって出力されるページの最初の部分には、HEAD タグ一式、BODY の開始タグ、そしてページ・トップのロゴが含まれます。JavaScript ファイルへの URL は、HEAD に追加されていることに注目してください。WAR ファイルをデプロイすると、この URL は失敗するだろうと思えるかもしれません。WAR ファイルは /madness コンテキスト・プレフィックスを URL の先頭に追加するからです。実際、コンテキスト・プレフィックスは動的に MadnessWriter のコンストラクターに渡され、このコンストラクターがスラッシュで始まる URL にコンテキスト・プレフィックスを自動的に追加します。コンテキストについてさっぱりわからない人にとっては、非常に便利な機能です。

次の呼び出しで、メイン・メニューを出力します。

out.printMenu(URL_PICKS);

このように、表示されているページの URL を渡すことで、MadnessWriter インスタンスにそのページのリンクをスキップ (または無効に設定) するように指示しています。次に、グラフィックで枠を付けた TABLE 要素 (ボックスと呼びます) を開始するメソッドを呼び出します。

out.printBeginBox();

このメソッドは、ボックスに含まれる特定のコンテンツまでの複数のタグを開きます (後で、同じようなメソッド呼び出しでこれらのタグを閉じることになります。上記の printMenu() も同じメソッドを呼び出すことに注意してください)。このようなカプセル化によって、デバックの効率化することができます。一例として、私はボックス構成の中で枠を設定する一部の TD の幅が 1% になっているというバグを見つけました。これではブラウザーの幅を広げたときのピクセル数が多くなりすぎます、そこで、幅を 0% に変更しました。このように、1 箇所でサイト全体を修正することができるのです。カスタム・タグ・ライブラリーを使って修正することもできますが、これほど簡単ではありません。

以下の行は、1 つまたは 2 つの DIV 要素を出力します。最初のものは、フォームがサブミットされた後に成功したことを示すためのものです。

if ("true".equals(request.getAttribute(P_SUCCESS)))
  out.printDIV("smallHeading", "Team picks were saved successfully.");
out.printDIV("reminder", "(Reminder:  \"Pick 20\" represents the team you"
      + " think likeliest to win.  \"Pick 1\" is the least likely.)");
  

"smallHeading" 引数と "reminder" 引数は、DIV の開始タグに適用するカスケーディング・スタイル・シート (CSS) クラス名を指定しています。続いて 2 番目の引数がそれぞれ DIV の終了タグ付きで出力されています。reminder の DIV のコンテンツがもっと複雑だったとしたら、代わりに out.printBeginDIV("reminder") を呼び出して開始 DIV だけを出力することになったでしょう。この命名パターンは HTMLWriter および HTMLFlexiWriter では一貫していますが、HTMLConstants でのストリング定数は多少異なります。例えば、デフォルトで DIV の開始タグと終了タグにそれぞれ DIV とEND_DIV を使用するなどです。

reminderに続いて出力するのは、ユーザーが選択した 20 チームのそれぞれにドロップダウン・コントロールを表示するフォームです。ユーザーが選択チームを表示することしかできない場合は、チーム名のみを出力します。Java 構文では、このロジックはごく自然に表現されます。

if (!readOnly)
  out.printSELECT(P_PICK + i, selectTeams, teamID);
else
  {
  String teamName = (String)(selectTeams.get(teamID));
  out.print((teamName != null) ? teamName : "(no pick)");
  }

printSELECT() メソッドは teamID と一致するキーを持つペアを事前に選択し、マップに含まれるキーと値のペアごとにOPTION を作成します。

フォームの作成を完了した後には、ページの右側に表示されるチームのリストを出力します。チームの配列は、NCAA の地域およびランキングの順になります。各地域には小さな見出しがあり、リスト全体は 2 つの列で表示されます。これには少々の演算が必要なので、リスト 1 のように 1 つのメソッドとして分けました。


リスト 1. メソッドとしての出力の分離
                
 private void doRegionList(Team[] teams, MadnessWriter out) throws IOException
  {
  out.print(TABLE + TR);
  out.printBeginTD(null, "regionList");
  for (int i = 0; i < teams.length; i++)
    {
    if ((i & 15) == 0)
      {
      if (i == 32)
        {
        out.print(END_TD + NL);
        out.printBeginTD(null, "regionList");
        }
      out.print(NL + DIV);
      out.print(REGION_NAMES[i >> 4]);
      out.print(":" + END_DIV + OL);
      }
    out.print(NL + LI);
    out.printHTMLEscape(teams[i].getFullName());
    out.print(" (");
    out.print((teams[i].getRank() & 15) + 1);
    out.print(")");
    out.print(END_LI);
    if ((i % 16) == 15)
      out.print(END_OL);
    }
  out.print(END_TABLE_3);
  }
  

上記の END_TABLE_3 定数は、終了 TD、TR、TABLE の組み合わせを省略した表現です。いったん使い方を覚えてしまえば簡単に使えますが、この構文の簡潔さは優れた HTML 設計によるものです。つまり、この省略表現は構造のみに使用し、スタイルはできるだけスタイル・シートの中に含めるようにしてください。

それではいよいよ、ページの仕上げに入ります。

out.printEndBox();
out.printPostContent();

最初の行で以前開始したボックスを完成させ、printPostContent() でフッターを含めたページの残りを出力しています。これで、Picks フォームのページは出来上がりです。

このハンドラー・サーブレット (PicksAction) は、サブミットされた Picks ページに応答して、選択されたチーム ID を要求オブジェクトから収集します。そして収集したチーム ID を該当する Player エンティティーに対して更新されるビジネス層に渡した後、Picks フォームのページに戻ります。この時点でもセキュリティー・チェックが行われ、試合の開始後にユーザーが選択内容を更新していないことを確認します。このように、フォームとそのハンドラーはどちらも単なるサーブレットであるため、インターフェースを分けて作成しなければならない理由はまるでありません。いずれも同じビジネス・オブジェクトを使ってブラウザーのパラメーター要求に応答し、2 つがまとまって 1 つの UI コンポーネントとなります。ここに MVC フレームワークを使ったとしても、片方がもう片方と協力するのを妨げるだけです。


その他の観点

Web フレームワークは全体像を複雑にしてしまうという傾向がありますが、細々とした問題を解決することは確かです。一方サーブレットをベースにした設計では、特定のソリューションに縛られることなく、場合に応じて問題に対処できるという柔軟性があります。

セキュリティー

エンタープライズ・アプリケーションではほとんどの場合、ページ・レベルのセキュリティーは XML 記述子内で宣言されて処理されます。ですが私の経験から言うと、それでもまだ、ページ内での特定の動作を制御する動的なコード・レベルのインターフェースが必要になるのが一般的です。例えば Picks サーブレットには日によって変動するロジックがあります。これに対処するには Servlet API の組み込みセキュリティー・メソッド (要求オブジェクトでの isUserInRole() など) を使うか、あるいは個別のインターフェースとして作成するという方法が考えられます。いずれの方法にしても、Servlet API にコーディングすることで簡単に対処できます。

HotSwap と開発サイクル

一般的なクラスを変更して再コンパイルする作業は、実行中のアプリケーションに自動的にコンパイルされる JSP の変更よりも厄介になる場合があります。この問題を解決するのが、Java HotSwap デバッグ・インターフェース (Java 1.4 で導入) です。HotSwap では、Web アプリケーションを再起動したり、セッション状態を失ったりすることなく、変更したクラスをすぐにリロードできます。優れた IDE であれば、問題解決はさらに楽になります。私が選ぶサーブレット・コンテナー、Caucho Resin 3.0 (「参考文献」を参照) では、WEB-INF/classes 内の変更を自動的に検出し、即座にホット・スワップまでしてくれます。

国際化対応

多くのフレームワークは国際化に対応するテキスト値をプロパティー・ファイルとして提供しますが、HTML 出力ライター・プログラムを使えば最小限のコーディングで同じ結果を得られます。例えば printText() メソッドを追加すると、キーを引数に取り、変換済みのテキスト値を出力できます (text() は、テキスト値を返すだけです)。

このプログラムを使ったサーブレット出力コードは、簡潔さを損なうことなく、しかも JSP のコードと同様に有効に機能します。HTML 出力ライター・プログラムでは、欠けている変換値の処理方法として例外をスローするか、あるいは別の言語にデフォルト設定するかを制御することもできます。

賢いスキン

March Madness 設計では極めて見事に実現されていることがあります。ホーム・ページに進んでログインするとウェルカム・メッセージが表示されるので、ここで「Welcome」に続くコンマをクリックしてみてください。するとルック・アンド・フィールが変わることに気付くはずです。この代替スキンは、単なる追加の CSS ファイルではありません。MadnessWriter を拡張して、代替スキンが選択されるとサーブレット基本クラスがそのサブクラスをインスタンス化し、protected サービス・メソッドに渡すようにしてあるのです。その結果、MadnessWriter サブクラスはデフォルトのスタイル・シートをオーバーライドできるだけでなく、構造化 HTML 出力コードもオーバーライドできるようになっています (別のロゴと一層複雑な枠で囲んだボックスを表示するなど)。サーブレット自体に特殊なコードを含める必要は一切ありません。

インデントについての一言

この手法で生成される HTML 出力には、インデントや読みやすいフォーマットが欠けているのではないかと心配する方がいるかもしれません (ただし、HTML とスクリプトレットを混在させてコードをテンプレート化する際によく持ち上がるインデントの悪夢も同じく悲惨です。スクリプトレットがないとしても、カット・アンド・ペース操作を続けるうちに、コードがおかしくなってしまって、標準的でないコードになったりすることがあります)。

March Madness サイトで生成される HTML を読みやすくするために使った方法は、ただ単に、いくつかの改行文字を出力に散りばめることでした。HTML を整然とフォーマット設定することは、この手法では重要でありません。なぜなら大抵のレイアウト・バグは、生成された HTML ソースを検討しなくても、Java コードを調べれば簡単に見つけられるからです。要素と構造を分けてメソッドにすると、コードを簡潔かつ管理しやすくする上で非常に効果があります。


まとめ

この記事をきっかけに、フレームワークだけにこだわらず、Java Servlet API に直接ビルドされた Web インターフェースに発想を展開しください。Java Web 開発者に用意されたフレームワークやテンプレート・システムは圧倒的な数にのぼるため、Web 開発には必要不可欠であるかのように思えますが、そのほとんどは驚くほど複雑で使いにくいものです。特定のタイプの Web アプリケーションにはどのフレームワークが最適化を考える一方で、拡張やカプセル化などの組み込み言語機能で何が実現可能かも検討してみてください。Bruce Tate が言うように (「参考文献」を参照)、「問題を解決するには、力ずくで取り組むよりも、単純で巧妙な方法のほうが効果的です」。

強力な Web フレームワークにはそれなりの役割があり、HTML を作成して管理できる HTML 設計者がプロジェクトに任命されていれば、JSP/テンプレートは大いに役立ちます。ですが一部のプロジェクトには、サーブレットのみによる単純さのほうが有効に働き、この手法ならではの利点がもたらされることがあります。制御能力と柔軟性を提供するサーブレットのみによる手法では、動的コンテンツをすべて要求オブジェクトに詰め込む必要はありません。純粋なサーブレットでは、複雑な策略を練らなくても単体テストを実行できます。しかも HTML 出力の再利用は、メソッドの追加やオーバーライドと同じぐらい簡単にすることができます。

この手法をぜひ試してみてください。その成果に驚かされるはずです。



ダウンロード

内容ファイル名サイズダウンロード形式
March Madness Web application and sourcesj-pureserv.zip3534KBHTTP

ダウンロード形式について


参考文献

学ぶために

  • What Web Application framework should you use?」(Timothy O'Brien 著、O'Reilly Network、2006年6月): Web フレームワークの状況に関するブログです。

  • Demonstrating Spring's Finesse」(Bruce Tate 著、ONJava.com): 同著者の本「Better, Faster, Lighter Java」から抜粋したこの章では、Struts の代わりとして人気の高い Spring について説明しています。

  • JSP technology -- friend or foe?」(Brett McLaughlin 著、developerWorks、2005年2月): この記事では、JSP を別の観点から説明しています。

  • JavaServer Pages: A Developer's Perspective」(Scott McPherson 著、Sun Developer Network、2000年4月): JSP を「インサイド・アウト」のサーブレットと呼んでいるこの記事では、開発者からの「協力なし」で設計者が JSP を作成できることに半信半疑であると主張しています。

  • EJB-Based Services」(Patrick Sean Neville 著、Java Developer's Journal、2004年): この記事では、Java Web アプリケーションでのパターンとアンチパターンについて説明しています。

  • Beyond MVC: A New Look at the Servlet Infrastructure」(N. Alex Rupp 著、java.net、2003年12月): Web 技術における MVC の誤信を説明しています。

  • Understanding JavaServer Pages Model 2 architecture」(Govind Seshadri 著、JavaWorld.com、1999年12月): サーブレットおよび MVC の見通しを紹介しています。

  • The Problems with JSP」(Jason Hunter 著、Servlets.com、2000年1月): JSP とその他のテンプレート・システムでの問題をいくつか取り上げています。

  • Else For If: 著者の Web サイトに、サーブレット・ベースの手法に関する追加説明が記載されています。

製品や技術を入手するために

  • Java Servlet technology: サーブレット技術の詳細を学んでください。

  • Spring Framework: Spring は Struts の代わりによく使用されています。

  • Caucho Resin: Resin は Apache Tomcat に対抗する高速かつ機能が豊富なオープン・ソースのサーブレット・エンジンです。

  • elseforif-servlet ライブラリー: elseforif-servlet ライブラリーをダウンロードしてください。著者は、ライブラリーの改善提案を歓迎しています。

  • JavaNCSS: Java 言語の 2 つの標準ソース・コード・メトリックを測定する単純なコマンド行ユーティリティーです。

著者について

Jason Van Cleve

Jason Van Cleve は、1990 年半ばから Web アプリケーションを開発してきました。若い頃からプログラミングを始めた彼は、教会の席でも方眼紙に 8 ビットのマシン言語ルーチンを作成して時間を過ごしていました。

不正使用の報告のヘルプ

不正使用の報告

ありがとうございます。 このエントリーは、モデレーターの注目フラグが設定されました。


不正使用の報告のヘルプ

不正使用の報告

不正使用の報告の送信に失敗しました。


developerWorks: サイン・イン


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 利用条件

 


お客様が developerWorks に初めてサインインすると、プロフィールが作成されます。 プロフィールで選択した情報は公開されますが、いつでもその情報を編集できます。 お客様の姓名(非表示設定にしていない限り)とディスプレイ・ネームは、投稿するコンテンツと一緒に表示されます。

表示名をお選びください

developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

(半角英数字で3文字以上31文字以下にする必要があります)


「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 利用条件

 


この記事を評価する

コメント

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Java technology, Web development
ArticleID=250163
ArticleTitle=100 パーセント、サーブレット: ビューについて考え直す
publish-date=03272007