Package mfinder.config

Source Code of mfinder.config.Configuration

/*
* Copyright (C) 2010-2011 sunjumper@163.com
*
* 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 mfinder.config;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.DocumentBuilderFactory;
import mfinder.ActionFactory;
import mfinder.impl.DefaultActionFactory;
import mfinder.impl.Injector;
import mfinder.impl.Injector.Injection;
import mfinder.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

/**
* 启动mfinder容器的入口配置类。通过Configuration类加载mfinder的配置文件(默认为mfinder.xml)初始化ActionFactory
* 及加载相应的属性配置,最终得到ActionFactory具体实例。
* 如果mfinder.xml中未指明ActionFactory的具体实现类,则默认使用{@link DefaultActionFactory }
* 通常如下使用:
* <code><blockquote><pre>
* Configuration config = new Configuration().load();
* ActionFactory factory = config.getFactory();
* factory...
* </pre></blockquote></code>
*
* @see #getFactory()
*/
public class Configuration {

    /* 日志记录 */
    private static final Logger LOG = LoggerFactory.getLogger(Configuration.class);

////////////////////////////////////////////////////////////////////////////////
//                           xml配置文件元素                                   //
////////////////////////////////////////////////////////////////////////////////
    /** 默认配置文件的名称 */
    public static final String DEFAULT_FILE = "mfinder-1.5.xml";

    /** 配置文件中表示ActionFactory的标签名 */
    public static final String ACTION_FACTORY = "action-factory";

    /** 配置文件中表示属性的标签属性 */
    public static final String PROPERTY = "property";

    /** 配置文件中表示常量的标签名,适配1.4版本 */
    @Deprecated
    public static final String CONSTANT = "constant";

    /** 配置文件中表示类名称的标签属性 */
    public static final String CLASS = "class";

    /** 配置文件中表示值的标签属性 */
    public static final String VALUE = "value";

    /** 配置文件中表示名称的标签属性 */
    public static final String NAME = "name";

    /** 配置文件中表示拦截器的标签名 */
    public static final String INTERCEPTOR = "interceptor";

    /** 配置文件中表示拦截栈的标签名 */
    public static final String INTERCEPTOR_STACK = "interceptor-stack";

    /** 配置文件中表示结果类型的标签名 */
    public static final String RESULT_TYPE = "result-type";

    /** 配置文件中表示结果对象的标签名 */
    public static final String RESULT = "result";

    /** 配置文件中表示全局结果对象集合的标签名 */
    @Deprecated
    public static final String DEFAULT_RESULT = "default-result";

    /** 配置文件中表示Action的标签名 */
    public static final String ACTION = "action";

    /** 配置文件中表示path的标签名 */
    public static final String PATH = "path";

    /** 配置文件中表示包含其它配置的标签名 */
    public static final String INCLUDE = "include";

    /** 配置文件中表示文件名称的标签属性 */
    public static final String FILE = "file";

    /** factory */
    private ActionFactory<?> factory;

    /** constant key and value */
    @Deprecated
    private Map<String, String> variables = new HashMap<String, String>();

    /**
     * 加载默认配置文件{@link #DEFAULT_FILE}
     *
     * @return 此配置对象的引用。
     *
     * @throws ConfigurationException 如果发生配置错误。
     */
    public Configuration load() throws ConfigurationException {
        try {
            load(getResourceAsStream(DEFAULT_FILE));
            LOG.info("Configuration initiate " + Configuration.this + " from /" + DEFAULT_FILE);
        } catch (ConfigurationException e) {
            LOG.error("Could not load mfinder settings from /" + DEFAULT_FILE);
            throw e;
        }
        return this;
    }

    /**
     * 从指定资源获取流数据。
     *
     * @param name 资源文件名。
     *
     * @return 流数据。
     */
    private static InputStream getResourceAsStream(String name) {
        return Configuration.class.getClassLoader().getResourceAsStream(name);
    }

    /**
     * 打印分隔符。
     *
     * @param bool 是否打印。
     */
    private static void printSeparator(boolean bool) {
        if (bool)
            System.out.println("--------------------------------------------------------------------------------");
    }

