T2でSlim3のバリデーションを使いたい

現在作っているサイトはWeb系の部分はt2、永続化周りはslim3-datastoreを使っています。
ただt2にはValidationはないので(t2にはなくていいと思います その薄さがとても素敵なので。)どうしようかなと思っていたのですが、
あるじゃないですかSlim3に!validatorsが!

ってことでslim3のValidatorsをT2で使えないかやってみました。

とりあえずなんも考えないでつかってみた。。。

とりあえず何も考えないで、使ってみました。

	/**
	 * イメージデータを保存します。
	 * @param context context
	 * @param request request
	 * @param file アップロードファイル
	 * @param title タイトル
	 * @param updaterComment 投稿者コメント
	 * @return 成功時完了画面
	 */
	@POST
	@ActionParam("store")
	public Navigation store( WebContext context
							,HttpServletRequest request
							,UploadFile file
							,@RequestParam("title") String title
							,@RequestParam("updaterComment") String updaterComment
							){
		//ここからValidation
		Validators v = new Validators(request);
		v.add(ImageMeta.get().title, v.required());
		v.add("img", v.byteType());
		v.add(ImageMeta.get().updaterComment , v.maxlength(500));
		if(v.validate() == false){
			//v.validate()がfalseなら終了
			return index(context);
		}

		if(file.get().length > StkConst.MAX_IMAGE_BYTES){
			return index(context);
		}

		if(!ArrayUtil.contains(StkConst.getSupportedExtensions() , FilenameUtils.getExtension(file.getName()).toLowerCase())){
			return index(context);
		}

		Image image = new Image();
		GlobalTransaction tx =Datastore.beginGlobalTransaction();
		try{
			image.setKey(Datastore.allocateId(Image.class));
			image.getUpdaterRef().setModel((Member)context.getSession().getAttribute(StkConst.SESSION_KEY_MEMBER));
			image.setTitle(title);
			image.setUpdaterComment(updaterComment);
			image.setCreateDate(new Date());
			image.setFileName(file.getName());
			List<ImgUnit> imgUnits = image.setImage(file.get());
			tx.put(imgUnits);
			tx.put(image);
			tx.commit();
		}catch (Exception e){
			tx.rollback();
			v.getErrors().put("put", e.getMessage());
		}
		return Redirect.to("/top");
	}

で実行すると、下記のようなException

Caused by: java.lang.NullPointerException
at org.slim3.jsp.Functions.errors(Functions.java:403)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.commons.el.FunctionInvocation.evaluate(FunctionInvocation.java:172)
at org.apache.commons.el.ExpressionEvaluatorImpl.evaluate(ExpressionEvaluatorImpl.java:263)
at org.apache.commons.el.ExpressionEvaluatorImpl.evaluate(ExpressionEvaluatorImpl.java:190)
at org.apache.jasper.runtime.PageContextImpl$13.run(PageContextImpl.java:884)
at java.security.AccessController.doPrivileged(Native Method)
at org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate(PageContextImpl.java:880)
at org.apache.jsp.upload.index_jsp._jspx_meth_c_forEach_0(index_jsp.java:153)
at org.apache.jsp.upload.index_jsp._jspService(index_jsp.java:86)
... 66 more

なんか必要らしい。。。
で該当の場所をみると

@slim3 Functions

    /**
     * Returns errors iterator.
     * 
     * @return errors iterator
     */
    @SuppressWarnings("unchecked")
    public static Iterator<String> errors() {
        HttpServletRequest request = RequestLocator.get();←ここでNullが帰る。
        Map<String, String> errors = ←ここでNullPointer
            (Map<String, String>) request  
                .getAttribute(ControllerConstants.ERRORS_KEY);
        if (errors != null) {
            return errors.values().iterator();
        }
        return null;
    }

こんな感じらしく、RequestLocatorにrequestが入っていないといけないらしい。。。
あと雰囲気的にRequestのなかにControllerConstrants.ERRORS_KEYでなんかはいってるらしい。

でいろいろ見てってPluginを作ってみた。

でいろいろ見てってPluginを作ってみた。
こんな感じです。

package com.google.code.stk.t2gae.commons.plugin;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slim3.controller.ControllerConstants;
import org.slim3.controller.validator.Errors;
import org.slim3.util.ApplicationMessage;
import org.slim3.util.RequestLocator;
import org.slim3.util.ResponseLocator;
import org.t2framework.t2.plugin.AbstractPlugin;

/**
 * slim3の各種機能を利用するためのPlugin
 * @author keisuke.oohashi
 *
 */
public class Slim3Plugin extends AbstractPlugin {

	//TODO 2010/03/21 16:37:53 keisuke.oohashi:ThreadSafeじゃなさそう。。。
	/** previousRequest */
	private HttpServletRequest previousRequest;

	/** previousResponse */
	private HttpServletResponse previousResponse;

	/**
	 * slim3のコントローラクラスの代わりにRequest、Response、Errorsの設定を行います。
	 *
	 * @param request Request
	 * @param response Response
	 *
	 * @see org.slim3.controller.FrontController
	 * @see org.t2framework.t2.plugin.AbstractPlugin#beginRequestProcessing(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
	 *
	 */
	@Override
	public void beginRequestProcessing(HttpServletRequest request, HttpServletResponse response) {

		previousRequest = RequestLocator.get();

		RequestLocator.set(request);

		previousResponse = ResponseLocator.get();

		ResponseLocator.set(response);

		ApplicationMessage.setBundle(ControllerConstants.DEFAULT_LOCALIZATION_CONTEXT, request.getLocale());

		Errors errors = (Errors) request.getAttribute(ControllerConstants.ERRORS_KEY);

		if (errors == null) {

			errors = new Errors();

			request.setAttribute(ControllerConstants.ERRORS_KEY, errors);
		}

		super.beginRequestProcessing(request, response);
	}

	/**
	 * slim3のコントローラクラスの代わりにRequest、Response、Errorsのクリアを行います。
	 *
	 * @param request Request
	 * @param response Response
	 *
	 * @see org.slim3.controller.FrontController
	 * @see org.t2framework.t2.plugin.AbstractPlugin#endRequestProcessing(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
	 */
	@Override
	public void endRequestProcessing(HttpServletRequest request, HttpServletResponse response) {

		ApplicationMessage.clearBundle();

		ResponseLocator.set(previousResponse);


		RequestLocator.set(previousRequest);

		super.endRequestProcessing(request, response);
	}
}

こんな感じで作ってみたのですが、TODOにも書いてる通り、ThreadSafeじゃなさそう。。。
PluginクラスはGuiceのRequestScopedで保存できないっぽいのでとりあえずSINGLETONにしてます。
かなり怪しい。
であとはこのクラスをGuiceモジュールで登録しておきます。

		bind(Plugin.class).annotatedWith(Names.named("slim3Plugin")).to(Slim3Plugin.class).in(Scopes.SINGLETON);

これで最初のメソッドを呼べばValidatorが実施されます。

ちなみにソースはこのへんです。
http://code.google.com/p/project-stk/source/browse/trunk/stk-t2-gae-commons/src/main/java/com/google/code/stk/t2gae/commons/plugin/Slim3Plugin.java

補足

今回のはSlim3のFilterクラス(FrontController)を入れればとくに設定する必要はありません。
ただ私的に、Web層のフレームワークが混在する状況はあまりよくないのかなと思って作りました。
T2の良さはその薄さにあると思っています。
薄いからこそ、機能拡張性が高く、他のフレームワークを邪魔しません。むしろ引き立てます。
T2使って仕事が出来たらすてきだなぁ〜