Package com.alibaba.citrus.service.pull.impl

Source Code of com.alibaba.citrus.service.pull.impl.PullServiceImpl$ToolName

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

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.StringUtil.*;
import static java.util.Collections.*;
import static org.springframework.web.context.request.RequestAttributes.*;

import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

import com.alibaba.citrus.service.AbstractService;
import com.alibaba.citrus.service.pull.PullContext;
import com.alibaba.citrus.service.pull.PullException;
import com.alibaba.citrus.service.pull.PullService;
import com.alibaba.citrus.service.pull.RuntimeToolSetFactory;
import com.alibaba.citrus.service.pull.ToolFactory;
import com.alibaba.citrus.service.pull.ToolNameAware;
import com.alibaba.citrus.service.pull.ToolSetFactory;
import com.alibaba.citrus.util.ToStringBuilder;
import com.alibaba.citrus.util.ToStringBuilder.CollectionBuilder;
import com.alibaba.citrus.util.ToStringBuilder.MapBuilder;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

public class PullServiceImpl extends AbstractService<PullService> implements PullService, ApplicationContextAware {
    private final static String DEFAULT_BEAN_NAME = "pullService";

    private final static int SINGLETON                = 0x1;
    private final static int TOOL_FACTORY             = 0x2;
    private final static int TOOL_SET_FACTORY         = 0x4;
    private final static int RUNTIME_TOOL_SET_FACTORY = 0x8;

    private final static AtomicInteger contextKeyCounter = new AtomicInteger();
    private ApplicationContext beanFactory;
    private PullService        parent;
    private String             contextKey;

    // toolName -> factory,包含所有类型的factories,初始化后即清除。
    // 可能包含:
    // 1. ToolFactory           - non-singleton or singleton
    // 2. ToolSetFactory        - non-singleton or singleton
    // 3. RuntimeToolSetFactory - non-singleton
    private Map<String, Object> toolFactories;

    // toolName -> toolFactory,non-singleton tool factories
    private Map<String, ToolFactory> tools;

    // toolName -> toolSetFactory,non-singleton tool set factories
    private Map<String, ToolSetInfo<ToolSetFactory>> toolsInSet;

    // toolName -> runtimeToolSetFactory
    private Map<String, RuntimeToolSetFactory> toolsRuntime;

    // 在初始化时,预先被pull的tools,有两种来源:
    // 1. singleton toolFactory
    // 2. singleton toolSetFactory
    private Map<String, Object> prePulledTools;

    // 所有非runtime tools的名称,包括:
    // 1. singleton or non-singleton tools
    // 1. singleton or non-singleton tools in set
    private Set<ToolName> toolNames;

    public void setApplicationContext(ApplicationContext factory) {
        this.beanFactory = factory;
    }

    public void setParent(PullService parent) {
        this.parent = parent;
    }

    public void setToolFactories(Map<String, Object> factories) {
        this.toolFactories = factories;
    }

    @Override
    protected void init() {
        initParent();
        initContextKey();
        initToolFactories();

        getLogger().info(
                "Initialized pull service [key={}] "
                + "with {} pre-pulled tools, {} pre-queued tools and {} runtime tools", new Object[] { //
                                                                                                       contextKey, prePulledTools.size(), tools.size() + toolsInSet.size(), toolsRuntime.size() });
    }

    /** 初始化parent pull service。 */
    private void initParent() {
        if (parent != null || beanFactory == null || beanFactory.getParent() == null) {
            return;
        }

        // 取得parent pull service,依次尝试:
        // 1. 在配置文件中明确设置parentRef
        // 2. parent context中同名的对象
        // 3. parent context中默认名称的对象
        String parentBeanName = null;

        if (beanFactory.getParent().containsBean(getBeanName())) {
            parentBeanName = getBeanName();
        } else if (beanFactory.getParent().containsBean(DEFAULT_BEAN_NAME)) {
            parentBeanName = DEFAULT_BEAN_NAME;
        }

        if (parentBeanName != null) {
            parent = (PullService) beanFactory.getParent().getBean(parentBeanName);
        }
    }

