Package net.paoding.rose.web.impl.module

Source Code of net.paoding.rose.web.impl.module.ModulesBuilderImpl$InterceptorBuilder

/*
* Copyright 2007-2009 the original author or authors.
*
* 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 net.paoding.rose.web.impl.module;

import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.servlet.ServletContext;

import net.paoding.rose.RoseConstants;
import net.paoding.rose.scanner.ModuleResource;
import net.paoding.rose.util.RoseStringUtil;
import net.paoding.rose.util.SpringUtils;
import net.paoding.rose.web.ControllerErrorHandler;
import net.paoding.rose.web.ControllerInterceptor;
import net.paoding.rose.web.InterceptorDelegate;
import net.paoding.rose.web.OncePerRequestInterceptorDelegate;
import net.paoding.rose.web.ParamValidator;
import net.paoding.rose.web.advancedinterceptor.Ordered;
import net.paoding.rose.web.annotation.Ignored;
import net.paoding.rose.web.annotation.Interceptor;
import net.paoding.rose.web.annotation.NotForSubModules;
import net.paoding.rose.web.annotation.Path;
import net.paoding.rose.web.paramresolver.ParamResolver;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.CannotLoadBeanClassException;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.util.ClassUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.XmlWebApplicationContext;

/**
* 根据输入的module类信息,构造出具体的Module结构出来
*
* @author 王志亮 [qieqie.wang@gmail.com]
*/
public class ModulesBuilderImpl implements ModulesBuilder {

    private Log logger = LogFactory.getLog(getClass());

    public List<Module> build(List<ModuleResource> moduleResources,
            WebApplicationContext rootContext) throws Exception {

        // 重排序,使父级别的模块资源比子模块资源先处理
        moduleResources = new ArrayList<ModuleResource>(moduleResources);
        Collections.sort(moduleResources);

        // 将要返回的模块列表
        List<Module> modules = new ArrayList<Module>(moduleResources.size());
        Map<ModuleResource, Module> modulesAsMap = new HashMap<ModuleResource, Module>();

        //
        for (ModuleResource moduleResource : moduleResources) {
            final Module parentModule = (moduleResource.getParent() == null) ? null//
                    : modulesAsMap.get(moduleResource.getParent());
            final WebApplicationContext parentContext = (parentModule == null) ? rootContext//
                    : parentModule.getApplicationContext();
            final String namespace = "context@controllers"
                    + moduleResource.getRelativePath().replace('/', '.');

            // 创建该module的spring context对象
            final ServletContext servletContext = parentContext == null ? null //
                    : parentContext.getServletContext();
            final ModuleAppContext moduleContext = ModuleAppContext.createModuleContext(//
                    parentContext,//
                    moduleResource.getContextResources(),//
                    moduleResource.getMessageBasenames(),//
                    /*id*/moduleResource.getModuleUrl().toString(),//
                    namespace//
                    );

            // 扫描找到的类...定义到applicationContext
            registerBeanDefinitions(moduleContext, moduleResource.getModuleClasses());

            // 创建module对象
            final ModuleImpl module = new ModuleImpl(//
                    parentModule, //
                    moduleResource.getModuleUrl(), //
                    moduleResource.getMappingPath(), //
                    moduleResource.getRelativePath(), //
                    moduleContext);
            //
            modulesAsMap.put(moduleResource, module);

            // 设置到servletContext全局属性
            if (servletContext != null) {
                String contextAttrKey = WebApplicationContext.class.getName() + "@"
                        + moduleResource.getModuleUrl();
                servletContext.setAttribute(contextAttrKey, moduleContext);
            }

            // 从Spring应用环境中找出本web模块要使用的ParamValidator,ParamResolver, ControllerInterceptor, ControllerErrorHandler
            List<ParamResolver> customerResolvers = findContextResolvers(moduleContext);

            // resolvers
            module.setCustomerResolvers(customerResolvers);
            if (logger.isDebugEnabled()) {
                logger.debug("module '" + module.getMappingPath() + "': apply resolvers "
                        + customerResolvers);
            }

            // 将拦截器设置到module中
            List<InterceptorDelegate> interceptors = findInterceptors(moduleContext);
            for (Iterator<InterceptorDelegate> iter = interceptors.iterator(); iter.hasNext();) {
                InterceptorDelegate interceptor = iter.next();

                ControllerInterceptor most = InterceptorDelegate
                        .getMostInnerInterceptor(interceptor);

                if (!most.getClass().getName().startsWith("net.paoding.rose.web")) {

                    // 先排除deny禁止的
                    if (moduleResource.getInterceptedDeny() != null) {
                        if (RoseStringUtil.matches(moduleResource.getInterceptedDeny(), interceptor
                                .getName())) {
                            iter.remove();
                            if (logger.isDebugEnabled()) {
                                logger.debug("module '" + module.getMappingPath()
                                        + "': remove interceptor by rose.properties: "
                                        + most.getClass().getName());
                            }
                            continue;
                        }
                    }
                    //  确认最大的allow允许
                    if (moduleResource.getInterceptedAllow() != null) {
                        if (!RoseStringUtil.matches(moduleResource.getInterceptedAllow(),
                                interceptor.getName())) {
                            iter.remove();
                            if (logger.isDebugEnabled()) {
                                logger.debug("module '" + module.getMappingPath()
                                        + "': remove interceptor by rose.properties: "
                                        + most.getClass().getName());
                            }
                            continue;
                        }
                    }
                }
            }
            module.setControllerInterceptors(interceptors);
            if (logger.isDebugEnabled()) {
                logger.debug("module '" + module.getMappingPath() + "': apply intercetpors "
                        + interceptors);
            }

            // 将validator设置到module中
            List<ParamValidator> validators = findContextValidators(moduleContext);
            module.setValidators(validators);
            if (logger.isDebugEnabled()) {
                logger.debug("module '" + module.getMappingPath() + "': apply global validators "
                        + validators);
            }

            // errorhandler
            ControllerErrorHandler errorHandler = getContextErrorHandler(moduleContext);
            if (errorHandler != null) {
                if (Proxy.isProxyClass(errorHandler.getClass())) {
                    module.setErrorHandler(errorHandler);
                } else {
                    ErrorHandlerDispatcher dispatcher = new ErrorHandlerDispatcher(errorHandler);
                    module.setErrorHandler(dispatcher);
                }
                if (logger.isInfoEnabled()) {
                    logger.info("set errorHandler: " + module.getMappingPath() + "  "
                            + errorHandler);
                }
            }

            // controllers
            final ListableBeanFactory beanFactory = moduleContext.getBeanFactory();
            for (String beanName : beanFactory.getBeanDefinitionNames()) {
                checkController(moduleContext, beanName, module);
            }

            // 放进去以返回
            modules.add(module);
        }

        return modules;
    }

