Package com.bastengao.struts2.freeroute

Source Code of com.bastengao.struts2.freeroute.DefaultUnknownHandler

package com.bastengao.struts2.freeroute;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ObjectFactory;
import com.opensymphony.xwork2.UnknownHandler;
import com.opensymphony.xwork2.XWorkException;
import com.opensymphony.xwork2.config.Configuration;
import com.opensymphony.xwork2.config.entities.ActionConfig;
import com.opensymphony.xwork2.config.entities.PackageConfig;
import com.opensymphony.xwork2.config.entities.ResultConfig;
import com.opensymphony.xwork2.config.entities.ResultTypeConfig;
import com.opensymphony.xwork2.inject.Inject;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
* 默认处理未知的 result 或者 action.
* <p/>
* 实现此类,主要通过获取 action 动态返回的 result 映射到对应的页面
*
* @author bastengao
* @date 12-12-18 22:03
* @since 1.0
*/
public class DefaultUnknownHandler implements UnknownHandler {
    private final static Logger log = LoggerFactory.getLogger(DefaultUnknownHandler.class);

    private ObjectFactory objectFactory;
    private Configuration configuration;
    private RouteMappingHandler routeMappingHandler;

    // 全局内容基路径(可选)
    private String contentBase;

    @Inject(value = "struts.freeroute.contentBase", required = false)
    private void setContentBase(String contentBase) {
        contentBase = ActionUtil.padSlash(contentBase);
        this.contentBase = ActionUtil.shrinkEndSlash(contentBase);
    }

    @Inject("routeMappingHandler")
    private void setRouteMappingHandler(RouteMappingHandler routeMappingHandler) {
        this.routeMappingHandler = routeMappingHandler;
    }

    @Inject
    public DefaultUnknownHandler(ObjectFactory objectFactory, Configuration configuration) {
        this.objectFactory = objectFactory;
        this.configuration = configuration;
    }

    @Override
    public ActionConfig handleUnknownAction(String namespace, String actionName) throws XWorkException {
        //这不是我的菜
        return null;
    }

    /**
     * 因为 actionConfig 只配置了 action name 与 method, 没有配置 result,
     * 所以任何一个 freeroute 的 action 执行完返回 的 resultCode 都是 unknown result,
     * 都会执行以下方法.
     *
     * @param actionContext
     * @param actionName
     * @param actionConfig
     * @param resultCode
     * @return
     * @throws XWorkException
     */
    @Override
    public com.opensymphony.xwork2.Result handleUnknownResult(ActionContext actionContext, String actionName, ActionConfig actionConfig, String resultCode) throws XWorkException {
        //这是我的菜, 更擅长处理未知的 result
        log.debug("catch result[{}] of action[{}]", resultCode, actionName);
        RouteMapping routeMapping = routeMappingHandler.route(actionConfig);
        log.debug("route: {}", routeMapping);

        ResultConfig resultConfig = parseResultCodeToResultConfig(actionConfig, resultCode, routeMapping);
        if (resultConfig == null) {
            return null;
        }

        try {
            return objectFactory.buildResult(resultConfig, actionContext.getContextMap());
        } catch (Exception e) {
            throw new XWorkException("Unable to build result", e, resultConfig);
        }
    }

    @Override
    public Object handleUnknownActionMethod(Object action, String methodName) throws NoSuchMethodException {
        //这不是我的菜
        return null;
    }

    /**
     * 解析 resultCode 为 ResultConfig, 如果无法解析则返回 null
     *
     * @param actionConfig
     * @param resultCode
     * @param routeMapping
     * @return
     */
    private ResultConfig parseResultCodeToResultConfig(ActionConfig actionConfig, String resultCode, RouteMapping routeMapping) {
        String packageName = actionConfig.getPackageName();
        PackageConfig packageConfig = configuration.getPackageConfig(packageName);
        Map<String, ResultTypeConfig> resultTypes = packageConfig.getAllResultTypeConfigs();

        if (log.isDebugEnabled()) {
            log.debug("packageName:{}", packageName);
            log.debug("resultTypes:{}", resultTypes);
        }

        return findResultConfig(routeMapping, resultCode, resultTypes);
    }

