足し算プログラム

slim3の勉強を始めてみる。まずは足し算から。

Getting Started に沿ってプロジェクト整備。そしてページとコントローラーを作成。ここでは"/add/"と指定して生成した。

index.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"%>

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>add Index</title>
<link rel="stylesheet" type="text/css" href="/css/global.css" />
</head>
<body>
<p>Hello add Index !!!</p>
<form action="${f:url('calculate')}" method="post">
<table>
<tr>
<td></td><td><input type="text" ${f:text("arg1")} title="INPUT1" class="${f:errorClass('arg1', 'err')}"/></td>
<td>${f:h(errors.arg1)}</td>
</tr>
<tr>
<td> + </td>
<td><input type="text" ${f:text("arg2")} title="INPUT2" class="${f:errorClass('arg2', 'err')}"/></td>
<td>${f:h(errors.arg2)}</td>
</tr>
<tr>
<td> = </td>
<td>${f:h(result)}</td>
</tr>
</table>
<input type="submit" name="doCalculate" value="calculate"/>
<input type="submit" name="doReset" value="reset"/>
</form>
<a href="${f:h('/')}">Go Index.</a>
</body>
</html>


まずformのaction指定から。SeasarシリーズのWebフレームワークと違って通常のform、actionの使用方法と同じ。TeedaSAStrutsに比べてsubmitボタンとサーブレットのメソッドが紐付いていないので少々面倒に感じる。どうやらslim3は自由度を高くしようという思想みたい。

input.textタグは

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

と書くらしい。name属性は指定せずf:textを用いるようだ。

バリデーションチェックに引っかかった場合はerrors.xxxでエラーメッセージを呼び出しf:hで出力する。フムフム。

f:errorClassを指定すればエラー時、正常時のクラス名指定が出来る。なるほど。

${f:h(result)} は計算結果の出力。

input.submitは通常のタグの使用方法で良い。と言う事はname属性を指定すればリクエストパラメータでどのボタンをクリックしたのか判断出来そうだ。なのでここではnameを指定しておく。nameの規則としてdoXxxと頭にdoを付ける事にした。

jspはこんな感じ。

続いてコントローラー

IndexController.java

package examples.controller.add;

import org.slim3.controller.Controller;
import org.slim3.controller.Navigation;

public class IndexController extends Controller {

    @Override
    public Navigation run() {
        requestScope("arg1", 0);
        requestScope("arg2", 0);
        requestScope("result", 0);
        return forward("index.jsp");
    }
}

input.textと計算結果の初期値を指定する。指定方法はrequestScope()というメソッドがあるのでそれを用いてリクエストスコープにセットする。

IndexController.javaは以上。続いて計算の為のコントローラーを作成。buildタスクでコントローラーのみを作成。ここでは"/add/calculate"とした。

CalculateController.java

package examples.controller.add;

import java.util.Map;

import org.slim3.controller.Controller;
import org.slim3.controller.Navigation;
import org.slim3.util.RequestMap;

import examples.cool.validator.MyValidators;

public class CalculateController extends Controller {

    private int arg1;
    private int arg2;
    private int result;

    @Override
    public Navigation run() {
        //バリデーションチェック
        if(!validate()) return forward("index.jsp");
        //パラメーターの値をプロパティに詰め替える
        this.convertParameter();
        //押下したsubmitに対応するメソッドの実行
        if (asString("doCalculate") != null) {
            return doCalc();
        }
        if (asString("doReset") != null) {
            return redirect("./");
        }
        //doXxxにマッチしなかった場合
        return redirect("./");
    }

    protected Navigation doCalc() {
        result = arg1 + arg2;
        requestScope("result", result);
        return forward("index.jsp");
    }

    protected boolean validate() {
        MyValidators v = new MyValidators(request);
        v.add("arg1", v.required(), v.integerType(), v.greaterThanConstant());
        v.add("arg2", v.required(), v.integerType(), v.greaterThanConstant());
        return v.validate();
    }

    protected void convertParameter() {
        arg1 = asInteger("arg1");
        arg2 = asInteger("arg2");
    }

}

書こうと思えばもっと単純に書けるけど、今後それなりにボリュームのある場合を想定してコントローラー作成のパターンを考慮して書いてみた。

考えたのは2つ。

1つ目はconvertParameter()というメソッドを作りrequestに格納されているパラメーターの値をコントローラーのプロパティに詰め替える作業。詰め替え作業を一つのメソッドで頑張れば後は何かと楽に使えそう。BeanUtil.copy()で一発で済むパターンならそれを使うけど、例えば繰り返しの入力項目をListに詰め直すとかそういうのはどうしても手作業になるだろうから。でもListに詰め直し位はユーティリティ作れそうだな。後で考えてみよう。

2つ目はパラメータからdoXxxを取り出し、それがnullでなければ特定のメソッドを実行するようにした。イメージ的にはTeedaSAStrutsのsubmitボタンの使用方法みたいな感じで。

パラメータを頑張ってクラスのプロパティに詰め替える、submitボタンとメソッドを紐付ける、この二つを念頭にプログラムを書けば複雑なクラスを綺麗に書けるんじゃないかな。

MyValidatorsは独自に拡張したバリデータ。説明は省きます。ソースを見てもらえば分かると思うので。

MyValidators.java

package examples.cool.validator;

import javax.servlet.http.HttpServletRequest;

import org.slim3.controller.validator.Validators;

public class MyValidators extends Validators {

    public MyValidators(HttpServletRequest request) {
        super(request);
    }

    /**
     * Returns {@link GreaterThanConstantValidator}. The key of error message is
     * "validator.greaterThanConstant".
     * 
     * @return {@link GreaterThanConstantValidator}
     */
    public GreaterThanConstantValidator greaterThanConstant() {
        return GreaterThanConstantValidator.INSTANCE;
    }

    /**
     * Returns {@link GreaterThanConstantValidator}. 
     *
     * @param message
     *            the error message
     * 
     * @return {@link GreaterThanConstantValidator}
     */
    public GreaterThanConstantValidator greaterThanConstant(String message) {
        return new GreaterThanConstantValidator(message);
    }

}

GreaterThanConstantValidator.java

package examples.cool.validator;

import java.util.Map;

import org.slim3.controller.validator.AbstractValidator;
import org.slim3.util.ApplicationMessage;

public class GreaterThanConstantValidator extends AbstractValidator {
    /**
     * The instance.
     */
    public static GreaterThanConstantValidator INSTANCE = new GreaterThanConstantValidator();

    /**
     * Constructor.
     */
    public GreaterThanConstantValidator() {
        super();
    }

    /**
     * Constructor.
     * 
     * @param message
     *            the error message
     */
    public GreaterThanConstantValidator(String message) {
        super(message);
    }

    public String validate(Map<String, Object> parameters, String name) {
        Object value = parameters.get(name);
        if (Integer.parseInt(value.toString()) <= 0) {
            if (message != null) {
                return message;
            }
            return ApplicationMessage.get(getMessageKey(), getLabel(name));
        }
        return null;
    }

    protected String getMessageKey() {
        return "validator.greaterThanConstant";
    }

}