com.mnt.base.web.action.impl.AbstractActionControllerManager.java Source code

Java tutorial

Introduction

Here is the source code for com.mnt.base.web.action.impl.AbstractActionControllerManager.java

Source

/**
 * $Revision: 1.0
 * $Date: 2013-5-21
 *
 * Copyright (C) 2013-2020 MNT. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */

package com.mnt.base.web.action.impl;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.mnt.base.util.CommonUtil;
import com.mnt.base.web.action.AccessChecker;
import com.mnt.base.web.action.ActionController;
import com.mnt.base.web.action.annotation.ACMethod;
import com.mnt.base.web.action.annotation.ACParam;
import com.mnt.base.web.action.annotation.ACPath;
import com.mnt.base.web.action.annotation.ACWebHandler;
import com.mnt.base.web.action.impl.AbstractActionControllerManager.MethodInfoHolder.ResourceInfoHolder;

/**
 * 
 * @author Peyton Peng
 * #date 2012-3-22
 *
 *
 */
public abstract class AbstractActionControllerManager {

    private final Log log = LogFactory.getLog(getClass());

    protected String contextPath;
    protected int lenContxtPath;

    protected AbstractActionControllerManager(String contextPath) {
        this.contextPath = contextPath;
        this.lenContxtPath = this.contextPath.length();
    }

    protected class MethodInfoHolder {
        Method method;
        ACMethod acMethod;
        Map<String, Class<?>> paramsMap;
        Map<String, Map<String, Field>> beanFieldDefMap;

        protected class ResourceInfoHolder extends MethodInfoHolder {
            String resourceDef;
            Map<String, Integer> paramsIndexMap = new LinkedHashMap<String, Integer>();
            Pattern pattern;
            boolean isDefault;

            final Pattern RESOURCE_PARAM_PATTERN = Pattern.compile("\\{([^}]+)\\}");

            public ResourceInfoHolder(String resourceDef) {
                this.resourceDef = resourceDef;
                isDefault = ACMethod.DEFAULT_RESOURCE_DEF.equals(this.resourceDef);
                this.method = MethodInfoHolder.this.method;
                this.acMethod = MethodInfoHolder.this.acMethod;
                this.paramsMap = MethodInfoHolder.this.paramsMap;
            }

            boolean parse() {
                Matcher matcher = RESOURCE_PARAM_PATTERN.matcher(resourceDef);

                StringBuffer buff = new StringBuffer();
                int paramIndex = 1;
                while (matcher.find()) {
                    paramsIndexMap.put(matcher.group(1), paramIndex++);
                    matcher.appendReplacement(buff, "([^\\/]+)");
                }

                if (paramIndex == 1) {
                    buff.append(resourceDef);
                }

                if (buff.charAt(buff.length() - 1) != '/') {
                    buff.append("/?");
                }

                pattern = Pattern.compile(
                        new StringBuilder("^").append(buff.toString().replace("/", "\\/")).append("$").toString());
                return true;
            }
        }
    }

    protected class SimpleControllerWrapper extends ActionController {

        private Object controller;

        public SimpleControllerWrapper(Object controller) {
            this.controller = controller;
        }

        @Override
        public Object handleRequest(String method, Map<String, Object> parameters) {
            Map<String, Object> resultMap = new HashMap<String, Object>();
            resultMap.put(K_RESULT, ActionController.INVALID_REQUEST);
            return resultMap;
        }

        @Override
        public Object getController() {
            return controller;
        }

        @Override
        public String path() {
            return null;
        }
    }

    protected Map<String, ActionController> actionControllerMap = new HashMap<String, ActionController>();
    protected Map<ActionController, Map<String, MethodInfoHolder>> acMethodsMap = new HashMap<ActionController, Map<String, MethodInfoHolder>>();
    protected Map<ActionController, List<ResourceInfoHolder>> acResourcesMap = new HashMap<ActionController, List<ResourceInfoHolder>>();
    protected String defaultActionController;
    protected AccessChecker accessChecker;