    /** 创建一个在整个JVM中不重复的contextKey。 */
    private void initContextKey() {
        int i = contextKeyCounter.getAndIncrement();
        contextKey = "PullService." + getBeanName() + (i > 0 ? "." + i : EMPTY_STRING);
    }

    /** 初始化所有tool factories。 */
    private void initToolFactories() {
        tools = createHashMap();
        toolsInSet = createHashMap();
        toolsRuntime = createHashMap();
        prePulledTools = createHashMap();
        toolNames = createHashSet();

        if (toolFactories != null) {
            for (Map.Entry<String, Object> e : toolFactories.entrySet()) {
                String name = assertNotNull(trimToNull(e.getKey()), "tool name");
                Object factory = e.getValue();

                if (factory instanceof ToolNameAware) {
                    ((ToolNameAware) factory).setToolName(name);
                }

                int type = getFactoryType(factory);

                if (testBit(type, SINGLETON)) {
                    // 将singleton tool预先取值
                    if (testBit(type, TOOL_FACTORY)) {
                        Object tool;

                        try {
                            tool = encode(((ToolFactory) factory).createTool());
                        } catch (Exception ex) {
                            throw new PullException("Could not create tool: \"" + name + "\"", ex);
                        }

                        ToolName toolName = new ToolName(null, name, false);

                        toolNames.add(toolName);
                        prePulledTools.put(name, tool);

                        if (getLogger().isDebugEnabled()) {
                            getLogger().debug("Pre-pulled tool: {} = {}", toolName, tool);
                        }
                    }

                    // 将singleton tool set预先取得每一个值
                    if (testBit(type, TOOL_SET_FACTORY)) {
                        Iterable<String> names = ((ToolSetFactory) factory).getToolNames();

                        if (names != null) {
                            for (String nameInSet : names) {
                                nameInSet = trimToNull(nameInSet);

                                if (nameInSet != null) {
                                    Object tool;

                                    try {
                                        tool = encode(((ToolSetFactory) factory).createTool(nameInSet));
                                    } catch (Exception ex) {
                                        throw new PullException("Could not create tool: \"" + name + "." + nameInSet
                                                                + "\"", ex);
                                    }

                                    ToolName toolName = new ToolName(name, nameInSet, false);

                                    toolNames.add(toolName);
                                    prePulledTools.put(nameInSet, tool);

                                    if (getLogger().isDebugEnabled()) {
                                        getLogger().debug("Pre-pulled tool: {} = {}", toolName, tool);
                                    }
                                }
                            }
                        }
                    }
                } else {
                    // 将non-singleton tool,预先取得其名称。
                    if (testBit(type, TOOL_FACTORY)) {
                        ToolName toolName = new ToolName(null, name, false);

                        toolNames.add(toolName);
                        tools.put(name, (ToolFactory) factory);

                        getLogger().debug("Pre-queued tool: {}", toolName);
                    }

                    // 将non-singleton tool set,预先取得每一个名称。
                    if (testBit(type, TOOL_SET_FACTORY)) {
                        Iterable<String> names = ((ToolSetFactory) factory).getToolNames();

                        if (names != null) {
                            for (String nameInSet : names) {
                                nameInSet = trimToNull(nameInSet);

                                if (nameInSet != null) {
                                    ToolName toolName = new ToolName(name, nameInSet, false);

                                    toolNames.add(toolName);
                                    toolsInSet.put(nameInSet, new ToolSetInfo<ToolSetFactory>(name,
                                                                                              (ToolSetFactory) factory, null));

                                    getLogger().debug("Pre-queued tool: {}", toolName);
                                }
                            }
                        }
                    }

                    // 保存runtime tool
                    if (testBit(type, RUNTIME_TOOL_SET_FACTORY)) {
                        toolsRuntime.put(name, (RuntimeToolSetFactory) factory);
                    }
                }
            }
        }

        toolFactories = null;
    }

