T2FrameworkとCDIを組み合わせる

早速T2FrameworkCDIを組み合わせてみた。
まずアダプタが必要だが、それは後ろのほうでコードを乗せる。

まず見ていただきたいアプリの必要なソース/JSPを先にのせる。

入力した名前を表示するだけのWebアプリ。入力していなければ「名前を入力してください」と表示してある。

JSP

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/html4/loose.dtd">

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>T2とCDIのすばらしい組み合わせ</title>
    </head>
    <body>
        ${requestData.message}
        <form action="">
            <input type="text" name="name" value="${requestData.name}"/>
            <input type="submit" value="はろー" />
        </form>
    </body>
</html>

ページクラス

package t2cdi;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named;
import org.t2framework.t2.annotation.core.Default;
import org.t2framework.t2.annotation.core.Form;
import org.t2framework.t2.annotation.core.Page;
import org.t2framework.t2.navigation.Forward;
import org.t2framework.t2.spi.Navigation;


@RequestScoped
@Page("/")
@Named("requestData")
public class HelloPage {

    String name ;

    @Default
    public Navigation index(@Form HelloPage input){
        
        this.name = input.name;

        return Forward.to("/index.jsp");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getMessage() {
        if(name == null){
            return "名前を入力してください";
        }
        return "Hello! "+name+" /instance="+this;//インスタンス表示
    }
}

実行!

http://shin.cside.com/diary/2010/0116-01.png

名前を入れてボタンを押すたびリクエストスコープのインスタンスが毎回変わるのがわかる。

requestに対してsetAttributeしていないのがわかるだろうか。これはページクラスをシングルトンにして、入力フォームをリクエストスコープの別のクラスにして@Injectで注入しても同じ(というか上の画像は別クラスにしていたのでインスタンスの名前が別クラスになている。ご愛嬌)。


ただ見てわかるようにフォームの値の受け渡しが激しくダサい。どうやらフォームの入力のときだけはDIコンテナ経由で取得してくれないようだ。
バグ?仕様?このへんドキュメントに書いてあったっけ?

プラグインとかの拡張ポイントで@Formで指定されたフォームの生成方法が対応できるならいいけど、どーすればいいのかな。


CDIAOPトランザクションも設定できるし、シンプルなのがいいと思う。

注入方法はJSR-330で定義された標準スタイルなのでSpring 3.0でもまったく同じ。

CDIアダプタのソース

というわけで、アダプタのソース。中身よくわかってないので間違ってるかもです。

package t2cdi;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.t2framework.commons.meta.BeanDesc;
import org.t2framework.commons.meta.BeanDescFactory;
import org.t2framework.commons.util.CollectionsUtil;
import org.t2framework.t2.adapter.AbstractContainerAdapter;
import org.t2framework.t2.handler.ExceptionHandler;
import org.t2framework.t2.handler.GlobalExceptionHandler;
import org.t2framework.t2.handler.impl.GlobalExceptionHandlerImpl;

public class CdiAdapter extends AbstractContainerAdapter<BeanManager> {

    BeanManager beanManager;

    public void init() {
        init("");
    }

    public void init(String string) {
        try {
            beanManager = InitialContext.doLookup("java:comp/BeanManager");

        } catch (NamingException ex) {
            Logger.getLogger(CdiAdapter.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

    public <T> boolean hasComponent(Class<T> type) {
        for (Bean b : beanManager.getBeans(type)) {
            System.out.println(type +"/hasComponent-true");
            return true;
        }

        System.out.println(type+"/hasComponent-false");
        return false;
    }

    public <T> T getComponent(Class<? super T> type) {
        for (Bean b : beanManager.getBeans(type)) {
            CreationalContext cc = beanManager.createCreationalContext(null);

            Object result = beanManager.getReference(b, type, cc);
            System.out.println("生成"+type+"/instance="+result);
            return (T) result;
        }
        System.out.println("生成失敗"+type);
        return null;
    }

    public <T> List<T> getComponents(Class<? super T> type) {
        List<Class<?>> result = new ArrayList<Class<?>>();
        for (Bean b : beanManager.getBeans(type)) {
            result.add(b.getBeanClass());
        }

        return  (List<T>) result;
    }

    public <T> BeanDesc<T> getBeanDesc(Class<? super T> type) {
        if(hasComponent(type)){
            return (BeanDesc<T>) BeanDescFactory.getBeanDesc(type);
        }
        return null;
    }

    public <T> void register(Class<? extends T> type) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public <T> void register(T t) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public <T> T injectDependency(T t) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void destroy() {
    }

    public BeanManager getContainer() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public List<ExceptionHandler<Throwable, Exception>> createExceptionHandlers() {
        return CollectionsUtil.emptyList();
    }

    public GlobalExceptionHandler createGlobalExceptionHandler() {
        return new GlobalExceptionHandlerImpl();
    }

}