The HIRO Says

If you smell what The HIRO is cooking!!!

Java Cloud Meeting Tokyoに参加してきました

懇親会とかからただいま帰宅。(22:00)
ひとまず内容を簡単にまとめ。

Seasar3

Spring の wrapper として、HOT reloading などの機能を追加する形で。
VMforce や Google App Engine for Business での Spring 公式サポートの現状を踏まえ、かつ Seasar2 との互換性を持ったプロダクトだと良いよね、という感じですかね。

slim3

次の2つのお話くらいでしたかね。
(1)複数の Entity Group を一度に更新できる”Global Transaction”が素敵です。
(2)spin up を考慮すれば、必ずしも Python > Java とは言い切れない。
   slim3 なら、1.3 〜 3.9 秒で spin up しますよ。


眠いのでポイント外しているかもしれませんが、ひとまず今日はここまで。

SDKなどの更新手順

GAE の SDK などが古くなってきたので、新しいものに更新することにしました。
今回は、これらを Eclipse で更新する手順についてまとめます。



(1)Eclipse のメニューから、「Help」→「Software Updates」を
   選択します。


(2)「Available Software」タブを選択し、以下のものを選択し、
   「Install」を押下します。

  1. Google Plugin for Eclipse 3.4
  2. Google App Engine Java SDK
  3. Google Web Toolkit SDK (オプション)


(3)選択したものが表示されていることを確認し、
   「Next >」を押下します。


(4)「I accept the terms of the license agreements」を選択し、
   「Finish」を押下します。
   選択したもののインストールが開始されます。
   ※インストールは、環境にもよりますが、
    10分以上は見込んで下さい。


(5)Eclipse の再起動を求められますので、再起動して終了です。


あとがき

後日、slim3バージョンアップ手順を調べてみます。

【slim3】JSPでModelを表示できない?

BigTable から取得した Model の List を HttpServletRequest へセットし、JSP で表示しようとしているのですが、うまく表示することができません。。。

1.forEachで型認識がうまくいかない

JSTL の c:forEach を使うと、Model が String 型と認識されてしまいます。
例えば、


と定義した場合、

  1. ${model}・${f:h(model)} はOK
  2. ${model.xxx}・${f:h(model.xxx)} はNG

となります。
エラーメッセージは以下の通り。
javax.servlet.jsp.el.ELException: オペレータ "." を利用しましたが、クラス "java.lang.String" のオブジェクトにおいて "xxx" に対応する値が見つかりません
※getter/setter はきちんと用意しています。
同じことを SAStruts でやったときは、全く問題が発生しませんでした。
GAE のランタイム(もしくはプラグイン)の問題ですかね?

2.castに失敗する

EL ではなくスクリプトレットを使用し、List から Model を取得しようとすると、正しいはずのクラス名を指定しても ClassCastException になります。
エラーメッセージは以下のような感じです。
java.lang.ClassCastException: xxx.yyy.model.order.Order cannot be cast to xxx.yyy.model.order.Order
JSP では、Model を直接参照しない方がよいのですかね?


※GAE プラグインslim3 ともに、2009/11/15 頃に取得したバージョンです。
 ひょっとしてバージョンが古い?

日本語の文字コードはUTF-8で!

slim3 でアプリを開発する際、JavaEE アプリのいつもの癖で、
JSP文字コード指定を”Shift_JIS”にしていたところ、
入力値の表示で文字化けが発生。


さらに調べてみると、文字コードUTF-8 でないと、
アプリを Google にアップロードする際にエラーとなる模様。


ひがさんのブログなどを確認させていただいたところ、
JSP文字コードUTF-8 とした方がよいみたいですね。

【slim3】JSPの開発手順および注意事項

前回まで、slim3 の画面遷移制御用コンポーネントである
”Controller”について触れてきました。
今回は、この Controller から呼び出される/を呼び出す JSP の開発手順、およびその際の注意事項について見ていきます。