    /**
     * 找能够处理的 resultType
     *
     * @param routeMapping
     * @param resultCode
     * @param resultTypes
     * @return
     */
    private ResultConfig findResultConfig(RouteMapping routeMapping, String resultCode, Map<String, ResultTypeConfig> resultTypes) {
        // 先解析出返回类型,然后再去 resultTypes 查找是否存在
        String resultType = parseResultType(resultCode);
        //如果不存在此返回类型, 直接返回
        if (!resultTypes.containsKey(resultType)) {
            return null;
        }

        // resultValue 有两种可能:
        // 1.一种直接是字面量(比如 dispatcher:/xxx.html)
        // 2.另一种是 json 参数(比如 dispatcher:{'location': '/xxx.html'} )

        ResultTypeConfig typeConfig = resultTypes.get(resultType);
        //如果没有默认参数
        if (Strings.isNullOrEmpty(typeConfig.getDefaultResultParam())) {
            ResultConfig.Builder resultBuilder = createResultConfigFromResultType(resultCode, typeConfig);
            // 只有返回类型, 例如是 "json"
            if (resultCode.length() == resultType.length()) {
                return resultBuilder.build();
            }
            //或者 ":" 后面有 json 参数
            if (resultCode.indexOf(":") == resultType.length()) {
                String resultParam = resultCode.substring(resultType.length() + 1);
                addParamByJSON(resultBuilder, resultParam);
                return resultBuilder.build();
            }
        }
        //如果有默认参数
        else {
            //  如果是 "type:" 这种形式
            if (resultCode.indexOf(":") == resultType.length()) {
                ResultConfig.Builder resultBuilder = createResultConfigFromResultType(resultCode, typeConfig);

                // 拿到参数部分(":" 后面的部分)
                String resultParam = resultCode.substring(resultType.length() + 1);
                String location = null; // 通常是 defaultParam
                if (isJSONObject(resultParam)) { //判断是否是 json 格式
                    /**
                     * 如果是 json 形式传递 param(并非 struts json 插件)的返回结果,
                     * 如果有 location ,也需要进行路径相对路径或绝对路径转换
                     */
                    Map<String, String> params = addParamByJSON(resultBuilder, resultParam);
                    location = params.get(typeConfig.getDefaultResultParam());
                } else {
                    location = resultParam;
                }

                // 页面路径的转换如果是相对路径, 但除过重定向
                if(!resultType.equals("redirect")){
                    location = parsePath(contentBase, routeMapping, location);
                }
                resultBuilder.addParam(typeConfig.getDefaultResultParam(), location);
                return resultBuilder.build();
            }
        }

        return null;
    }

    /**
     * 通过 ResultTypeConfig 构造 ResultConfig.Builder
     *
     * @param resultCode
     * @param typeConfig
     * @return
     */
    private static ResultConfig.Builder createResultConfigFromResultType(String resultCode, ResultTypeConfig typeConfig) {
        ResultConfig.Builder resultBuilder = new ResultConfig.Builder(resultCode, typeConfig.getClassName());
        if (typeConfig.getParams() != null) {
            resultBuilder.addParams(typeConfig.getParams());
        }

        return resultBuilder;
    }

    /**
     * 解析返回类型。如果没有 ":" 则返回全部; 如果有 ":" 则返回 ":"  前面部分。
     *
     * @param resultCode
     * @return
     */
    @VisibleForTesting
    static String parseResultType(String resultCode) {
        int index = resultCode.indexOf(":");
        if (index == -1) {
            return resultCode;
        }

        return resultCode.substring(0, index);
    }


    /**
     * 解析路径
     * 区分相对路径还是绝对路径。如果是绝对路径则不需要转换。
     * <p/>
     * 如果是相对路径那么则将其转化为绝对路径。转化时优先使用 @ContentBase,然后 contentBase,
     * 如果前两个都不满足则直接转换为绝对路径
     *
     * @param globalContentBase
     * @param routeMapping
     * @param originPath
     * @return
     */
    @VisibleForTesting
    static String parsePath(String globalContentBase, RouteMapping routeMapping, String originPath) {
        //如果是绝对路径
        if (originPath.startsWith("/")) {
            // 啥也不干
            return originPath;
        }

        //如果是相对路径

        //如果有 @ContentBase 配置,在 path 前追加 @ContentBase.
        if (routeMapping.getContentBase() != null) {
            String contentBase = ActionUtil.padSlash(routeMapping.getContentBase().value());
            originPath = ActionUtil.shrinkEndSlash(contentBase) + ActionUtil.padSlash(originPath);
            return originPath;
        }

        //如果全局内容基存在,则在 path 前追加
        if (!Strings.isNullOrEmpty(globalContentBase)) {
            originPath = globalContentBase + ActionUtil.padSlash(originPath);
            return originPath;
        }

        // 如果没有则将路径转换为绝对路径进行尝试
        originPath = ActionUtil.padSlash(originPath);

        return originPath;
    }

    /**
     * 测试是否是 json 参数
     *
     * @param param
     * @return
     */
    @VisibleForTesting
    static boolean isJSONObject(String param) {
        try {
            new JSONObject(param);
            return true;
        } catch (JSONException e) {
            return false;
        }
    }

    private static Map<String, String> addParamByJSON(ResultConfig.Builder resultBuilder, String resultParam) {
        try {
            Map<String, String> params = new HashMap<String, String>();
            JSONObject jsonObject = new JSONObject(resultParam);
            for (Iterator it = jsonObject.keys(); it.hasNext(); ) {
                String key = (String) it.next();
                resultBuilder.addParam(key, jsonObject.getString(key));
                params.put(key, jsonObject.getString(key));
            }
            return params;
        } catch (JSONException e) {
            throw new IllegalArgumentException("could not parse result param", e);
        }
    }
}
TOP

Related Classes of com.bastengao.struts2.freeroute.DefaultUnknownHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.