    private void throwExceptionIfDuplicatedNames(List<InterceptorDelegate> interceptors) {
        for (int i = 0; i < interceptors.size(); i++) {
            InterceptorDelegate interceptor = interceptors.get(i);
            for (int j = i + 1; j < interceptors.size(); j++) {
                // 先判断是否有"名字"一样的拦截器
                InterceptorDelegate position = interceptors.get(j);
                if (position.getName().equals(interceptor.getName())) {
                    // rose内部要求interceptor要有一个唯一的标识
                    // 请这两个类的提供者商量改类名,不能同时取一样的类名
                    // 如果是通过@Component等设置名字的,则不要设置一样
                    ControllerInterceptor duplicated1 = InterceptorDelegate
                            .getMostInnerInterceptor(position);
                    ControllerInterceptor duplicated2 = InterceptorDelegate
                            .getMostInnerInterceptor(interceptor);

                    throw new IllegalArgumentException(
                            "duplicated interceptor name for these two interceptors: '"
                                    + duplicated1.getClass() + "' and '" + duplicated2.getClass()
                                    + "'");
                }
            }
        }
    }

    private boolean checkController(final XmlWebApplicationContext context, String beanName,
            ModuleImpl module) throws IllegalAccessException {
        AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition) context.getBeanFactory()
                .getBeanDefinition(beanName);
        String beanClassName = beanDefinition.getBeanClassName();
        String controllerSuffix = null;
        for (String suffix : RoseConstants.CONTROLLER_SUFFIXES) {
            if (beanClassName.length() > suffix.length() && beanClassName.endsWith(suffix)) {
                if (suffix.length() == 1
                        && Character.isUpperCase(beanClassName.charAt(beanClassName.length()
                                - suffix.length() - 1))) {
                    continue;
                }
                controllerSuffix = suffix;
                break;
            }
        }
        if (controllerSuffix == null) {
            if (beanDefinition.hasBeanClass()) {
                Class<?> beanClass = beanDefinition.getBeanClass();
                if (beanClass.isAnnotationPresent(Path.class)) {
                    throw new IllegalArgumentException("@" + Path.class.getSimpleName()
                            + " is only allowed in Resource/Controller, "
                            + "is it a Resource/Controller? wrong spelling? : " + beanClassName);
                }
            }
            // 对少字母l、r、u,er写成or的等常见错误进行提醒
            if (beanClassName.endsWith("Controler") || beanClassName.endsWith("Controllor")
                    || beanClassName.endsWith("Resouce") || beanClassName.endsWith("Resorce")) {
                // 记录错误,但不throw出,不阻断其他程序的正常运行
                logger.error("", new IllegalArgumentException(
                        "invalid class name end, wrong spelling? : " + beanClassName));
            }
            return false;
        }
        String[] controllerPaths = null;
        if (!beanDefinition.hasBeanClass()) {
            try {
                beanDefinition.resolveBeanClass(Thread.currentThread().getContextClassLoader());
            } catch (ClassNotFoundException e) {
                throw new CannotLoadBeanClassException("", beanName, beanDefinition
                        .getBeanClassName(), e);
            }
        }
        final Class<?> clazz = beanDefinition.getBeanClass();
        final String controllerName = StringUtils.removeEnd(ClassUtils
                .getShortNameAsProperty(clazz), controllerSuffix);
        Path reqMappingAnnotation = clazz.getAnnotation(Path.class);
        if (reqMappingAnnotation != null) {
            controllerPaths = reqMappingAnnotation.value();
        }
        if (controllerPaths != null) {
            // 如果controllerPaths.length==0,表示没有任何path可以映射到这个controller了
            for (int i = 0; i < controllerPaths.length; i++) {
                if ("#".equals(controllerPaths[i])) {
                    controllerPaths[i] = "/" + controllerName;
                } else if (controllerPaths[i].equals("/")) {
                    controllerPaths[i] = "";
                } else if (controllerPaths[i].length() > 0 && controllerPaths[i].charAt(0) != '/') {
                    controllerPaths[i] = "/" + controllerPaths[i];
                }
                if (controllerPaths[i].length() > 1 && controllerPaths[i].endsWith("/")) {
                    if (controllerPaths[i].endsWith("//")) {
                        throw new IllegalArgumentException("invalid path '" + controllerPaths[i]
                                + "' for controller " + beanClassName
                                + ": don't end with more than one '/'");
                    }
                    controllerPaths[i] = controllerPaths[i].substring(0, controllerPaths[i]
                            .length() - 1);
                }
            }
        } else {
            // TODO: 这个代码是为了使从0.9到1.0比较顺畅而做的判断,201007之后可以考虑删除掉
            if (controllerName.equals("index") || controllerName.equals("home")
                    || controllerName.equals("welcome")) {
                // 这个异常的意思是让大家在IndexController/HomeController/WelcomeController上明确标注@Path("")
                throw new IllegalArgumentException("please add @Path(\"\") to " + clazz.getName());
            } else {
                controllerPaths = new String[] { "/" + controllerName };
            }
        }
        // 这个Controller是否已经在Context中配置了?
        // 如果使用Context配置,就不需要在这里实例化
        Object controller = context.getBean(beanName);
        module.addController(//
                controllerPaths, clazz, controllerName, controller);
        if (Proxy.isProxyClass(controller.getClass())) {
            if (logger.isDebugEnabled()) {
                logger.debug("module '" + module.getMappingPath() + "': add controller "
                        + Arrays.toString(controllerPaths) + "= proxy of " + clazz.getName());
            }
        } else {
            if (logger.isDebugEnabled()) {
                logger.debug("module '"
                        + module.getMappingPath() //
                        + "': add controller " + Arrays.toString(controllerPaths) + "= "
                        + controller.getClass().getName());
            }
        }
        return true;
    }

    private static final String AUTO_BEAN_NAME_PREFIX = "ModuleBuilder.";

    private void registerBeanDefinitions(XmlWebApplicationContext context, List<Class<?>> classes) {
        DefaultListableBeanFactory bf = (DefaultListableBeanFactory) context.getBeanFactory();
        String[] definedClasses = new String[bf.getBeanDefinitionCount()];
        String[] definitionNames = bf.getBeanDefinitionNames();
        for (int i = 0; i < definedClasses.length; i++) {
            String name = definitionNames[i];
            definedClasses[i] = bf.getBeanDefinition(name).getBeanClassName();
        }
        for (Class<?> clazz : classes) {
            // 排除非规范的类
            if (!isCandidate(clazz)) {
                continue;
            }

            // 排除手动定义的bean
            String clazzName = clazz.getName();
            if (ArrayUtils.contains(definedClasses, clazzName)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Ignores bean definition because it has been exist in context: "
                            + clazz.getName());
                }
                continue;
            }
            //
            String beanName = null;
            if (StringUtils.isEmpty(beanName) && clazz.isAnnotationPresent(Component.class)) {
                beanName = clazz.getAnnotation(Component.class).value();
            }
            if (StringUtils.isEmpty(beanName) && clazz.isAnnotationPresent(Resource.class)) {
                beanName = clazz.getAnnotation(Resource.class).name();
            }
            if (StringUtils.isEmpty(beanName) && clazz.isAnnotationPresent(Service.class)) {
                beanName = clazz.getAnnotation(Service.class).value();
            }
            if (StringUtils.isEmpty(beanName)) {
                beanName = AUTO_BEAN_NAME_PREFIX + clazz.getName();
            }

            bf.registerBeanDefinition(beanName, new AnnotatedGenericBeanDefinition(clazz));
        }
    }

    private boolean isCandidate(Class<?> clazz) {
        if (clazz.isAnnotationPresent(Ignored.class)) {
            if (logger.isDebugEnabled()) {
                logger.debug("Ignores bean definition because it's present by @Ignored : "
                        + clazz.getName());
            }
            return false;
        }
        if (!Modifier.isPublic(clazz.getModifiers())) {
            if (logger.isDebugEnabled()) {
                logger.debug("Ignores bean definition because it's not a public class: "
                        + clazz.getName());
            }
            return false;
        }
        if (Modifier.isAbstract(clazz.getModifiers())) {
            if (logger.isDebugEnabled()) {
                logger.debug("Ignores bean definition because it's a abstract class: "
                        + clazz.getName());
            }
            return false;
        }
        if (clazz.getDeclaringClass() != null) {
            if (logger.isDebugEnabled()) {
                logger.debug("Ignores bean definition because it's a inner class: "
                        + clazz.getName());
            }
            return false;
        }
        return true;
    }

    /** 错误处理器,只从本容器找,不从父容器找 */
    private ControllerErrorHandler getContextErrorHandler(XmlWebApplicationContext context) {
        ControllerErrorHandler errorHandler = null;
        String[] names = context.getBeanNamesForType(ControllerErrorHandler.class);
        for (int i = 0; errorHandler == null && i < names.length; i++) {
            errorHandler = (ControllerErrorHandler) context.getBean(names[i]);
            Class<?> userClass = ClassUtils.getUserClass(errorHandler);
            if (userClass.isAnnotationPresent(Ignored.class)) {
                logger.debug("Ignored controllerErrorHandler: " + errorHandler);
                errorHandler = null;
                continue;
            }
        }
        return errorHandler;
    }

    private List<ParamResolver> findContextResolvers(XmlWebApplicationContext context) {
        String[] resolverNames = SpringUtils.getBeanNames(context.getBeanFactory(),
                ParamResolver.class);
        ArrayList<ParamResolver> resolvers = new ArrayList<ParamResolver>(resolverNames.length);
        for (String beanName : resolverNames) {
            ParamResolver resolver = (ParamResolver) context.getBean(beanName);
            Class<?> userClass = ClassUtils.getUserClass(resolver);
            if (userClass.isAnnotationPresent(Ignored.class)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Ignored context resolver:" + resolver);
                }
                continue;
            }
            if (userClass.isAnnotationPresent(NotForSubModules.class)
                    && context.getBeanFactory().getBeanDefinition(beanName) == null) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Ignored context resolver (NotForSubModules):" + resolver);
                }
                continue;
            }
            resolvers.add(resolver);
            if (logger.isDebugEnabled()) {
                logger.debug("context resolver[" + resolver.getClass().getName());
            }
        }
        return resolvers;
    }

    private List<InterceptorDelegate> findInterceptors(XmlWebApplicationContext context) {
        String[] interceptorNames = SpringUtils.getBeanNames(context.getBeanFactory(),
                ControllerInterceptor.class);
        ArrayList<InterceptorDelegate> interceptors = new ArrayList<InterceptorDelegate>(
                interceptorNames.length);
        for (String beanName : interceptorNames) {
            ControllerInterceptor interceptor = (ControllerInterceptor) context.getBean(beanName);
            Class<?> userClass = ClassUtils.getUserClass(interceptor);
            if (userClass.isAnnotationPresent(Ignored.class)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Ignored interceptor (Ignored):" + interceptor);
                }
                continue;
            }
            if (userClass.isAnnotationPresent(NotForSubModules.class)
                    && !context.getBeanFactory().containsBeanDefinition(beanName)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Ignored interceptor (NotForSubModules):" + interceptor);
                }
                continue;
            }
            if (!userClass.getSimpleName().endsWith(RoseConstants.INTERCEPTOR_SUFFIX)) {
                logger.error("", new IllegalArgumentException("Interceptor must be end with '"
                        + RoseConstants.INTERCEPTOR_SUFFIX + "': " + userClass.getName()));
                continue;
            }
            InterceptorBuilder builder = new InterceptorBuilder(interceptor);
            Interceptor annotation = userClass.getAnnotation(Interceptor.class);
            if (annotation != null) {
                builder.oncePerRequest(annotation.oncePerRequest());
            }
            String interceporName;
            if (beanName.startsWith(AUTO_BEAN_NAME_PREFIX)) {
                interceporName = StringUtils.removeEnd(StringUtils.uncapitalize(userClass
                        .getSimpleName()), RoseConstants.INTERCEPTOR_SUFFIX);
            } else {
                interceporName = StringUtils.removeEnd(beanName, RoseConstants.INTERCEPTOR_SUFFIX);
            }
            final String rose = "rose";
            if (interceporName.startsWith(rose)
                    && (interceporName.length() == rose.length() || Character
                            .isUpperCase(interceporName.charAt(rose.length())))
                    && !userClass.getName().startsWith("net.paoding.rose.")) {
                throw new IllegalArgumentException("illegal interceptor name '" + interceporName
                        + "' for " + userClass.getName()
                        + ": don't starts with 'rose', it's reserved");
            }

            builder.name(interceporName);

            InterceptorDelegate wrapper = builder.build();
            interceptors.add(wrapper);
            if (logger.isDebugEnabled()) {
                int priority = 0;
                if (interceptor instanceof Ordered) {
                    priority = ((Ordered) interceptor).getPriority();
                }
                logger.debug("recognized interceptor[priority=" + priority + "]: " // \r\n
                        + wrapper.getName() + "=" + userClass.getName());
            }
        }
        Collections.sort(interceptors);
        throwExceptionIfDuplicatedNames(interceptors);
        return interceptors;
    }

    private List<ParamValidator> findContextValidators(XmlWebApplicationContext context) {
        String[] validatorNames = SpringUtils.getBeanNames(context.getBeanFactory(),
                ParamValidator.class);
        ArrayList<ParamValidator> globalValidators = new ArrayList<ParamValidator>(
                validatorNames.length);
        for (String beanName : validatorNames) {
            ParamValidator validator = (ParamValidator) context.getBean(beanName);
            Class<?> userClass = ClassUtils.getUserClass(validator);
            if (userClass.isAnnotationPresent(Ignored.class)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Ignored context validator:" + validator);
                }
                continue;
            }
            if (userClass.isAnnotationPresent(NotForSubModules.class)
                    && context.getBeanFactory().getBeanDefinition(beanName) == null) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Ignored context validator (NotForSubModules):" + validator);
                }
                continue;
            }
            globalValidators.add(validator);
            if (logger.isDebugEnabled()) {
                logger.debug("add context validator: " + userClass.getName());
            }
        }
        return globalValidators;
    }

    public static class InterceptorBuilder {

        private boolean oncePerRequest;

        private String name;

        private ControllerInterceptor interceptor;

        public InterceptorBuilder(ControllerInterceptor interceptor) {
            this.interceptor = interceptor;
        }

        public InterceptorBuilder name(String name) {
            this.name = name;
            return this;
        }

        public InterceptorBuilder oncePerRequest(boolean oncePerRequest) {
            this.oncePerRequest = oncePerRequest;
            return this;
        }

        public InterceptorDelegate build() {
            ControllerInterceptor interceptor = this.interceptor;
            if (oncePerRequest) {
                interceptor = new OncePerRequestInterceptorDelegate(interceptor);
            }
            InterceptorDelegate wrapper = new InterceptorDelegate(interceptor);
            if (StringUtils.isBlank(wrapper.getName())) {
                wrapper.setName(name);
            }
            return wrapper;
        }
    }
}
TOP

Related Classes of net.paoding.rose.web.impl.module.ModulesBuilderImpl$InterceptorBuilder

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.