TemplateServlet

GroovyにTemplateServletというものがあることを発見。JSPに似た構文を使えるらしい。

  • JSPみたいに<% %>や<%= %>が使える。${}もつかえる。
  • 暗黙変数もjspと同じ。Defaultのものを使うならinit-paramでtrueを指定する。暗黙変数はTemplateServletを継承すればカスタマイズ可能。
  • 入力画面からのパラメータ(Request Parameter)はrequestから取り出さなくても次画面でそのまま使用できる。web.xmlのinit-paramでtrueを指定する必要あり。
  • Groovyなので型を意識する必要なし。<% %>のなかでも${}でもパス構造?(request.class.methods.nameみたいな)でアクセスできる。
  • GroovyServletよりこっちのほうが使いやすそう。

今わかってるのはこれくらい。

web.xml


    Template
    groovy.servlet.TemplateServlet
    
        bindDefaultVariables
        true
    
    
        bindRequestParameters
        true
            


    Template
    *.template

カスタムタグみたいのはあるのか(これから出来るのか)なぁ?

groovy.servlet.GroovyServletの拡張

ソースはとても短いので継承するにしても別個に同等のものつくるにしても簡単。
GroovyServletでは「request」「response」「application」「session」「out」の変数がGroovletで暗黙的に使用できるように定義されている。outの変数を定義する前にServletResponse#setContentType()をしとけば日本語が表示可能。

暗黙変数作るのも簡単なのでS2Containerもたとえば「container」とかで定義できちゃう。
暗黙変数を定義して使うのっていうのはGroovletじゃなくても使えそう。これは面白い。

import groovy.xml.MarkupBuilder

html = new MarkupBuilder(out).html() {
  head() {
    title("日本語つかえます")
  }  
  p("宣言しなくてもcontainerの変数使えます")
  body() {
    table(border:1) {
      tr() {	
        td("戻り値"); td("メソッド名"); td("引数の型")
      }
      for (m in container.class.methods) {
        tr() {
          td(m.returnType.name); td(m.name); td(m.parameterTypes.name)
        }
      }  
    }
  }
}

GroovletとMarkupBuilder

Groovletを動かしてみる。なんとなく使ってみたくなった。

Groovy - Java用スクリプト言語の例ではHTML自体が文字列でその中に変数埋め込んでいるけど、条件分岐やループを使ってページを生成するにはMarkupBuilderを使うことになると思う。生きてまをみてMarkupBuilderのコンストラクタにWriterを渡せることに気づいたので暗黙変数のoutをわたす。

import groovy.xml.MarkupBuilder

map = [1:"one",2:"two",3:"three"]

html = new MarkupBuilder(out).html() {
  head() {
    title("Groovlet and MarkupBuilder")
  }
  body() {
    p("Number Mapping Display")
    table(border:1) {
      for (e in map) {
        tr() {
          td(e.key); td(e.value) //;を使って横に書く
        }
      }  
    }
  }
}

結果

Number Mapping Display

1 one
2 two
3 three

  • 感想など
    • 設定はweb.xmlを編集するだけなのですぐ使える。
    • 日本語を表示したかったけどどうするのだろう?HttpServletResponse#setContentType()つかえないし。
    • eachとかtimesをつかったループが使えなかった。for文は問題ない。
    • タグのBodyと属性の両方指定することはできない?
    • ひとつのタグで1行じゃなきゃいけないのか?td()など横にいくつも書けたらいいのに。でないと見にくい。横に書ける。;が使える忘れてた。
    • 使い勝手はいまいち?

「Groovlet」でぐぐってみると日本語のページは3件だけ。自分の日記もふくまれちゃってる。

S2GroovyBuilderでClosureを使う

adviceにClosureを渡せるところがすてきだと思う。

こんなことをしてみた。

    • Container全体でTraceInterceptorを使うかどうかのコントロール
    • Container全体でDEBUG用のロジックを使うかどうかのコントロール
    • 上記二つの条件分岐により設定情報が見にくくならないようにメソッド使う。

CalcClient

package taedium.study;

import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.S2ContainerFactory;
import test.org.seasar.extension.openamf.Calculator;

public class CalcClient {

    private static final String PATH =
        "taedium/study/CalcClient.groovy";
        
    public static void main(String[] args) {
        S2Container container = S2ContainerFactory.create(PATH);
        Calculator calc = (Calculator) container.getComponent(Calculator.class);
        int answer = calc.plus(10,20);
        System.out.println(answer);
    }
}

クラスに属さないメソッドの定義(defを使ったもの)の中からはそのスコープの外で定義された変数を参照できないのでメソッドからDEBUGやTRACEを参照するためにクラスをつくる。
CalcClient.groovy

import org.seasar.framework.aop.interceptors.*
import org.seasar.groovy.*
import test.org.seasar.extension.openamf.CalculatorImpl

// Container
new SeasarBuilder().components(){
    ad = new Advices(); // 下で定義したclass
    include("j2ee.dicon")
    component(class: TraceInterceptor, name: "trace")
    component(class: CalculatorImpl){
        aspect(advice: "j2ee.requiredTx")
        aspect(advice: ad.trace())
        aspect(advice: ad.intercept())
    }
}


class Advices { 
    TRACE = true
    DEBUG = true
    
    /* DefaultのClosure、interceptされてるmethodを実行するだけ */
    invocation = { return it.proceed() }
            
    /*
     * TRACEがtrueならばTraceInterceptorのComponent登録名(String)を返す
     * TRACEがfalseならばDefaultのClosureを返す
     */
    Object trace() {
        if (TRACE) {
            return "trace"
        }        
        return invocation
    }
    
     /*
      * DEBUGがtrueならば任意のClosureを返す
      * DEBUGがfalseならばDefaultのClosureを返す
      */    
      Object intercept() {
        if (DEBUG) {
            return { return 9999999 }
        }        
        return invocation
    }
}

実行結果(TRACEもDEBUGもtrueの場合)

BEGIN est.org.seasar.extension.openamf.CalculatorImpl#plus(10, 20)
END est.org.seasar.extension.openamf.CalculatorImpl#plus(10, 20) : 9999999
9999999

実行結果(TRACEもDEBUGもfalseの場合)

30

ひさびさにSeasarさわった。
diconからgroovyをincludeできることとその逆ができることを確認。適材適所で使い分けができますね。