トップ 差分 一覧 ソース ヘルプ RSS ログイン

S2Click

ClickとS2の連携

ClickServletを拡張してS2Containerからページクラスを作成するサーブレットを作ってみました。ClickにはSpring用のサーブレットも用意されているのでそれを参考にしました。ちょっと長いですがソースを全文掲載しておきます。

package tk.sample;

import java.lang.reflect.Field;
import java.util.Map;

import javax.servlet.GenericServlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.sf.click.ClickServlet;
import net.sf.click.Context;
import net.sf.click.Page;

import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.SingletonS2ContainerFactory;
import org.seasar.framework.util.StringUtil;

/**
 * Provides Seasar2 (S2) integration <tt>S2ClickServlet</tt>.
 * <p/>
 * This specialized Click Servlet can inject S2 dependencies into
 * defined S2 Page component. If a requested Page is not configured 
 * as a S2 component, then a plain new Page instance is created.
 * <p/>
 * You have to register Page classes to the dicon file 
 * (the dicon file is the configuration file of Seasar2) like following:
 * <pre class="codeConfig">
 * &lt;components&gt;
 *   &lt;component name="/index.htm" 
 *              class="examples.IndexPage" 
 *              instance="prototype" /&gt;
 * &lt;/components&gt;
 * </pre>
 * There are no needs to register Page classes to click.xml.
 * <p/>
 * <strong>Note:</strong> the instance attribute of component 
 * in the dicon file have to be prototype or request.
 * 
 * @author Naoki Takezoe
 */
public class S2ClickServlet extends ClickServlet {
    
    private static final long serialVersionUID = -1915099546074919781L;
    private S2Container container;
    public static final String CONFIG_PATH_KEY = "configPath";
    
    /**
     * Initializes the Click servlet, the Velocity runtime and S2Container.
     *
     * @see GenericServlet#init()
     */
    public void init() throws ServletException {
        super.init();
        
        String configPath = null;
        ServletConfig servletConfig = getServletConfig();
        if (servletConfig != null) {
            configPath = servletConfig.getInitParameter(CONFIG_PATH_KEY);
        }
        
        if (!StringUtil.isEmpty(configPath)) {
            SingletonS2ContainerFactory.setConfigPath(configPath);
        }
        
        SingletonS2ContainerFactory.setServletContext(getServletContext());
        SingletonS2ContainerFactory.init();
        container = SingletonS2ContainerFactory.getContainer();
    }
    
    /**
     * Creates and initializes a new Page instance.
     * This method gets the Page instance from S2Container.
     * 
     * @param request the servlet request
     * @param response the servlet response
     * @param isPost determines whether the request is a POST
     * @return a new Page instance for the given request
     */
    protected Page createPage(HttpServletRequest request,
        HttpServletResponse response, boolean isPost) {

        Context context = new Context(getServletContext(),
                                      getServletConfig(),
                                      request,
                                      response,
                                      isPost,
                                      pageMaker);

        String path = context.getResourcePath();

        if (request.getAttribute(FORWARD_PAGE) != null) {
            Page forwardPage = (Page) request.getAttribute(FORWARD_PAGE);
            forwardPage.setContext(context);
            request.removeAttribute(FORWARD_PAGE);
            return forwardPage;
        }
        
        Page page = null;
        
           if(container.hasComponentDef(path)){
            page = (Page)container.getComponent(path);
           } else {
            page = super.createPage(request, response,isPost);
        }
           
           Object clickApp = getField(ClickServlet.class, this, "clickApp");
           
           if(page.getFormat()==null){
               try {
                   Class formatClass = (Class)getField(clickApp.getClass(), clickApp, "formatClass");
                   page.setFormat(formatClass.newInstance());
               } catch(Exception ex){
                throw new RuntimeException(ex);
               }
           }
           
           if(page.getHeaders()==null){
               page.setHeaders((Map)getField(clickApp.getClass(), clickApp, "commonHeaders"));
           }
           
           page.setPath(path);
        page.setContext(context);
        return page;
    }
    
    /**
     * This is the utility method to get the field using reflection.
     * 
     * @param type The type that declares the field
     * @param obj The target object
     * @param fieldName the field name
     * @return the field object
     */
    private Object getField(Class type, Object obj, String fieldName){
        try {
            Field[] fields = type.getDeclaredFields();
            for(int i=0;i<fields.length;i++){
                if(fields[i].getName().equals(fieldName)){
                    fields[i].setAccessible(true);
                    return fields[i].get(obj);
                }
            }
        } catch(Exception ex){
            throw new RuntimeException(ex);
        }
        return null;
    }
    
}

ダウンロードは以下のリンクからどうぞ。

このサーブレットを以下のような感じでweb.xmlに登録しておきます。サーブレットの初期化パラメータで設定ファイルを指定できるのはS2ContainerServletと同じです。指定しなければクラスパスルートのapp.diconを読み込みます。

<servlet>
  <servlet-name>click-servlet</servlet-name>
  <servlet-class>tk.sample.S2ClickServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
  <servlet-name>click-servlet</servlet-name>
  <url-pattern>*.htm</url-pattern>
</servlet-mapping>

diconファイルには以下のようにページクラスを登録しておきます。

<component name="/index.htm" class="tk.sample.IndexPage" instance="prototype"/>

ページクラスのinstance属性はrequestまたはprototypeにしておく必要があります。Clickに付属している SpringClickServletとは違ってclick.xmlにはページクラスを登録しておく必要はありません(SpringClickServletはclick.xmlとSpringの設定ファイルの両方にページクラスを登録しておく必要があります)。