/*
* 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();
// }
}