続 SAStrutsをStruts形式のURLでも動くようにする

前回エントリの続き。
S2ModuleConfigを継承して、もう少しコードを整理してみる。

まずはFilter
2009/10/6 追記
前回エントリの修正に合わせて修正しました。

public class MyRoutingFilter extends RoutingFilter {
    public static final String INCLUDE_PATH_INFO = "javax.servlet.include.path_info";
    public static final String INCLUDE_SERVLET_PATH = "javax.servlet.include.servlet_path";

    protected static final MessageResources messages = MessageResources
            .getMessageResources("org.apache.struts.actions.LocalStrings");

    /** Struts URLパターンの正規表現 */
    protected String strutsUrlPattern = ".*Action\\.do$";

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;
        String path = RequestUtil.getPath(req);

        if (!processDirectAccess(request, response, chain, path)) {
            return;
        }

        if (isStrutsPath(path)) {
            ActionConfig actionConfig = ((MyModuleConfig) S2ModuleConfigUtil
                    .getModuleConfig()).findStrutsActionConfig(processPath(req, res));
            if (actionConfig != null) {
                // struts-config.xmlに定義されているActionのフルクラス名を取得
                String actionType = actionConfig.getType();

                // ForwardAction の場合は、struts-config.xml の parameter
                // に定義されているパスへフォワードする
                if ("org.apache.struts.actions.ForwardAction"
                        .equals(actionType)) {
                    executeForwardAction(actionConfig, req, res);
                    return;
                }

                // struts-config.xml に forward 属性が定義されている場合は、定義されているパスへフォワードする
                if (actionConfig.getForward() != null) {
                    request.getRequestDispatcher(actionConfig.getForward())
                            .forward(request, response);
                    return;
                }

                // IncludeAction の場合は、struts-config.xml の parameter
                // に定義されているパスをインクルードする
                if ("org.apache.struts.actions.IncludeAction"
                        .equals(actionType)) {
                    executeIncludeAction(actionConfig, req, res);
                    return;
                }

                String actionPath = getActionPath(actionType);

                // Actionの実行メソッド名を取得
                String methodName = getActionMethodName(actionConfig, req);

                if (SingletonS2ContainerFactory.getContainer()
                    .hasComponentDef(ActionUtil.fromPathToActionName(actionPath))) {
                    if (StringUtil.isEmpty(methodName)) {
                        // RoutingFilter では、URLにメソッド指定がない場合は、
                        // S2ExecuteConfigUtil#findExecuteConfig(String,
                        // HttpServletRequest)で
                        // S2ExecuteConfigを取得しているが、その場合、SAStrutsの仕様により
                        // URLのパラメータ名にメソッド名と一致するものがあれば
                        // そのメソッドが実行されてしまうためStrutsと挙動が変わってしまう。
                        // Strutsと挙動を合わせるため、デフォルトメソッド名を補完する。
                        S2ExecuteConfig executeConfig = findExecuteConfig(actionPath, "index");
                        if (executeConfig != null) {
                            forward(req, res, actionPath, null, null);
                            return;
                        }
                    } else {
                        S2ExecuteConfig executeConfig = findExecuteConfig(actionPath, methodName);
                        if (executeConfig != null) {
                            forward(req, res, actionPath, methodName, executeConfig);
                            return;
                        }
                    }
                }
            }
        } else {
            super.doFilter(request, response, chain);
            return;
        }
        res.sendError(HttpServletResponse.SC_NOT_FOUND, req.getRequestURI());
    }

    // 他のメソッドは前エントリと同じなので省略
}

Struts設定情報を保持するModuleConfig

public class MyModuleConfig extends S2ModuleConfig {
    private static final long serialVersionUID = 1L;

    /** Struts の Action 設定情報 */
    protected HashMap<String, ActionConfig> strutsActionConfigs = null;

    public MyModuleConfig(String prefix) {
        super(prefix);
        initialize();
    }

    @Override
    public void freeze() {
        super.freeze();
        // struts-config.xml の設定情報は、ActionServlet 初期化時には actionConfigs に格納されるが、
        // HotDeployではリクエストのたびに、S2ModuleConfig#disposeで actionConfigs がクリアされるため、
        // strutsActionConfigs に退避しておく。
        // struts-config.xml の設定情報は、HotDeploy 対象外となるため、strutsActionConfigs は
        // dispose でクリアしない。
        if (strutsActionConfigs == null) {
            strutsActionConfigs = new HashMap<String, ActionConfig>();
            for (Object key : actionConfigs.keySet()) {
                ActionConfig config = (ActionConfig) actionConfigs.get(key);
                if (!(config instanceof S2ActionMapping)) {
                    // SAStruts ではない、Struts の ActionMapping を保存する
                    strutsActionConfigs.put((String) key, config);
                }
            }
        }
    }

    public ActionConfig findStrutsActionConfig(String path) {
        return strutsActionConfigs.get(path);
    }
}

最後はMyModuleConfigを使用させるためのファクトリ

public class MyModuleConfigFactory extends ModuleConfigFactory {
    @Override
    public ModuleConfig createModuleConfig(String prefix) {
        return new MyModuleConfig(prefix);
    }
}

使い方

web.xmlに次のようにMyModuleConfigFactoryを設定する。
その他については、前回エントリと同じ。

<servlet>
    <servlet-name>action</servlet-name>
    <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
    <init-param>
        <param-name>config</param-name>
        <param-value>/WEB-INF/struts-config.xml</param-value>
    </init-param>
    <init-param>
        <param-name>configFactory</param-name>
        <param-value>hoge.config.MyModuleConfigFactory</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>