Package com.alibaba.citrus.springext.impl

Source Code of com.alibaba.citrus.springext.impl.ConfigurationPointImpl

/*
* 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.springext.impl;

import static com.alibaba.citrus.springext.Schema.*;
import static com.alibaba.citrus.springext.support.SchemaUtil.*;
import static com.alibaba.citrus.util.Assert.*;
import static com.alibaba.citrus.util.CollectionUtil.*;
import static com.alibaba.citrus.util.ObjectUtil.*;
import static com.alibaba.citrus.util.StringUtil.*;
import static java.util.Collections.*;

import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;

import com.alibaba.citrus.springext.ConfigurationPoint;
import com.alibaba.citrus.springext.ConfigurationPointException;
import com.alibaba.citrus.springext.ConfigurationPoints;
import com.alibaba.citrus.springext.Contribution;
import com.alibaba.citrus.springext.ContributionAware;
import com.alibaba.citrus.springext.ContributionType;
import com.alibaba.citrus.springext.ResourceResolver.PropertyHandler;
import com.alibaba.citrus.springext.ResourceResolver.Resource;
import com.alibaba.citrus.springext.Schema;
import com.alibaba.citrus.springext.SourceInfo;
import com.alibaba.citrus.springext.VersionableSchemas;
import com.alibaba.citrus.springext.support.ConfigurationPointSourceInfo;
import com.alibaba.citrus.springext.support.SourceInfoSupport;
import com.alibaba.citrus.springext.support.parser.DefaultElementDefinitionParser;
import com.alibaba.citrus.util.ToStringBuilder;
import com.alibaba.citrus.util.ToStringBuilder.MapBuilder;
import org.dom4j.Document;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.FatalBeanException;
import org.springframework.beans.factory.xml.BeanDefinitionDecorator;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.NamespaceHandler;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
import org.springframework.util.ClassUtils;

/**
* 代表一个configuration point的实现,并处理configuration point
* namespace下的elements及attributes。
*
* @author Michael Zhou
*/
public class ConfigurationPointImpl extends NamespaceHandlerSupport
        implements ConfigurationPoint, NamespaceHandler, ConfigurationPointSourceInfo {
    private final static Logger log = LoggerFactory.getLogger(ConfigurationPoint.class);
    private final ConfigurationPoints                cps;
    private final ConfigurationPointSettings         settings;
    private final String                             name;
    private final String                             namespaceUri;
    private final String                             defaultElementName;
    private final String                             preferredNsPrefix;
    private final String                             contributionLocationPrefix;
    private final Map<ContributionKey, Contribution> contributions;
    private final Map<String, Contribution>          dependingContributions;
    private final Collection<Contribution>           dependingContributionsUnmodifiable;
    private final SourceInfo<SourceInfo<?>>          sourceInfo;
    private       VersionableSchemas                 schemas;
    private       boolean                            initialized;

    ConfigurationPointImpl(ConfigurationPoints cps, ConfigurationPointSettings settings, String name,
                           String namespaceUri, String defaultElementName, String preferredNsPrefix, SourceInfo<SourceInfo<?>> sourceInfo) {
        this.cps = cps;
        this.settings = settings;
        this.name = assertNotNull(name, "name");
        this.namespaceUri = assertNotNull(namespaceUri, "namespaceUri");
        this.defaultElementName = trimToNull(defaultElementName);
        this.preferredNsPrefix = trimToNull(preferredNsPrefix);
        this.contributionLocationPrefix = settings.baseLocation + name.replace('/', '-'); // eg. my-conf-point
        this.contributions = createTreeMap();
        this.dependingContributions = createTreeMap(); // 使用排序的map,使测试结果恒定
        this.dependingContributionsUnmodifiable = unmodifiableCollection(dependingContributions.values());
        this.sourceInfo = assertNotNull(sourceInfo, "sourceInfo");
    }

    public ConfigurationPoints getConfigurationPoints() {
        return cps;
    }

    public String getName() {
        return name;
    }

    public String getNamespaceUri() {
        return namespaceUri;
    }

    public String getDefaultElementName() {
        return defaultElementName;
    }

    public String getPreferredNsPrefix() {
        return preferredNsPrefix;
    }

    public NamespaceHandler getNamespaceHandler() {
        return this;
    }

    public Contribution getContribution(String name, ContributionType type) {
        return contributions.get(new ContributionKey(name, type));
    }

    public Collection<Contribution> getContributions() {
        return contributions.values();
    }

    @Override
    public Collection<Contribution> getDependingContributions() {
        return dependingContributionsUnmodifiable;
    }

    public void addDependingContribution(Contribution contribution) {
        if (contribution != null) {
            String key = contribution.getConfigurationPoint().getName() + "." + contribution.getName();
            dependingContributions.put(key, contribution);
        }
    }

    public void init() {
        if (initialized) {
            return;
        }

        initialized = true;

        for (ContributionType type : ContributionType.values()) {
            loadContributions(type);
        }

        // 注册default element parser
        String defaultName = getDefaultElementName();

        if (defaultName != null) {
            registerBeanDefinitionParser(defaultName, new DefaultElementDefinitionParser());
        }
    }

    private void loadContributions(final ContributionType contribType) {
        final String contribLocation = contributionLocationPrefix + contribType.getContributionsLocationSuffix();

        log.trace("Trying to load contributions at {}", contribLocation);

        final Map<String, String> sortedMappings = createTreeMap();

        settings.resourceResolver.loadAllProperties(contribLocation, new PropertyHandler() {
            public void handle(String key, String value, Resource source, int lineNumber) {
                String contribName = trimToNull(key);
                String contribClassName = trimToNull(value);

                if (getDefaultElementName() != null && isEquals(contribName, getDefaultElementName())) {
                    throw new FatalBeanException(
                            "Contribution has a same name as the default element name for configuration point: contributionType="
                            + contribType + ", contribuitionClass=" + contribClassName + ", contributionName="
                            + contribName + ", configurationPoint=" + getName() + ", namespaceUri="
                            + getNamespaceUri());
                }

                sortedMappings.put(contribName, contribClassName);

                ContributionImpl contrib = new ContributionImpl(
                        ConfigurationPointImpl.this, settings, contribType, contribName, contribClassName,
                        new SourceInfoSupport<ConfigurationPointSourceInfo>(ConfigurationPointImpl.this).setSource(source, lineNumber));

                Contribution existContrib = contributions.get(contrib.getKey());

                if (existContrib != null) {
                    throw new ConfigurationPointException("Duplicated contributions from locations: " + contribLocation
                                                          + "\n" + "     " + existContrib + "\n and " + contrib);
                }

                register(contrib);

                contributions.put(contrib.getKey(), contrib);
            }
        });

        if (log.isDebugEnabled() && !sortedMappings.isEmpty()) {
            ToStringBuilder buf = new ToStringBuilder();

            buf.format("Loaded contributions at %s", contribLocation);
            buf.appendMap(sortedMappings);

            log.debug(buf.toString());
        }
    }

    private void register(Contribution contrib) {
        // 当classLoader为null(IDE plugin)时,不创建和注册contribution class。
        if (settings.classLoader == null) {
            return;
        }

        Object obj;

        try {
            obj = instantiateContributionImplementation(contrib);
        } catch (FatalBeanException e) {
            log.warn("Skipped registration of {} due to the error: {}", contrib.getDescription(), e);
            return;
        }

        if (obj instanceof ContributionAware) {
            ((ContributionAware) obj).setContribution(contrib);
        }

        switch (contrib.getType()) {
            case BEAN_DEFINITION_PARSER:
                registerBeanDefinitionParser(contrib.getName(), (BeanDefinitionParser) obj);
                break;

            case BEAN_DEFINITION_DECORATOR:
                registerBeanDefinitionDecorator(contrib.getName(), (BeanDefinitionDecorator) obj);
                break;

            case BEAN_DEFINITION_DECORATOR_FOR_ATTRIBUTE:
                registerBeanDefinitionDecoratorForAttribute(contrib.getName(), (BeanDefinitionDecorator) obj);
                break;

            default:
                unreachableCode("unknown contributionType: %s", contrib.getType());
        }
    }

    private Object instantiateContributionImplementation(Contribution contrib)
            throws FatalBeanException {
        String implementationClassName = contrib.getImplementationClassName();

        if (implementationClassName == null) {
            throw new FatalBeanException("Contribution class not defined: contributionType=" + contrib.getType()
                                         + ", contributionName=" + contrib.getName() + ", configurationPoint="
                                         + contrib.getConfigurationPoint().getName() + ", namespaceUri="
                                         + contrib.getConfigurationPoint().getNamespaceUri());
        }

        Class<?> implementationClass;

        try {
            implementationClass = ClassUtils.forName(implementationClassName, settings.classLoader);
        } catch (ClassNotFoundException e) {
            throw new FatalBeanException("Contribution class not found: contributionType=" + contrib.getType()
                                         + ", contribuitionClass=" + implementationClassName + ", contributionName=" + contrib.getName()
                                         + ", configurationPoint=" + contrib.getConfigurationPoint().getName() + ", namespaceUri="
                                         + contrib.getConfigurationPoint().getNamespaceUri(), e);
        }

        if (!contrib.getType().getContributionInterface().isAssignableFrom(implementationClass)) {
            throw new FatalBeanException("Contribution class does not implement the "
                                         + contrib.getType().getContributionInterface().getSimpleName() + " interface:  contributionType="
                                         + contrib.getType() + ", contribuitionClass=" + implementationClassName + ", contributionName="
                                         + contrib.getName() + ", configurationPoint=" + contrib.getConfigurationPoint().getName()
                                         + ", namespaceUri=" + contrib.getConfigurationPoint().getNamespaceUri());
        }

        return BeanUtils.instantiateClass(implementationClass);
    }

    public VersionableSchemas getSchemas() {
        if (schemas == null) {
            init(); // if not inited yet

            // eg. my-conf-point
            String mainName = getName().replace('/', '-');

            Schema mainSchema = loadMainSchema(mainName);
            Schema[] versionedSchemas = loadVersionedSchemas(mainName);

            schemas = new VersionableSchemasImpl(mainSchema, versionedSchemas);
        }

        return schemas;
    }

    private Schema loadMainSchema(String mainName) {
        String schemaName = mainName + "." + XML_SCHEMA_EXTENSION;
        Document schemaSource = createConfigurationPointSchema(this, null);

        return SchemaImpl.createForConfigurationPoint(
                schemaName, null, namespaceUri, preferredNsPrefix, getDescription(), schemaSource,
                new SourceInfoSupport<ConfigurationPointSourceInfo>(this));
    }

    private Schema[] loadVersionedSchemas(String mainName) {
        // 收集所有contribution schema的版本
        Set<String> allVersions = createTreeSet();

        for (Contribution contrib : getContributions()) {
            Collections.addAll(allVersions, contrib.getSchemas().getVersions());
        }

        // 生成每个schemas。
        Schema[] schemas = new Schema[allVersions.size()];
        int i = 0;
        for (String version : allVersions) {
            String schemaName = mainName + "-" + version + "." + XML_SCHEMA_EXTENSION;
            Document schemaSource = createConfigurationPointSchema(this, version);

            schemas[i++] = SchemaImpl.createForConfigurationPoint(
                    schemaName, version, namespaceUri, preferredNsPrefix, getDescription(), schemaSource,
                    new SourceInfoSupport<ConfigurationPointSourceInfo>(this));
        }

        return schemas;
    }

    public String getDescription() {
        return String.format("ConfigurationPoint[%s]", name);
    }

    public SourceInfo<?> getParent() {
        return sourceInfo.getParent();
    }

    public Resource getSource() {
        return sourceInfo.getSource();
    }

    public int getLineNumber() {
        return sourceInfo.getLineNumber();
    }

    @Override
    public String toString() {
        ToStringBuilder buf = new ToStringBuilder();

        buf.format("ConfigurationPoint[%s=%s, loaded contributions from %s.*]", name, namespaceUri,
                   contributionLocationPrefix);

        MapBuilder mb = new MapBuilder();

        if (!contributions.isEmpty()) {
            mb.append("Contributions", contributions.values());
        }

        mb.append("Schemas", getSchemas()).appendTo(buf);

        return buf.toString();
    }
}
TOP

Related Classes of com.alibaba.citrus.springext.impl.ConfigurationPointImpl

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.