    public PullContext getContext() {
        RequestAttributes attrs = null;

        // 从request中取得context,假如当前不是在web环境中,则创建一个新的context,
        // 从而确保在非web环境中也可以使用pull service。
        try {
            attrs = RequestContextHolder.currentRequestAttributes();
        } catch (IllegalStateException e) {
            getLogger().debug("Getting pull context in non-WEB environment: {}", e.getMessage());
        }

        PullContext context;

        if (attrs == null) {
            context = new PullContextImpl();
        } else {
            context = (PullContext) attrs.getAttribute(contextKey, SCOPE_REQUEST);

            if (context == null) {
                context = new PullContextImpl();
                attrs.setAttribute(contextKey, context, SCOPE_REQUEST);
            }
        }

        return context;
    }

    public Map<String, Object> getTools() {
        return getContext().getTools();
    }

    private static int getFactoryType(Object factory) {
        int type = 0;

        if (factory instanceof RuntimeToolSetFactory) {
            type |= RUNTIME_TOOL_SET_FACTORY;

            if (factory instanceof ToolFactory) {
                type |= TOOL_FACTORY;
            }

            return type;
        }

        if (factory instanceof ToolFactory) {
            type |= TOOL_FACTORY;

            if (((ToolFactory) factory).isSingleton()) {
                type |= SINGLETON;
            }
        }

        if (factory instanceof ToolSetFactory) {
            type |= TOOL_SET_FACTORY;

            if (((ToolSetFactory) factory).isSingleton()) {
                type |= SINGLETON;
            }
        }

        assertTrue(type != 0, "unknown pull tool factory type: %s", factory == null ? null : factory.getClass()
                                                                                                    .getName());

        return type;
    }

    private static boolean testBit(int type, int mask) {
        return (type & mask) != 0;
    }

    private static Object encode(Object value) {
        return value == null ? NULL_PLACEHOLDER : value;
    }

    private static Object decode(Object value) {
        return value == NULL_PLACEHOLDER ? null : value;
    }

    @Override
    public String toString() {
        if (isInitialized()) {
            ToStringBuilder sb = new ToStringBuilder().append("PullTools").append(
                    new CollectionBuilder().appendAll(toolNames).setPrintCount(true).setSort(true));

            if (parent != null) {
                sb.append("Parent ").append(parent);
            }

            return sb.toString();
        } else {
            return "PullTools[uninitialized]";
        }
    }

    static class ToolSetInfo<F> {
        private final String toolSetName;
        private final F      factory;
        private final Object tool;

        public ToolSetInfo(String toolSetName, F factory, Object tool) {
            this.toolSetName = toolSetName;
            this.factory = factory;
            this.tool = tool;
        }

        public String getToolSetName() {
            return toolSetName;
        }

        public F getFactory() {
            return factory;
        }

        public Object getTool() {
            return tool;
        }
    }

    private class PullContextImpl implements PullContext {
        private final PullContext                                     parentContext;
        private final Map<String, Object>                             pulledTools;
        private final Map<String, RuntimeToolSetFactory>              toolsRuntime;
        private final Map<String, ToolSetInfo<RuntimeToolSetFactory>> toolsInRuntimeSet;
        private final Set<ToolName>                                   toolNames;
        private       Set<String>                                     toolNamesIncludingParent;
        private       Map<String, Object>                             toolsIncludingParent;

        private PullContextImpl() {
            if (parent == null) {
                parentContext = null;
            } else {
                parentContext = parent.getContext();
            }

            pulledTools = createHashMap();
            toolsRuntime = createTreeMap(); // 排序以保证单元测试的确定性
            toolsInRuntimeSet = createHashMap();
            toolNames = createHashSet();

            // copy runtime tools
            toolsRuntime.putAll(PullServiceImpl.this.toolsRuntime);

            // copy tool names and sort
            toolNames.addAll(PullServiceImpl.this.toolNames);
        }