1.あらかじめ用意されているもの

slim3-blank をインストールする際、あらかじめ以下のものが用意されています。

  1. ベースとなる JSP 部品
  2. CSS

これらをベースに、自動生成した JSP(後述)に修正を加えていく形で、JSP を開発していくことになります。


なお、slim3-blank のインストール時に用意されているファイルについては、slim3のプロジェクト構成をご覧下さい。

(1)ベースとなるJSP部品

war ディレクトリ下に、以下の4つの JSP 部品が用意されています。

JSP ファイル名 内容
header.jsp ヘッダー:タイトルの表示機能を持つ。
menu.jsp メニュー用のリンクを持つ。
footer.jsp フッター。
index.jsp アプリの初期表示画面。上記3ファイルを include している。

header・menu・footer を使用する JSP を構築したい場合は、index.jsp をベースとすれば良いでしょう。
また、これらの JSP文字コードは、いずれも UTF-8 となっています。

(2)CSS

war/css ディレクトリ下に、global.css が用意されています。
上述の index.jsp、および自動生成した JSP(後述)は、同ファイルを参照するようになっています。
そのため、画面のデザインを定義・変更したい場合は、通常このファイルを編集していくことになります。
(ちなみに、独自の CSS ファイルを定義・使用しても問題ありません。)


2.自動生成されるJSPの概要

個別の JSP は、gen-controller で、Controller クラスらと一緒に Ant で自動生成されます。
自動生成される JSP のポイントは、以下の5点です。

  1. 文字コードUTF-8
  2. war/css/global.css を参照するようになっている。
  3. header・menu・footer は include していない。
  4. JSTL の各タグを使用できる。
  5. slim3 専用の function タグを使用できる。
    • 機能は、SAStruts のものとほぼ同等。


なお、gen-controller での JSP ファイル名およびパスのルールについては、こちらの「2.URL の指定方法」をご覧下さい。

【参考】自動生成されたJSP

★で注記した箇所以外は、全て同じ内容で生成されます。

<%@page pageEncoding="UTF-8" isELIgnored="false"%>
<%@taglib prefix="c"
    uri="http://java.sun.com/jsp/jstl/core"%>
<%@taglib prefix="fmt"
    uri="http://java.sun.com/jsp/jstl/fmt"%>
<%@taglib prefix="fn"
    uri="http://java.sun.com/jsp/jstl/functions"%>
<%@taglib prefix="f"
    uri="http://www.slim3.org/functions"%>




order Order←★URL をタイトルにしている



Hello order Order !!!

↑★Hello [URL で指定したもの]になっている

3.JSPからControllerを呼び出す手順

(1)リンクで呼び出す場合

<a> タグで、gen-controller で指定したものと同じパスを指定すればOKです。

    <a href="${f:h('/order/')}">注文</a>

ちなみに f:h() は、引数の文字列を HTML エスケープ(サニタイズ)してくれます。
クロスサイトスクリプティングを防止してくれる、という意味です。

(2)formで呼び出す場合

<form> タグで、action 属性に gen-controller で指定したものと同じパスを指定すればOKです。


(a)入力値の設定方法
(例)テキストフィールドの場合。

    <input type="text" ${f:text("name")} />

f:text() で、テキストフィールドと HttpServletRequest 設定時のパラメータ名とを関連付けます。


(b)ボタンの設定方法

    <input type="submit" value="登録"
        name="registerButton" />

複数のボタンを用意し、どれが押下されたかを判定したい場合は、ボタンに name を設定し、Controller 内で HttpServletRequest#getAttribute() を呼び出し、その name の値が取得できるか否かで判定できます。


4.注意事項

JSP の開発・使用に際しては、以下の点に注意が必要です。

(1)JSPのURL直接指定は不可