    public void setSimpleControllers(Collection<Object> controllers) {
        if (!CommonUtil.isEmpty(controllers)) {
            Collection<ActionController> scs = new ArrayList<ActionController>();
            for (Object controller : controllers) {
                scs.add(new SimpleControllerWrapper(controller));
            }

            setControllers(scs);
        }
    }

    public void setControllers(Collection<ActionController> controllers) {
        if (!CommonUtil.isEmpty(controllers)) {
            for (ActionController ac : controllers) {

                Class<?> acClazz = ac.getController().getClass();

                if (CommonUtil.isEmpty(ac.path())) {
                    ACPath acPath = acClazz.getAnnotation(ACPath.class);
                    ACWebHandler acWebHandler = acClazz.getAnnotation(ACWebHandler.class);

                    if (acWebHandler != null) {
                        ac.setWebHandler(acWebHandler.value());
                    }

                    if (acPath == null && acClazz.getInterfaces().length > 0) {

                        Class<?>[] clazzs = acClazz.getInterfaces();

                        for (Class<?> clazz : clazzs) {
                            acClazz = clazz;
                            acPath = acClazz.getAnnotation(ACPath.class);

                            if (acPath != null) {
                                acWebHandler = acClazz.getAnnotation(ACWebHandler.class);
                                if (acWebHandler != null) {
                                    ac.setWebHandler(acWebHandler.value());
                                }

                                break;
                            }
                        }
                    }

                    if (acPath != null) {
                        log.info(new StringBuilder("Attach the action controller for path: ").append(acPath.value())
                                .append(", implementation class: ")
                                .append(ac.getController().getClass().getName()));
                        actionControllerMap.put(acPath.value(), ac);
                    } else {
                        log.error(
                                "Skip the invalid action controller, the path of the action controller need to be specified, class: "
                                        + ac.getClass().getName());
                        continue;
                    }
                } else {
                    log.info(new StringBuilder("Attach the action controller for path: ").append(ac.path())
                            .append(", implementation class: ").append(ac.getClass().getName()));
                    actionControllerMap.put(ac.path(), ac);
                }

                Map<String, MethodInfoHolder> acMs = new HashMap<String, MethodInfoHolder>();
                List<ResourceInfoHolder> acRs = new ArrayList<ResourceInfoHolder>();
                Method[] methods = acClazz.getDeclaredMethods();

                ACMethod acMethod;
                MethodInfoHolder mih;
                ResourceInfoHolder rih;

                for (Method m : methods) {
                    acMethod = m.getAnnotation(ACMethod.class);

                    if (acMethod != null) {

                        mih = new MethodInfoHolder();
                        mih.method = m;
                        mih.method.setAccessible(true);
                        mih.acMethod = acMethod;
                        mih.paramsMap = new LinkedHashMap<String, Class<?>>();

                        Class<?>[] pts = m.getParameterTypes();

                        if (pts.length > 0) {

                            Annotation[][] pass = m.getParameterAnnotations();

                            Annotation[] pas;

                            ACParam acParam;

                            // skip the first two parameters: parameterMap, responseMap
                            for (int i = 0; i < pass.length; i++) {
                                pas = pass[i];

                                if (pas.length != 1 || !(pas[0] instanceof ACParam)) {
                                    log.warn("no corresponding ACParam specified for actioncontroller: "
                                            + acClazz.getName() + " method: " + m.getName()
                                            + " parameter at index: " + i + ".");

                                    mih.paramsMap.put(ACParam.NULL_PREFIX + i, pts[i]);
                                    continue;
                                }

                                acParam = (ACParam) (pas[0]);

                                if (acParam.beanPrefix()) {
                                    String beanFieldIndexKey = ACParam.BEAN_PREFIX + i;
                                    mih.paramsMap.put(beanFieldIndexKey, pts[i]);
                                    if (mih.beanFieldDefMap == null) {
                                        mih.beanFieldDefMap = new LinkedHashMap<String, Map<String, Field>>();
                                    }

                                    Class<?> paramClass = pts[i];
                                    Field[] fields = paramClass.getDeclaredFields();
                                    Map<String, Field> beanFieldDefMap = new LinkedHashMap<String, Field>();
                                    for (Field field : fields) {
                                        field.setAccessible(true);
                                        beanFieldDefMap.put(new StringBuilder(acParam.value()).append(".")
                                                .append(field.getName()).toString(), field);
                                    }

                                    mih.beanFieldDefMap.put(beanFieldIndexKey, beanFieldDefMap);
                                } else {
                                    mih.paramsMap.put(acParam.value(), pts[i]);
                                }
                            }
                        }

                        if (!CommonUtil.isEmpty(acMethod.resource())) {
                            String resourceDef = acMethod.resource();
                            rih = mih.new ResourceInfoHolder(resourceDef);
                            if (rih.parse()) {
                                acRs.add(rih);
                            }
                        }

                        acMs.put(acMethod.type(), mih);
                    }
                }

                if (!acMs.isEmpty()) {
                    acMethodsMap.put(ac, acMs);
                }

                if (!acRs.isEmpty()) {
                    acResourcesMap.put(ac, acRs);
                }
            }
        }
    }

