Package com.alibaba.citrus.webx.context

Source Code of com.alibaba.citrus.webx.context.WebxComponentsLoader$WebxComponentsImpl$RootComponent

/*
* Copyright (c) 2002-2012 Alibaba Group Holding Limited.
* 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.alibaba.citrus.webx.context;

import static com.alibaba.citrus.springext.util.SpringExtUtil.*;
import static com.alibaba.citrus.util.Assert.*;
import static com.alibaba.citrus.util.BasicConstant.*;
import static com.alibaba.citrus.util.CollectionUtil.*;
import static com.alibaba.citrus.util.FileUtil.*;
import static com.alibaba.citrus.util.StringUtil.*;
import static com.alibaba.citrus.util.regex.PathNameWildcardCompiler.*;
import static com.alibaba.citrus.webx.WebxConstant.*;

import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.ServletContext;

import com.alibaba.citrus.springext.util.SpringExtUtil;
import com.alibaba.citrus.util.ToStringBuilder;
import com.alibaba.citrus.util.ToStringBuilder.MapBuilder;
import com.alibaba.citrus.webx.WebxComponent;
import com.alibaba.citrus.webx.WebxComponents;
import com.alibaba.citrus.webx.WebxController;
import com.alibaba.citrus.webx.WebxRootController;
import com.alibaba.citrus.webx.config.WebxConfiguration;
import com.alibaba.citrus.webx.config.WebxConfiguration.ComponentConfig;
import com.alibaba.citrus.webx.config.WebxConfiguration.ComponentsConfig;
import com.alibaba.citrus.webx.config.impl.WebxConfigurationImpl;
import com.alibaba.citrus.webx.config.impl.WebxConfigurationImpl.ComponentsConfigImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ApplicationContextException;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.SourceFilteringListener;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.core.Ordered;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.util.ClassUtils;
import org.springframework.web.context.ConfigurableWebApplicationContext;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.ServletContextResourcePatternResolver;

/**
* 用来装载webx components的装载器。
*
* @author Michael Zhou
*/
public class WebxComponentsLoader extends ContextLoader {
    private final static Logger log = LoggerFactory.getLogger(WebxComponentsLoader.class);
    private String                webxConfigurationName;
    private ServletContext        servletContext;
    private WebApplicationContext componentsContext;
    private WebxComponentsImpl    components;

    /** 取得context中<code>WebxConfiguration</code>的名称。 */
    public String getWebxConfigurationName() {
        return webxConfigurationName == null ? "webxConfiguration" : webxConfigurationName;
    }

    /** 设置context中<code>WebxConfiguration</code>的名称。 */
    public void setWebxConfigurationName(String webxConfigurationName) {
        this.webxConfigurationName = trimToNull(webxConfigurationName);
    }

    /** 取得在servlet context中保存component context的key。 */
    public String getComponentContextAttributeName(String componentName) {
        return COMPONENT_CONTEXT_PREFIX + componentName;
    }

    public ServletContext getServletContext() {
        return servletContext;
    }

    /** 取得components。 */
    public WebxComponents getWebxComponents() {
        return components;
    }

    @Override
    public WebApplicationContext initWebApplicationContext(ServletContext servletContext) throws IllegalStateException,
                                                                                                 BeansException {
        this.servletContext = servletContext;
        init();

        return super.initWebApplicationContext(servletContext);
    }

    protected void init() {
        setWebxConfigurationName(servletContext.getInitParameter("webxConfigurationName"));
    }

    @Override
    protected final Class<?> determineContextClass(ServletContext servletContext) throws ApplicationContextException {
        String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);