    /**
     * 加载配置。
     *
     * @param stream InputStream。
     *
     * @return 此配置对象的引用。
     *
     * @throws ConfigurationException 如果发生配置错误。
     */
    public Configuration load(InputStream stream) throws ConfigurationException {
        try {
            Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(stream);
            //root node : <mfinder>
            Element root = doc.getDocumentElement();

            List<Element> list = null;
            int length = 0;

            //ActionFactory
            list = getChildNodesByTagName(root, ACTION_FACTORY);
            if ((length = list.size()) == 1) {
                Element e = list.get(0);
                String cls = e.getAttribute(CLASS);
                //create ActionFactory
                factory = StringUtil.isNull(cls)
                        ? new DefaultActionFactory()
                        : (ActionFactory) Class.forName(cls).newInstance();

                //action factory's properties
                list = getChildNodesByTagName(e, PROPERTY);
                //inject factory's properties
                setProperties(factory, list);
            } else if (length > 1) {
                throw new ConfigurationException("More than one <" + ACTION_FACTORY + "> tag in setting.", null);
            }

            //adapt for 1.0
            /*
            if (factory == null) {
            //constant
            list = doc.getElementsByTagName(CONSTANT);
            length = list.getLength();
            if (length > 0) {
            LOG.warn("Deprecated tag <" + CONSTANT + ">, use <" + ACTION_FACTORY + "> instead.");
            } else {
            //factory = new DefaultActionFactory();
            for (int i = 0; i < length; i++) {
            e = (Element) list.item(i);
            String name = e.getAttribute(NAME);
            //加载常量
            String val = e.getAttribute(VALUE);
            if (StringUtil.isNotNull(val)) {
            variables.put(name, val);
            } else {
            String cls = e.getAttribute(CLASS);
            if (StringUtil.isNotNull(cls)) {
            variables.put(name, cls);
            }
            }
            }
            String prop = variables.get("ActionFactory");
            //create ActionFactory
            factory = StringUtil.isNull(prop)
            ? new DefaultActionFactory()
            : (ActionFactory) Class.forName(prop).newInstance();

            //set properties for ActionFactory
            prop = variables.get("DefaultInterceptorStack");
            if (StringUtil.isNotNull(prop))
            factory.setDefaultInterceptorStack(prop);
            prop = variables.get("DefaultResultType");
            if (StringUtil.isNotNull(prop))
            factory.setDefaultResultType(prop);
            }
            }
             */
            //if no ActionFactory setting
            if (factory == null)
                factory = new DefaultActionFactory();
            //give subclasses a chance to prepare factory
            afterActionFactoryCreation(factory);

            printSeparator(!list.isEmpty());

            addActionFactoryProperties(root);

            //include
            list = getChildNodesByTagName(root, INCLUDE);
            length = list.size();
            Collection coll = new HashSet<String>();
            for (Element e : list) {
                //add included files, use a hash set to avoid circular reference
                addIncludedProperties(e.getAttribute(FILE), coll);
            }
        } catch (ConfigurationException e) {
            throw e;
        } catch (Exception e) {
            throw new ConfigurationException("Could not load or parse properties from input stream", e);
        }
        return this;
    }

    /**
     * 添加包含的配置文件, 用一个集合类判断并避免循环引用。
     *
     * @param name 配置文件名称。
     * @param coll 指定的集合。
     */
    private void addIncludedProperties(String name, Collection<String> coll) {
        LOG.info("Load included file : " + name);
        printSeparator(true);

        InputStream stream = getResourceAsStream(name);
        if (stream == null) {
            throw new IllegalArgumentException("Can't load input stream from included file : " + name);
        }
        //if circular reference
        if (coll.contains(name)) {
            throw new IllegalArgumentException("Load circular reference file : " + name);
        }
        coll.add(name);

        try {
            Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(stream);
            Element root = doc.getDocumentElement();

            //add properties
            addActionFactoryProperties(root);

            //add include
            List<Element> list = getChildNodesByTagName(root, INCLUDE);
            for (Element e : list) {
                addIncludedProperties(e.getAttribute(FILE), coll);
            }

        } catch (Exception e) {
            throw new ConfigurationException("Could not load or parse properties from included file : " + name, e);
        } finally {
            if (stream != null)
                try {
                    stream.close();
                } catch (IOException e) {
                    LOG.error("Fail to close input stream " + name, e);
                }
        }
    }