    protected ActionController getRequestController(String[] secondLevelPaths) {
        ActionController ac = actionControllerMap.get(secondLevelPaths[0]);

        if (ac == null && !CommonUtil.isEmpty(defaultActionController)) {
            ac = actionControllerMap.get(defaultActionController);
        }

        return ac;
    }

    protected Object processRequest(String requestUri, String method, Map<String, Object> parameters) {
        String[] secondLevelPaths = detectSecondLevelPath(requestUri);
        ActionController ac = getRequestController(secondLevelPaths);
        return processRequest(ac, secondLevelPaths, requestUri, method, parameters);
    }

    protected Object processRequest(ActionController actionController, String[] secondLevelPaths, String requestUri,
            String method, Map<String, Object> parameters) {
        if (log.isDebugEnabled()) {
            log.debug("Request: " + requestUri + ", method: " + method + ", parameters: " + parameters);
        }

        Object responseHolder = null;

        if (actionController != null) {
            // check if exists the type first
            String type = ActionController.getParameter(parameters, ActionController.K_TYPE);

            MethodInfoHolder mih = null;

            if (CommonUtil.isEmpty(type)) {

                boolean defaultFlag = false;
                String resValue = null;
                if (secondLevelPaths.length == 2) {
                    resValue = secondLevelPaths[1];
                }

                if (CommonUtil.isEmpty(resValue)) {
                    resValue = ACMethod.DEFAULT_RESOURCE_DEF;
                    defaultFlag = true;
                }

                if (true) {
                    List<ResourceInfoHolder> rihs = acResourcesMap.get(actionController);
                    if (rihs != null) {
                        for (ResourceInfoHolder rih : rihs) {
                            // default only match the default 
                            if (defaultFlag) {
                                if (rih.isDefault) {
                                    mih = rih;
                                    break;
                                } else {
                                    continue;
                                }
                            }

                            Matcher matcher = rih.pattern.matcher(resValue);
                            if (matcher.matches()) {
                                for (String key : rih.paramsIndexMap.keySet()) {
                                    parameters.put(key, matcher.group(rih.paramsIndexMap.get(key)));
                                }

                                mih = rih;
                                break;
                            }
                        }
                    }
                }
            } else {
                Map<String, MethodInfoHolder> mihs = acMethodsMap.get(actionController);
                if (mihs != null) {
                    mih = mihs.get(type);
                }
            }

            if (mih != null) {

                ACMethod acMethod = mih.acMethod;

                if (!acMethod.accessCheck()
                        || accessChecker.accessable(parameters, acMethod.checkLevel(), acMethod.accessRole())) {

                    List<Object> paramVals = new ArrayList<Object>();

                    Map<String, Class<?>> paramsSpec = mih.paramsMap;

                    if (paramsSpec != null) {

                        for (String key : paramsSpec.keySet()) {
                            if (ACParam.ALL_PARAMS.equals(key)) {
                                paramVals.add(parameters);
                            } else if (ACParam.RESPONSE_MAP.equals(key)) {
                                responseHolder = new LinkedHashMap<String, Object>();
                                paramVals.add(responseHolder);
                            } else if (key.startsWith(ACParam.NULL_PREFIX)) {
                                paramVals.add(null);
                            } else if (key.startsWith(ACParam.BEAN_PREFIX)) {
                                Object beanObj = createBeanObj(mih.beanFieldDefMap.get(key), paramsSpec.get(key),
                                        parameters);
                                paramVals.add(beanObj);
                            } else {
                                paramVals.add(
                                        ActionController.getParameterValue(parameters, key, paramsSpec.get(key)));
                            }
                        }
                    }

                    Method mtd = mih.method;

                    try {

                        if (mtd.getReturnType() == Void.class) {
                            mtd.invoke(actionController.getController(), paramVals.toArray());
                        } else {
                            Object result = mtd.invoke(actionController.getController(), paramVals.toArray());
                            responseHolder = (responseHolder == null) ? result : responseHolder;
                        }
                    } catch (Exception e) {
                        log.error("Error when process the request by ActionController", e);

                        Map<String, Object> resultMap = new HashMap<String, Object>();
                        resultMap.put(ActionController.K_RESULT, ActionController.SYSTEM_INTERNAL_ERROR);
                        responseHolder = resultMap;

                    } finally {
                        paramVals.clear();
                    }
                } else {
                    Map<String, Object> resultMap = new HashMap<String, Object>();
                    resultMap.put(ActionController.K_RESULT, ActionController.ACCESS_NO_PRIVILEGE);
                    responseHolder = resultMap;
                }
            } else {
                try {
                    responseHolder = actionController.handleRequest(method, parameters);
                } catch (Exception e) {
                    log.error("Error when process the request by ActionController", e);
                }
            }
        } else {
            log.warn("Invalid second level path access: " + "Request: " + requestUri + ", method: " + method
                    + ", parameters: " + parameters);
        }

        return responseHolder;
    }

