GroovyDelegateInterceptor

なんか昨日の日記の追記と矛盾するけどgroovyスクリプトにインタフェースを実装させるのは面倒くさい。
interfaceをimplementsする場合、メソッドのシグネチャはインタフェースと厳密に一致しなければいけない。アクセス修飾子、戻り値、メソッド名、引数の型と数、throws宣言など。それにインタフェースで宣言されているメソッドすべてを実装しなければいけない。でもそんなにいっぱい書くのめんどくさい、せっかくスクリプトなのに。そこでDelegateInterceptor!diconからお手軽にgroovyスクリプトを呼び出そう。

package study.seasar;

import groovy.lang.GroovyClassLoader;

import java.io.File;
import java.io.IOException;

import org.codehaus.groovy.control.CompilationFailedException;
import org.seasar.framework.aop.interceptors.DelegateInterceptor;
import org.seasar.framework.exception.IORuntimeException;
import org.seasar.framework.exception.IllegalAccessRuntimeException;
import org.seasar.framework.exception.InstantiationRuntimeException;
import org.seasar.framework.util.ResourceUtil;

public class GroovyDelegateInterceptor extends DelegateInterceptor {

    private String path_;
    
    public GroovyDelegateInterceptor() {
    }
    
    public GroovyDelegateInterceptor(String path) {
        setGroovyScriptPath(path);
    }
    
    public Object getGroovyScriptPath() {
        return path_;
    }

    public void setGroovyScriptPath(String path) {
        path_ = path;
        Class clazz = createClass(path_);
        Object obj;
        try {
            obj = clazz.newInstance();
        } catch (InstantiationException ex) {
            throw new InstantiationRuntimeException(clazz, ex);
        } catch (IllegalAccessException ex) {
            throw new IllegalAccessRuntimeException(clazz, ex);
        }
        setTarget(obj);
    }
    
    private Class createClass(String groovyPath) {
        GroovyClassLoader groovyLoader = 
            new GroovyClassLoader(getClass().getClassLoader());
        File file = ResourceUtil.getResourceAsFile(groovyPath);
        try {
            return groovyLoader.parseClass(file);
        } catch (CompilationFailedException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new IORuntimeException(e);
        }
    }    
}
  • Hoge.java インタフェース。クライアントからはこのインタフェース経由でgroovyスクリプトを実行。
public interface Hoge{
    Object hoge();
    String foo() throws IOException;
    int add(int a, int b);   
    
    int sub(int a, int b);
}
  • hogeFake.groovy groovyスクリプト。インタフェースとメソッド名称と引数の数さえあっていれば呼び出せる。
import java.io.*
class hogeFake {
    hoge() {
        return "hoge method"
    }
    foo() {
        return new test().poo()
    }
    add(i, j) {
        return i + j
    }
    
}

class test {
    poo() {
        "poo"
    }
}


    
        
            
                "study/seasar/hogeFake.groovy"
            
        
        

  • Client.java クライアントはこんなカンジ。インタフェースさえ見とけばいい。
package study.seasar;


import java.io.IOException;

import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.S2ContainerFactory;

public class Client {

    private static final String PATH = "study/seasar/delegate.dicon";

    public static void main(String[] args) {
        S2Container container = S2ContainerFactory.create(PATH);
        container.init();
        try {
            Hoge hoge = (Hoge)container.getComponent(Hoge.class);
            System.out.println(hoge.hoge());
            try {
                System.out.println(hoge.foo());
            } catch (IOException e) {
                System.out.println(e.getMessage());
            }
            System.out.println(hoge.add(40, 80));
        } finally {
            container.destroy();
        }
    }
}
  • 実行結果

hoge method
poo
120

表示した値があまりに面白みがないのは、まあ気にしない。

インタフェース経由で呼び出せば取替えきくし、とりあえず動かすというときには便利だと思う。あれ、でもこれじゃgroovyから生成したクラスにDIできなくなるのか?