プレゼンテーション層にGroovy

こんなことしてみました。

  • StrutsでいうところのAction(LookupDispatchAction)部分をGroovyで作成。
  • 上記のGroovyでつくったClassにService層のクラスをdiconのouter&autoBindingでセッター・インジェクト。
    • Groovyなのでsetterメソッドを省略できるので楽チン、見やすい。ただautoBindig使用なのでpropertyの型は必ずインタフェースで。
    • componentをclass指定せずnameで管理すればinstanceのタイプをouterにしたものをひとつつくるだけ。
  • 特定の変数をgroovy.lang.Bindingクラスにくっつけて宣言せず参照できるようにする。requestやsessionに加えてmessages(複数メッセージを格納するクラス)などをGroovyServletの継承クラスで指定。diconでinstance="prototype"としてService層と一緒にinjectしてもいいかも。
  • GroovyServletで扱われる場合、Groovyスクリプトはgroovy.lang.Scriptクラスとして扱われrunメソッドが呼びだされる。これを親クラスでインターセプトし、diconをつかって依存性を注入してパラメターで指定されたメソッドを呼び出すようにする。Groovyスクリプトの戻り値でpathをもってきてTemplateServletの継承クラスにDispatchされるようにする。


app.dicon:"operation"というnameでinstance属性がouterのものを用意しておく


    
    
    
    

authentication.dicon:永続化層とサービス層のコンポーネント。SimpleAuthenticationはインタフェースの実装。


    
    
        traceInterceptor
    

Operation.java:groovyスクリプトの親になる

package taedium.presentation;
//import宣言は省略

public abstract class Operation extends Script {
    
    protected final Log logger = LogFactory.getLog(this.getClass());
    
    protected Log getLogger() {
        return logger;
    }
    
    /**
     * @see groovy.lang.Script#run()
     */
    public Object run() {
        // "operation"を使って依存性注入
        S2Container container = SingletonS2ContainerFactory.getContainer();
        container.injectDependency(this, "operation");
        
        // もともとrequestパラメータだがBindingオブジェクトにも格納されている
        // 親クラスのメソッドで取得
        String method = (String)getProperty("method");
        
        // パラメータ指定された任意のメソッドを呼び出す。
        String viewname = (String)invokeMethod(method, null);
        
        dispatch(viewname != null? viewname : "error");
        return null;
    }
    
    
    private void dispatch(String viewname) {        
        ResourceBundle bundle = ResourceBundle.getBundle("view");
        String path = bundle.getString(viewname);
        
        HttpServletRequest request = (HttpServletRequest)getProperty("request");
        HttpServletResponse response = (HttpServletResponse)getProperty("response");
        try {
            request.getRequestDispatcher(path).forward(request, response);
        } catch(Exception e) {
            throw new RuntimeException(e);
        }    
    }
}

login.groovyStrutsのAction部分に相当するgroovyスクリプト。Operationクラスを継承する。

package taedium.presentation;
import taedium.service.*

class Login extends Operation {
    
    // diconによって注入される。型はインタフェース
    AuthenticationService auth  
    
    
    // login()はOperation#run()から呼び出される。
    // name, password, session, messagesはBindingされている。
    login() {
        
        success = auth.login(name, password)
        
        if (success) {
            user = auth.getLoginUser(name, password)
            session.setAttribute("user", user);    
            return "menu"
        } else {
            messages.add("名前またはパスワードが不正です") 
            return "index"
        }
    }
}

ServletとgroovyのHTMLをつくるtemplateは省略…


感想などをおもいつくままに

  • groovyスクリプトにpackage宣言すればそのpackageのJavaクラスはimportする必要なくなる。そうなのかー。
  • outerやautoBindingをはじめて知ったときはどういうときに使えばいいのかわからなかったけどわかってきたかな。
  • setterを書かずに依存注入できてしまうのがすっきりしてうれしい。
  • sciptが継承をしないといけないのはかっこ悪いかな?
  • groovy.text.TemplateEngineの可能性を探ってみたい。
  • groovyがgroovyぽいのはgroovy.lang.MetaClassが暗躍しているからか?
  • しかしDEBUGが大変だった。今回はとにかく常にコンテナで動かしたけどTestのしやすさはJavaと比べてどうなのかなーと思う。まずJavaのTestの仕方がなってないのでJUnit in Actionを読もう。