ブラウザで、JSP の URL を直接指定することはできません。
HTTP ERROR:403(アクセス禁止)になります。

  1. メッセージから推察するに、slim3 というよりは GAE/J の仕様のようです。
  2. ちなみに HTML は、WEB-INF 下でなければ直接参照が可能です。


よく JavaEE での開発では、直接参照されるのを避けるために、JSP を WEB-INF 下に配置したりします。
ですが、GAE/J・slim3 ではそもそも JSP を直接参照できないので、特に WEB-INF 下に配置・移動する必要性はないようです。

(2)web.xmljsp-configを使用できない

taglib や JavaScript の参照など、JSP 共通の定義を行えるようにするため、JavaEE では web.xmljsp-config を設定できるようになっています。
ですが GAE/J では jsp-config の設定が認識されません。
ですので、taglib や JavaScript の参照などは、JSP に1つ1つ設定していく必要があります。


詳しくは、以下のリンクをご覧下さい。

  1. jsp-configは使えない?
  2. http://groups.google.co.jp/group/google-appengine-java/browse_thread/thread/2bfc201cd7ad8b48?pli=1

5.JSPのhot deploy

slim3 では、JSP も hot deploy してくれます。
(1)Eclipse の同期化をさせなくても、Windowsexplorer レベルでの変更を反映してくれます。
(2)JSP文字コードの変更も反映してくれます。


ちなみに、web.xml などの Servlet の起点となるものについては、hot deploy できない模様です。


6.【参考】JSPの実装例

header・menu・footer を使用する JSP の実装例を、以下に示します。

<%@ page pageEncoding="UTF-8" isELIgnored="false" %>
<%@ taglib uri="http://www.slim3.org/functions" prefix="f" %>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>注文登録</title>
    <link rel="stylesheet" type="text/css" href="/css/global.css" />
</head>
<body>
    <%-- ◆header --%>
    <jsp:include page="header.jsp">
        <jsp:param name="title" value="注文登録" />
    </jsp:include>
    <%-- ◆menu --%>
    <jsp:include page="menu.jsp" />
    <%-- ◆メインのコンテンツ --%>
    <div id="body">
        <form action="/order/orderConfirm" method="POST">
            <table border="1" align="center">
                <tr>
                    <th>商品名</th>
                    <td><input type="text" ${f:text("name")} /></td>
                </tr>
                <tr>
                    <th>数量</th>
                    <td><input type="text" ${f:text("amount")} /></td>
                </tr>
            </table>
            <br />
            <table border="0" align="center">
                <tr>
                    <td><input type="submit" value="登録"   /></td>
                    <td>   </td>
                    <td><input type="reset"  value="クリア" /></td>
                </tr>
            </table>
        </form>
    </div>
    <%-- ◆footer --%>
    <jsp:include page="footer.jsp" />
</body>
</html>

【slim3】画面遷移のための”Controller”−(3)UTの実施方法

前回slim3 の画面遷移制御用コンポーネントである
”Controller”の実装方法をまとめました。
今回は、この Controller の UT の実施方法を見てみます。


1.テストクラスの概要

Controller のテストクラスは、Controller クラスと一緒に Ant で自動生成されます。
テストクラスのポイントは、以下の2点です。
(1)org.slim3.tester.ControllerTestCase を継承すること。
(2)jUnit 4.x 系でテストを実装すること。
   詳細は、jUnit4.x系の使い方をご覧下さい。


2.テストケースの基本的な実装手順

Controller のテストケース(テストメソッド)の基本的な実装手順は、以下の通りです。

(1)ControllerTester#start() で、Controller のパスを指定する。

ControllerTestCase には、ControllerTester 型のインスタンス変数 tester があります。
(テスト用のヘルパークラスと思えば良いでしょう。)
これの start() メソッドを呼び出し、テスト対象の Controller の run() メソッドを呼び出します。
ちなみに、start() メソッドの引数として指定するパスは、gen-controller で指定したパスと同じです。