    private Object createBeanObj(Map<String, Field> beanFieldDefMap, Class<?> beanClass,
            Map<String, Object> parameters) {

        Object beanObj = null;
        try {
            beanObj = beanClass.newInstance();
        } catch (Exception e) {
            log.error("error while create bean object for beanClass: " + beanClass.getName());
        }

        if (beanObj != null) {
            //ActionController.getParameterValue(parameters, key, paramsSpec.get(key))

            Object fieldVal;
            Field field;
            for (String fieldRef : beanFieldDefMap.keySet()) {
                if (parameters.containsKey(fieldRef)) {
                    field = beanFieldDefMap.get(fieldRef);
                    fieldVal = ActionController.getParameterValue(parameters, fieldRef, field.getType());
                    if (fieldVal != null) {
                        try {
                            field.set(beanObj, fieldVal);
                        } catch (Exception e) {
                            log.error("error while set the bean field value: " + fieldVal + ", beanObj: " + beanObj
                                    + ", field: " + field.getName());
                        }
                    }
                }
            }
        }

        return beanObj;
    }

    protected String[] detectSecondLevelPath(String requestUri) {
        String[] secondLevelPaths = null;

        if (requestUri.startsWith(contextPath) && requestUri.length() > lenContxtPath) {
            String secondLevelPath = requestUri.substring(contextPath.length() + 1); // context value: "/<contextpath>", need to remove the value "/<contextpath>/"
            secondLevelPaths = secondLevelPath.split("\\/", 2);
        }

        return secondLevelPaths;
    }

    public String getDefaultActionController() {
        return defaultActionController;
    }

    public void setDefaultActionController(String defaultActionController) {
        this.defaultActionController = defaultActionController;
    }

    public AccessChecker getAccessChecker() {
        return accessChecker;
    }

    public void setAccessChecker(AccessChecker accessChecker) {
        this.accessChecker = accessChecker;
    }
}