        public Object pull(String name) {
            name = trimToNull(name);

            if (name == null) {
                return null;
            }

            Object tool;

            // 如果name已经被pre-pulled,则直接返回
            tool = prePulledTools.get(name);

            if (tool == null) {
                // 检查本地缓存,如果name早已存在,则直接返回
                tool = pulledTools.get(name);

                if (tool == null) {
                    tool = doPulling(name); // encoded tool

                    if (tool != null) {
                        pulledTools.put(name, tool);
                    }
                }
            }

            // 如果有parent context,则试着从parent中取得
            if (tool == null && parentContext != null) {
                return parentContext.pull(name);
            } else {
                return decode(tool);
            }
        }

        private Object doPulling(String name) {
            // 如果存在于tools中,则pull之。
            ToolFactory toolFactory = tools.get(name);

            if (toolFactory != null) {
                Object tool;

                try {
                    tool = toolFactory.createTool();
                } catch (Exception ex) {
                    throw new PullException("Could not create tool: \"" + name + "\"", ex);
                }

                if (getLogger().isDebugEnabled()) {
                    getLogger().debug("Pulled tool: {} = {}", name, tool);
                }

                return encode(tool);
            }

            // 如果存在于toolsInSet中,则pull之。
            ToolSetInfo<ToolSetFactory> toolSetInfo = toolsInSet.get(name);

            if (toolSetInfo != null) {
                Object tool;

                try {
                    tool = toolSetInfo.getFactory().createTool(name);
                } catch (Exception ex) {
                    throw new PullException("Could not create tool: \"" + toolSetInfo.getToolSetName() + "." + name
                                            + "\"", ex);
                }

                if (getLogger().isDebugEnabled()) {
                    getLogger().debug("Pulled tool: {}.{} = {}",
                                      new Object[] { toolSetInfo.getToolSetName(), name, tool });
                }

                return encode(tool);
            }

            // 如果存在于toolsInRuntimeSet中,则pull之。
            pullToolsRuntime(name);

            ToolSetInfo<RuntimeToolSetFactory> runtimeToolSetInfo = toolsInRuntimeSet.get(name);

            if (runtimeToolSetInfo != null) {
                Object tool;

                try {
                    tool = runtimeToolSetInfo.getFactory().createTool(runtimeToolSetInfo.getTool(), name);
                } catch (Exception ex) {
                    throw new PullException("Could not create tool: \"" + runtimeToolSetInfo.getToolSetName() + "."
                                            + name + "\"", ex);
                }

                if (getLogger().isDebugEnabled()) {
                    getLogger().debug("Pulled tool: {}.{} = {}",
                                      new Object[] { runtimeToolSetInfo.getToolSetName(), name, tool });
                }

                return encode(tool);
            }

            return null;
        }

        private void pullToolsRuntime(String stopOnName) {
            if (stopOnName != null && toolsInRuntimeSet.containsKey(stopOnName)) {
                return;
            }

            for (Iterator<Map.Entry<String, RuntimeToolSetFactory>> i = toolsRuntime.entrySet().iterator(); i.hasNext(); ) {
                Map.Entry<String, RuntimeToolSetFactory> entry = i.next();

                i.remove();
                String toolSetName = entry.getKey();
                RuntimeToolSetFactory factory = entry.getValue();

                int count = 0;
                Object tool;

                try {
                    tool = factory.createToolSet();
                } catch (Exception ex) {
                    throw new PullException("Could not create runtime tool-set: \"" + toolSetName + "\"", ex);
                }

                Iterable<String> names = factory.getToolNames(tool);

                if (names != null) {
                    for (String nameInSet : names) {
                        if (nameInSet != null) {
                            toolsInRuntimeSet.put(nameInSet, new ToolSetInfo<RuntimeToolSetFactory>(toolSetName,
                                                                                                    factory, tool));
                            toolNames.add(new ToolName(toolSetName, nameInSet, false));
                            count++;
                        }
                    }
                }

                getLogger().debug("Queued {} tools for runtime tool-set \"{}\"", count, toolSetName);

                // 假如已经找到了stopOnName,则立即返回
                if (stopOnName != null && toolsInRuntimeSet.containsKey(stopOnName)) {
                    break;
                }
            }
        }