(2)ControllerTester#getController() で、テスト対象の Controller のインスタンスを取得する。

ここで取得したテスト対象の Controller のインスタンスに対して、以下で assert を行っていきます。

(3)Null ではないことを検証する。

assertThat(is(notNullValue())) で、テスト対象の Controller のインスタンスが null ではないことを検証します。
※assertNull() と同義です。

(4)forward か redirect かを検証する。

ControllerTester#isRedirect() で、テスト対象の Controller の戻り値が forward(メソッドのもの)か redirect(メソッドのもの)かを検証します。

(5)遷移先のパスを検証する。

ControllerTester#getDestinationPath() で、遷移先のパスを検証します。


ちなみに上記は、Ant で自動的に生成されます


3.パラメータの設定方法

検証のためにあらかじめ HttpServletRequest などにパラメータを設定しておきたい場合は、org.slim3.util.RequestLocator クラスなどを、ControllerTester#start() を実行する前に呼び出しておきます。

    HttpServletRequest request = RequestLocator.get();
    request.setAttribute("name", "iPhone");
    this.tester.start("/order/order");


RequestLocator などの使い方については、前回の「3.HttpServletRequest等へのアクセス方法」をご覧下さい。


4.validationの検証方法

想定通りに validation が機能したかを検証するためには、org.slim3.controller.validator.Errors クラスを利用します。
検証手順は、以下の通りです。

(1)リクエストから、Errors のインスタンスを取得する。

Errors のインスタンスは、リクエストにキー "errors" で登録されています。
※同インスタンスは、validation の有無および結果に関係なく登録されています。

    Errors errors =
            (Errors) request.getAttribute("errors");
(2)Errors#size() で、validation エラーの有無を検証する。

validation エラーがない場合は、size() がゼロになります。

(3)Errors#get() で、エラーメッセージを検証する。

Errors の get() メソッドにテスト対象のフィールド名を指定すると、validation エラーがあった場合に、該当するメッセージを取得・検証できます。

    assertEquals(
            "nameは必須です。",
            errors.get("name"));


ちなみに上記は、ControllerTester#start() を実行した後に呼び出せます


5.テスト用のテーブルデータの設定方法

検証のためにあらかじめテーブル(BigTable)にデータを設定しておきたい場合は、該当する Service ないし Datastore でのデータ格納操作を、ControllerTester#start() を実行する前に呼び出しておきます。


ControllerTestCase は、Service のテストケースと同様、テスト終了後に処理をロールバックしてくれます
そのため、単体テストの連続実行が可能となります。


6.【参考】テストの実装例

package xxx.yyy.controller.order;

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import javax.servlet.http.HttpServletRequest;

import org.junit.Test;
import org.slim3.controller.validator.Errors;
import org.slim3.tester.ControllerTestCase;
import org.slim3.util.RequestLocator;

public class OrderControllerTest
        extends ControllerTestCase {

    // ◆例:name が null で validation エラーになるケース
    @Test
    public void run_NameIsEmptyError() throws Exception {

        // ◆準備:リクエストの設定
        HttpServletRequest request = RequestLocator.get();
        request.setAttribute(
                "name",
                "");

        // ◆run() メソッドの呼び出し
        this.tester.start("/order/order");
        OrderController controller =
                this.tester.getController();

        // ◆Ant で自動生成される検証ロジック
        assertThat(
                controller,
                is(notNullValue()));
        assertThat(
                this.tester.isRedirect(),
                is(false));
        assertThat(
                this.tester.getDestinationPath(),
                is("/WEB-INF/view/order/order.jsp"));

        // ◆validation エラーの検証
        Errors errors =
                (Errors) request.getAttribute("errors");
        assertEquals(
                1,
                errors.size());
        assertEquals(
                "nameは必須です。",
                errors.get("name"));
    }
}

次の予定

次は、JSP の作成方法・ルールについて見ていこうと思います。