        if (contextClassName != null) {
            try {
                return ClassUtils.forName(contextClassName);
            } catch (ClassNotFoundException ex) {
                throw new ApplicationContextException("Failed to load custom context class [" + contextClassName + "]",
                                                      ex);
            }
        } else {
            return getDefaultContextClass();
        }
    }

    /**
     * 取得默认的components <code>WebApplicationContext</code>实现类。
     * <p>
     * 子类可以覆盖并修改此方法。
     * </p>
     */
    protected Class<? extends WebxComponentsContext> getDefaultContextClass() {
        return WebxComponentsContext.class;
    }

    /** 在componentsContext.refresh()之前被调用。 */
    @Override
    protected void customizeContext(ServletContext servletContext, ConfigurableWebApplicationContext componentsContext) {
        this.componentsContext = componentsContext;

        if (componentsContext instanceof WebxComponentsContext) {
            ((WebxComponentsContext) componentsContext).setLoader(this);
        }
    }

    /**
     * 在创建beanFactory之初被调用。
     *
     * @param webxComponentsContext
     */
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        // 由于初始化components依赖于webxConfiguration,而webxConfiguration可能需要由PropertyPlaceholderConfigurer来处理,
        // 此外,其它有一些BeanFactoryPostProcessors会用到components,
        // 因此components必须在PropertyPlaceholderConfigurer之后初始化,并在其它BeanFactoryPostProcessors之前初始化。
        //
        // 下面创建的WebxComponentsCreator辅助类就是用来确保这个初始化顺序:
        // 1. PriorityOrdered - PropertyPlaceholderConfigurer
        // 2. Ordered - WebxComponentsCreator
        // 3. 普通 - 其它BeanFactoryPostProcessors
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(WebxComponentsCreator.class);
        builder.addConstructorArgValue(this);
        BeanDefinition componentsCreator = builder.getBeanDefinition();
        componentsCreator.setAutowireCandidate(false);

        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
        String name = SpringExtUtil.generateBeanName(WebxComponentsCreator.class.getName(), registry);

        registry.registerBeanDefinition(name, componentsCreator);
    }

    public static class WebxComponentsCreator implements BeanFactoryPostProcessor, Ordered {
        private final WebxComponentsLoader loader;

        public WebxComponentsCreator(WebxComponentsLoader loader) {
            this.loader = assertNotNull(loader, "WebxComponentsLoader");
        }

        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
            if (loader.components == null) {
                WebxComponentsImpl components = loader.createComponents(loader.getParentConfiguration(), beanFactory);
                AbstractApplicationContext wcc = (AbstractApplicationContext) components.getParentApplicationContext();
                wcc.addApplicationListener(new SourceFilteringListener(wcc, components));
                loader.components = components;
            }
        }

        public int getOrder() {
            return Ordered.LOWEST_PRECEDENCE;
        }
    }

    /** 初始化所有components。 */
    public void finishRefresh() {
        components.getWebxRootController().onFinishedProcessContext();

        for (WebxComponent component : components) {
            logInBothServletAndLoggingSystem("Initializing Spring sub WebApplicationContext: " + component.getName());

            WebxComponentContext wcc = (WebxComponentContext) component.getApplicationContext();
            WebxController controller = component.getWebxController();

            wcc.refresh();
            controller.onFinishedProcessContext();
        }

        logInBothServletAndLoggingSystem("WebxComponents: initialization completed");
    }

    private void logInBothServletAndLoggingSystem(String msg) {
        servletContext.log(msg);
        log.info(msg);
    }

    /** 初始化components。 */
    private WebxComponentsImpl createComponents(WebxConfiguration parentConfiguration,
                                                ConfigurableListableBeanFactory beanFactory) {
        ComponentsConfig componentsConfig = getComponentsConfig(parentConfiguration);

        // 假如isAutoDiscoverComponents==true,试图自动发现components
        Map<String, String> componentNamesAndLocations = findComponents(componentsConfig, getServletContext());

        // 取得特别指定的components
        Map<String, ComponentConfig> specifiedComponents = componentsConfig.getComponents();

        // 实际要初始化的comonents,为上述两种来源的并集
        Set<String> componentNames = createTreeSet();

        componentNames.addAll(componentNamesAndLocations.keySet());
        componentNames.addAll(specifiedComponents.keySet());

        // 创建root controller
        WebxRootController rootController = componentsConfig.getRootController();

        if (rootController == null) {
            rootController = (WebxRootController) BeanUtils.instantiateClass(componentsConfig.getRootControllerClass());
        }

        // 创建并将components对象置入resolvable dependencies,以便注入到需要的bean中
        WebxComponentsImpl components = new WebxComponentsImpl(componentsContext,
                                                               componentsConfig.getDefaultComponent(), rootController, parentConfiguration);

        beanFactory.registerResolvableDependency(WebxComponents.class, components);

        // 初始化每个component
        for (String componentName : componentNames) {
            ComponentConfig componentConfig = specifiedComponents.get(componentName);

            String componentPath = null;
            WebxController controller = null;

            if (componentConfig != null) {
                componentPath = componentConfig.getPath();
                controller = componentConfig.getController();
            }

            if (controller == null) {
                controller = (WebxController) BeanUtils.instantiateClass(componentsConfig.getDefaultControllerClass());
            }

            WebxComponentImpl component = new WebxComponentImpl(components, componentName, componentPath,
                                                                componentName.equals(componentsConfig.getDefaultComponent()), controller,
                                                                getWebxConfigurationName());

            components.addComponent(component);

            prepareComponent(component, componentNamesAndLocations.get(componentName));
        }

        return components;
    }

    private void prepareComponent(WebxComponentImpl component, String componentLocation) {
        String componentName = component.getName();
        WebxComponentContext wcc = new WebxComponentContext(component);

        wcc.setServletContext(getServletContext());
        wcc.setNamespace(componentName);
        wcc.addApplicationListener(new SourceFilteringListener(wcc, component));

        if (componentLocation != null) {
            wcc.setConfigLocation(componentLocation);
        }

        component.setApplicationContext(wcc);

        // 将context保存在servletContext中
        String attrName = getComponentContextAttributeName(componentName);
        getServletContext().setAttribute(attrName, wcc);

        log.debug("Published WebApplicationContext of component {} as ServletContext attribute with name [{}]",
                  componentName, attrName);
    }

    /** 查找component名称。 */
    private Map<String, String> findComponents(ComponentsConfig componentsConfig, ServletContext servletContext) {
        String locationPattern = componentsConfig.getComponentConfigurationLocationPattern();
        String[] prefixAndPattern = checkComponentConfigurationLocationPattern(locationPattern);
        String prefix = prefixAndPattern[0];
        String pathPattern = prefixAndPattern[1];

        Map<String, String> componentNamesAndLocations = createTreeMap();

        if (componentsConfig.isAutoDiscoverComponents()) {
            try {
                ResourcePatternResolver resolver = new ServletContextResourcePatternResolver(servletContext);
                Resource[] componentConfigurations = resolver.getResources(locationPattern);
                Pattern pattern = compilePathName(pathPattern);

                if (componentConfigurations != null) {
                    for (Resource resource : componentConfigurations) {
                        String path = resource.getURL().getPath();
                        Matcher matcher = pattern.matcher(path);

                        assertTrue(matcher.find(), "unknown component configuration file: %s", path);
                        String componentName = trimToNull(matcher.group(1));

                        if (componentName != null) {
                            componentNamesAndLocations.put(componentName,
                                                           prefix + pathPattern.replace("*", componentName));
                        }
                    }
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

        return componentNamesAndLocations;
    }

    /**
     * 检查componentConfigurationLocationPattern是否符合以下要求:
     * <ol>
     * <li>非空。</li>
     * <li>包含且只包含一个<code>*</code></li>
     * <li>支持<code>classpath*:</code>前缀。</li>
     * </ol>
     * <p>
     * 返回数组:[前缀, 不包含<code>classpath*:</code>的路径]。
     * </p>
     */
    private String[] checkComponentConfigurationLocationPattern(String componentConfigurationLocationPattern) {
        if (componentConfigurationLocationPattern != null) {
            // 允许并剔除classpath*:前缀。
            boolean classpath = componentConfigurationLocationPattern.startsWith("classpath*:");
            String pathPattern = componentConfigurationLocationPattern;

            if (classpath) {
                pathPattern = componentConfigurationLocationPattern.substring("classpath*:".length()).trim();
            }

            // 检查路径。
            int index = pathPattern.indexOf("*");

            if (index >= 0) {
                index = pathPattern.indexOf("*", index + 1);

                if (index < 0) {
                    if (pathPattern.startsWith("/")) {
                        pathPattern = pathPattern.substring(1);
                    }

                    return new String[] { classpath ? "classpath:" : EMPTY_STRING, pathPattern };
                }
            }
        }

        throw new IllegalArgumentException("Invalid componentConfigurationLocationPattern: "
                                           + componentConfigurationLocationPattern);
    }

    /** 从parent configuration中取得components配置。 */
    private ComponentsConfig getComponentsConfig(WebxConfiguration parentConfiguration) {
        ComponentsConfig componentsConfig = assertNotNull(parentConfiguration, "parentConfiguration")
                .getComponentsConfig();

        if (componentsConfig == null) {
            // create default components configuration
            componentsConfig = new ComponentsConfigImpl();
        }

        return componentsConfig;
    }

    /** 从parent context中取得<code>WebxConfiguration</code>。 */
    private WebxConfiguration getParentConfiguration() {
        try {
            return (WebxConfiguration) componentsContext.getBean(getWebxConfigurationName());
        } catch (BeansException e) {
            // create default configuration
            WebxConfigurationImpl parentConfiguration = new WebxConfigurationImpl();
            parentConfiguration.setApplicationContext(componentsContext);

            try {
                parentConfiguration.afterPropertiesSet();
            } catch (RuntimeException ee) {
                throw ee;
            } catch (Exception ee) {
                throw new RuntimeException(ee);
            }

            return parentConfiguration;
        }
    }

    private static class WebxComponentsImpl implements WebxComponents, ApplicationListener {
        private final WebxConfiguration          parentConfiguration;
        private final WebApplicationContext      parentContext;
        private final Map<String, WebxComponent> components;
        private final RootComponent              rootComponent;
        private final String                     defaultComponentName;
        private final WebxRootController         rootController;

        public WebxComponentsImpl(WebApplicationContext parentContext, String defaultComponentName,
                                  WebxRootController rootController, WebxConfiguration parentConfiguration) {
            this.parentConfiguration = assertNotNull(parentConfiguration, "no parent webx-configuration");
            this.parentContext = parentContext;
            this.components = createHashMap();
            this.rootComponent = new RootComponent();
            this.defaultComponentName = defaultComponentName;
            this.rootController = assertNotNull(rootController, "no rootController");

            rootController.init(this);
        }

        public WebxConfiguration getParentWebxConfiguration() {
            return parentConfiguration;
        }

        private void addComponent(WebxComponent component) {
            components.put(component.getName(), component);
        }

        public WebxComponent getComponent(String componentName) {
            if (componentName == null) {
                return rootComponent;
            } else {
                return components.get(componentName);
            }
        }

        public String[] getComponentNames() {
            String[] names = components.keySet().toArray(new String[components.size()]);
            Arrays.sort(names);
            return names;
        }

        public WebxComponent getDefaultComponent() {
            return defaultComponentName == null ? null : components.get(defaultComponentName);
        }

        public Iterator<WebxComponent> iterator() {
            return components.values().iterator();
        }

        public WebxComponent findMatchedComponent(String path) {
            if (!path.startsWith("/")) {
                path = "/" + path;
            }

            WebxComponent defaultComponent = getDefaultComponent();
            WebxComponent matched = null;

            // 前缀匹配componentPath。
            for (WebxComponent component : this) {
                if (component == defaultComponent) {
                    continue;
                }

                String componentPath = component.getComponentPath();

                if (!path.startsWith(componentPath)) {
                    continue;
                }

                // path刚好等于componentPath,或者path以componentPath/为前缀
                if (path.length() == componentPath.length() || path.charAt(componentPath.length()) == '/') {
                    matched = component;
                    break;
                }
            }

            // fallback to default component
            if (matched == null) {
                matched = defaultComponent;
            }

            return matched;
        }

        public WebxRootController getWebxRootController() {
            return rootController;
        }

        public WebApplicationContext getParentApplicationContext() {
            return parentContext;
        }

        public void onApplicationEvent(ApplicationEvent event) {
            if (event instanceof ContextRefreshedEvent) {
                // autowire and init root controller
                autowireAndInitialize(rootController, getParentApplicationContext(),
                                      AbstractBeanDefinition.AUTOWIRE_AUTODETECT, "webxRootController");

                rootController.onRefreshContext();
            }
        }

        @Override
        public String toString() {
            MapBuilder mb = new MapBuilder();

            mb.append("parentContext", parentContext);
            mb.append("defaultComponentName", defaultComponentName);
            mb.append("components", components);
            mb.append("rootController", rootController);

            return new ToStringBuilder().append("WebxComponents").append(mb).toString();
        }

        /** 这是一个特殊的component实现,对应于root context。 */
        private class RootComponent implements WebxComponent {
            public WebxComponents getWebxComponents() {
                return WebxComponentsImpl.this;
            }

            public String getName() {
                return null;
            }

            public String getComponentPath() {
                return EMPTY_STRING;
            }

            public WebxConfiguration getWebxConfiguration() {
                return getParentWebxConfiguration();
            }

            public WebxController getWebxController() {
                unsupportedOperation("RootComponent.getWebxController()");
                return null;
            }

            public WebApplicationContext getApplicationContext() {
                return getParentApplicationContext();
            }

            @Override
            public String toString() {
                return WebxComponentsImpl.this.toString();
            }
        }
    }

    private static class WebxComponentImpl implements WebxComponent, ApplicationListener {
        private final WebxComponents        components;
        private final String                name;
        private final String                componentPath;
        private final WebxController        controller;
        private final String                webxConfigurationName;
        private       WebApplicationContext context;

        public WebxComponentImpl(WebxComponents components, String name, String path, boolean defaultComponent,
                                 WebxController controller, String webxConfigurationName) {
            this.components = assertNotNull(components, "components");
            this.name = assertNotNull(name, "componentName");
            this.controller = assertNotNull(controller, "controller");
            this.webxConfigurationName = assertNotNull(webxConfigurationName, "webxConfigurationName");

            // 规格化path,去除尾部的/;空路径则设为null
            path = trimToNull(normalizeAbsolutePath(path, true));

            if (defaultComponent) {
                assertTrue(path == null, "default component \"%s\" should not have component path \"%s\"", name, path);
                this.componentPath = EMPTY_STRING;
            } else if (path != null) {
                this.componentPath = path;
            } else {
                this.componentPath = "/" + name;
            }

            controller.init(this);
        }

        public WebxComponents getWebxComponents() {
            return components;
        }

        public String getName() {
            return name;
        }

        public String getComponentPath() {
            return componentPath;
        }

        public WebxController getWebxController() {
            return controller;
        }

        public WebxConfiguration getWebxConfiguration() {
            return (WebxConfiguration) context.getBean(webxConfigurationName);
        }

        public WebApplicationContext getApplicationContext() {
            return context;
        }

        private void setApplicationContext(WebApplicationContext context) {
            this.context = context;
        }

        public void onApplicationEvent(ApplicationEvent event) {
            if (event instanceof ContextRefreshedEvent) {
                // autowire and init controller
                autowireAndInitialize(controller, getApplicationContext(), AbstractBeanDefinition.AUTOWIRE_AUTODETECT,
                                      "webxController." + getName());

                controller.onRefreshContext();
            }
        }

        @Override
        public String toString() {
            MapBuilder mb = new MapBuilder();

            mb.append("name", name);
            mb.append("path", componentPath);
            mb.append("controller", controller);
            mb.append("context", context);

            return new ToStringBuilder().append("WebxComponent").append(mb).toString();
        }
    }
}
TOP

Related Classes of com.alibaba.citrus.webx.context.WebxComponentsLoader$WebxComponentsImpl$RootComponent

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.