        public Set<String> getQualifiedToolNames() {
            Set<String> names = createTreeSet();

            for (ToolName toolName : populateToolNames()) {
                names.add(toolName.getQualifiedName());
            }

            return names;
        }

        public Set<String> getToolNames() {
            if (toolNamesIncludingParent == null) {
                Set<String> names = createTreeSet();

                for (ToolName toolName : populateToolNames()) {
                    names.add(toolName.getName());
                }

                toolNamesIncludingParent = unmodifiableSet(names);
            }

            return toolNamesIncludingParent;
        }

        private Set<ToolName> populateToolNames() {
            Set<ToolName> toolNamesIncludingParent;

            pullToolsRuntime(null);

            if (parentContext == null) {
                toolNamesIncludingParent = toolNames;
            } else {
                toolNamesIncludingParent = createHashSet();

                for (String parentToolName : parentContext.getQualifiedToolNames()) {
                    toolNamesIncludingParent.add(new ToolName("_parent", parentToolName, true));
                }

                toolNamesIncludingParent.addAll(toolNames);
            }

            return toolNamesIncludingParent;
        }

        public Map<String, Object> getTools() {
            if (toolsIncludingParent == null) {
                for (String name : tools.keySet()) {
                    pull(name);
                }

                for (String name : toolsInSet.keySet()) {
                    pull(name);
                }

                pullToolsRuntime(null);

                for (String name : toolsInRuntimeSet.keySet()) {
                    pull(name);
                }

                toolsIncludingParent = createHashMap();

                if (parentContext != null) {
                    toolsIncludingParent.putAll(parentContext.getTools());
                }

                putAll(toolsIncludingParent, pulledTools);
                putAll(toolsIncludingParent, prePulledTools);

                toolsIncludingParent = unmodifiableMap(toolsIncludingParent);
            }

            return toolsIncludingParent;
        }

        private void putAll(Map<String, Object> tools, Map<String, Object> objects) {
            for (Map.Entry<String, Object> entry : objects.entrySet()) {
                tools.put(entry.getKey(), decode(entry.getValue()));
            }
        }

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

            mb.append("prePulledTools", new MapBuilder().appendAll(prePulledTools).setSortKeys(true)
                                                        .setPrintCount(true));
            mb.append("pulledTools", new MapBuilder().appendAll(pulledTools).setSortKeys(true).setPrintCount(true));

            ToStringBuilder sb = new ToStringBuilder().append("PullContext").append(mb);

            if (parentContext != null) {
                sb.append("Parent ").append(parentContext);
            }

            return sb.toString();
        }
    }

    static final class ToolName implements Comparable<ToolName> {
        private final String qname;
        private final String name;

        public ToolName(String namespace, String name, boolean parse) {
            namespace = trimToNull(namespace);
            name = assertNotNull(trimToNull(name), "tool name");

            if (parse) {
                name = trim(name, "/");
                int index = name.lastIndexOf("/");

                if (index >= 0) {
                    if (namespace == null) {
                        namespace = name.substring(0, index);
                    } else {
                        namespace = namespace + "/" + name.substring(0, index);
                    }

                    namespace = trim(namespace, "/");
                    name = name.substring(index + 1);
                }
            }

            this.qname = namespace == null ? "/" + name : "/" + namespace + "/" + name;
            this.name = name;
        }

        public String getQualifiedName() {
            return qname;
        }

        public String getName() {
            return name;
        }

        public int compareTo(ToolName o) {
            return qname.compareTo(o.qname);
        }

        @Override
        public int hashCode() {
            return 31 + qname.hashCode();
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj) {
                return true;
            }

            if (obj == null) {
                return false;
            }

            if (!(obj instanceof ToolName)) {
                return false;
            }

            return qname.equals(((ToolName) obj).qname);
        }

        @Override
        public String toString() {
            return qname;
        }
    }
}
TOP

Related Classes of com.alibaba.citrus.service.pull.impl.PullServiceImpl$ToolName

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.