    /**
     * 依次添加interceptor、interceptorStack、result-type、result、action。
     *
     * @param root 文档根节点。
     *
     * @throws ClassNotFoundException 如果没有找到具有指定名称的类。
     * @throws IllegalAccessException 如果底层方法不可访问。
     * @throws InstantiationException 如果实例化失败。
     */
    private void addActionFactoryProperties(Element root) throws ClassNotFoundException,
            IllegalAccessException, IntrospectionException, InvocationTargetException {
        if (factory instanceof DefaultActionFactory) {
            DefaultActionFactory defaultFactory = (DefaultActionFactory) this.factory;
            List<Element> list = null;
            //interceptor
            list = getChildNodesByTagName(root, INTERCEPTOR);
            for (Element e : list) {
                //System.out.println(e.getAttribute(CLASS));
                Object obj = factory.getObjectFactory().newInstance(Class.forName(e.getAttribute(CLASS)));
                //set property nodes
                setProperties(obj, getChildNodesByTagName(e, PROPERTY));
                //add interceptor
                defaultFactory.addInterceptors(obj);
            }

            printSeparator(!list.isEmpty());

            //interceptor-stack
            list = getChildNodesByTagName(root, INTERCEPTOR_STACK);
            for (Element e : list) {
                //System.out.println(INTERCEPTOR_STACK + " : " + e.getAttribute(CLASS));
                //add interceptor stacks
                defaultFactory.addInterceptorStacks(factory.getObjectFactory().newInstance(Class.forName(e.getAttribute(CLASS))));
            }

            printSeparator(!list.isEmpty());

            //result-type
            list = getChildNodesByTagName(root, RESULT_TYPE);
            for (Element e : list) {
                Object obj = factory.getObjectFactory().newInstance(Class.forName(e.getAttribute(CLASS)));
                //set property nodes
                setProperties(obj, getChildNodesByTagName(e, PROPERTY));
                //add result types
                defaultFactory.addResultTypes(obj);
            }

            printSeparator(!list.isEmpty());

            //result
            list = getChildNodesByTagName(root, RESULT);
            for (Element e : list) {
                //add results
                Object obj = factory.getObjectFactory().newInstance(Class.forName(e.getAttribute(CLASS)));
                //set property nodes
                setProperties(obj, getChildNodesByTagName(e, PROPERTY));
                defaultFactory.addResults(obj);
            }

            printSeparator(!list.isEmpty());

            //action
            list = getChildNodesByTagName(root, ACTION);
            for (Element action : list) {
                //System.out.println(e.getAttribute(CLASS));
                Object obj = factory.getObjectFactory().newInstance(Class.forName(action.getAttribute(CLASS)));

                //action <property> nodes
                List<Element> propnodes = getChildNodesByTagName(action, PROPERTY);

                //common class properties
                Map<String, PropertyDescriptor> supports = Injector.getSupportedProperties(obj.getClass());

                //待注入相应class的Action属性列表
                Map<String, Injection> classInjections = getInjections(propnodes, obj, supports);

                //properties for action class
                if (classInjections.size() > 0) {
                    Injector.putClassProperties(obj.getClass(), classInjections.values().toArray(new Injection[classInjections.size()]));
                    //Action对象属性注入
                    Injector.injectObject(obj);
                }

                //<path> nodes
                List<Element> pathnodes = getChildNodesByTagName(action, PATH);
                for (Element path : pathnodes) {
                    //path <property> nodes
                    propnodes = getChildNodesByTagName(path, PROPERTY);
                    //待注入相应path的Action属性列表
                    Map<String, Injection> pathInjections = getInjections(propnodes, obj, supports);
                    if (pathInjections.size() > 0) {
                        //属性包含class注入属性和path注入属性
                        Map<String, Injection> allInjections = new LinkedHashMap<String, Injection>(classInjections.size() + pathInjections.size());
                        allInjections.putAll(classInjections);
                        allInjections.putAll(pathInjections);
                        //System.out.println(path.getAttribute(NAME) + "," + allInjections);
                        Injector.putActionProperties(path.getAttribute(NAME), allInjections.values().toArray(new Injection[allInjections.size()]));
                    }
                }
                //add actions
                defaultFactory.addActions(obj);
            }
            printSeparator(!list.isEmpty());
        }
    }

    /**
     * 由标签<property>集合获取指定对象所支持注入属性的映射集合。
     *
     * @param propnodes 标签<property>集合。
     * @param obj 指定对象。
     * @param supports 指定所支持的属性集。
     *
     * @return 指定对象所支持注入属性的映射集合。
     */
    private static Map<String, Injection> getInjections(List<Element> propnodes, Object obj,
            Map<String, PropertyDescriptor> supports) {
        String cls = obj.getClass().getName();
        Map<String, Injection> injections = new LinkedHashMap<String, Injection>(propnodes.size());
        for (Element prop : propnodes) {
            String pName = prop.getAttribute(NAME);
            PropertyDescriptor pd = supports.get(pName);
            if (pd == null) {
                LOG.error("Not supported property [{}] in [{}]", pName, cls);
            } else {
                Object value = Injector.stringToObject(prop.getAttribute(VALUE), pd.getPropertyType());
                if (value == null) {
                    LOG.warn("Not supported property [{}] for type [{}] in " + cls, pName, pd.getPropertyType());
                } else {
                    if (null != injections.put(pName, new Injection(pd.getWriteMethod(), value))) {
                        LOG.info("Override property [{}] value [{}] in " + cls, pName, prop.getAttribute(VALUE));
                    }
                }
            }
        }
        return injections;
    }

    /**
     * 注入指定对象的属性。
     *
     * @param obj 指定的对象。
     * @param propnodes 属性名称和字符串值的节点列表。
     *
     * @throws IntrospectionException 如果在内省期间发生异常。
     * @throws IllegalAccessException 如果底层方法不可访问。
     * @throws InvocationTargetException 如果底层方法抛出异常。
     */
    private static void setProperties(Object obj, List<Element> propnodes) throws
            IntrospectionException, IllegalAccessException, InvocationTargetException {
        String cls = obj.getClass().getName();
        Map<String, PropertyDescriptor> supports = Injector.getSupportedProperties(obj.getClass());
        Map<String, Object> injections = new LinkedHashMap<String, Object>(propnodes.size());
        for (Element prop : propnodes) {
            String pName = prop.getAttribute(NAME);
            PropertyDescriptor pd = supports.get(pName);
            if (pd == null) {
                LOG.error("Not supported property [{}] in [{}]", pName, cls);
            } else {
                Object value = Injector.stringToObject(prop.getAttribute(VALUE), pd.getPropertyType());
                if (null != injections.put(pName, value)) {
                    LOG.info("Override property [{}] value [{}] in " + cls, pName, prop.getAttribute(VALUE));
                }
            }
        }
        //属性注入
        for (Map.Entry<String, Object> e : injections.entrySet()) {
            supports.get(e.getKey()).getWriteMethod().invoke(obj, e.getValue());
        }
        supports.clear();
    }

    /**
     * 获取指定父节点和节点名称的子节点集合。
     *
     * @param parent 指定的父节点。
     * @param name 指定的节点名称
     *
     * @return 子节点集合。
     */
    private static List<Element> getChildNodesByTagName(Element parent, String name) {
        List<Element> eles = new ArrayList<Element>();
        for (Node child = parent.getFirstChild(); child != null; child = child.getNextSibling()) {
            //System.out.println(child.getNodeName() + "," + child.getNodeType());
            if (Node.ELEMENT_NODE == child.getNodeType() && name.equals(child.getNodeName())) {
                eles.add((Element) child);
            }
        }
        return eles;
    }
////////////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * 返回ActionFactory。
     *
     * @param <T> Action工厂对象的类型。
     *
     * @return ActionFactory。
     */
    public <T extends ActionFactory<?>> T getFactory() {
        return (T) factory;
    }

    /**
     * 用于子类继承, 在初始化ActionFactory前执行设置其一些特定的操作。
     * 默认情况下不做任何处理。
     *
     * @param factory 未初始化的{@link ActionFactory}
     */
    protected void afterActionFactoryCreation(ActionFactory<?> factory) {
    }

    /**
     * @deprecated 返回常量的名称和值。
     *
     * @return 常量的名称和值。
     */
    public Map<String, String> getVariables() {
        return variables;
    }
    //test
//    public static void main(String[] args) {
//        Configuration config = new Configuration();
//        config.load();
//    }
}
TOP

Related Classes of mfinder.config.Configuration

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.