package guang.crawler.localConfig;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
/**
* 从组件配置xml中加载相关信息和类.该过程类似于web.xml中定义servlet的过程.
*
* @author yang
*
*/
public class ComponentLoader<T> {
/**
* 默认组件的名称
*/
private static final String DEFAULT_COMPONENT_NAME = "default";
/**
* 配置文件
*/
private File configFile;
/**
* 是否已经加载过了.只允许加载一次,不能重复加载
*/
private boolean loaded = false;
/**
* 组件名称和类名的映射
*/
private HashMap<String, String> nameToClass = new HashMap<String, String>();
/**
* url与组件名称的映射
*/
private HashMap<String, String> urlToName = new HashMap<String, String>();
/**
* 组件名称和组件实例的映射
*/
private HashMap<String, T> nameToObj = new HashMap<String, T>();
/**
* XML元素的命名空間
*/
private static final String NS = "http://guang.org/distributedCrawler/components";
private File schemaFile;
/**
* 创建一个组件加载器
*
* @param configFile
* 组件的配置文件
* @param schemaFile
* XSD模板文件
*/
public ComponentLoader(final File configFile, final File schemaFile) {
this.configFile = configFile;
}
/**
* 根据URL的值,找到匹配该URL的第一个组件.
*
* @param url
* @return
*/
public T getComponent(final String url) {
if (url == null) {
return null;
}
Iterator<String> patterns = this.urlToName.keySet()
.iterator();
String name = null;
while (patterns.hasNext()) {
String pattern = patterns.next();
if (url.matches(pattern)) {
name = this.urlToName.get(pattern);
break;
}
}
if (name == null) {
name = ComponentLoader.DEFAULT_COMPONENT_NAME;
}
T component = this.nameToObj.get(name);
if (component == null) {
String className = this.nameToClass.get(name);
if (className == null) {
return null;
}
try {
@SuppressWarnings("unchecked")
T newInstance = (T) Class.forName(className)
.newInstance();
component = newInstance;
} catch (Exception e) {
return null;
}
}
return component;
}
/**
* 从配置文件中加载组件
*
* @throws SAXException
* @throws IOException
* @throws ParserConfigurationException
* @throws InstantiationException
* @throws IllegalAccessException
* @throws ClassNotFoundException
*/
public void load() throws SAXException, IOException,
ParserConfigurationException, InstantiationException,
IllegalAccessException, ClassNotFoundException {
if (this.loaded) {
return;
}
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setIgnoringComments(true);
factory.setNamespaceAware(true);
if (this.schemaFile != null) {
Schema schema = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema")
.newSchema(this.schemaFile);
factory.setSchema(schema);
}
Document config = factory.newDocumentBuilder()
.parse(this.configFile);
this.loadComponentDefines(config);
this.loadComponentMappings(config);
this.loaded = true;
}
/**
* 加载配置文件中组件定义部分
*
* @param config
* @throws InstantiationException
* @throws IllegalAccessException
* @throws ClassNotFoundException
*/
private void loadComponentDefines(final Document config)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
NodeList nodelist = config.getElementsByTagNameNS(ComponentLoader.NS,
"component-defines");
int size = nodelist.getLength();
if (size == 1) {
Element componentDefines = (Element) nodelist.item(0);
nodelist = componentDefines.getElementsByTagNameNS(ComponentLoader.NS,
"component");
size = nodelist.getLength();
if (size > 0) {
for (int i = 0; i < size; i++) {
Element componentElement = (Element) nodelist.item(i);
NodeList nodes = componentElement.getElementsByTagNameNS(ComponentLoader.NS,
"component-name");
if (nodes.getLength() == 0) {
continue;
}
String name = nodes.item(0)
.getTextContent()
.trim();
nodes = componentElement.getElementsByTagNameNS(ComponentLoader.NS,
"component-class");
if (nodes.getLength() == 0) {
continue;
}
String className = nodes.item(0)
.getTextContent()
.trim();
nodes = componentElement.getElementsByTagNameNS(ComponentLoader.NS,
"load-on-startup");
boolean loadOnStartup = false;
if (nodes.getLength() != 0) {
loadOnStartup = Boolean.parseBoolean(nodes.item(0)
.getTextContent()
.trim());
}
this.nameToClass.put(name, className);
if (loadOnStartup) {
@SuppressWarnings("unchecked")
T component = (T) Class.forName(className)
.newInstance();
this.nameToObj.put(name, component);
}
}
}
}
}
/**
* 加载组件配置文件中组件映射部分
*
* @param config
* @throws InstantiationException
* @throws IllegalAccessException
* @throws ClassNotFoundException
*/
private void loadComponentMappings(final Document config)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
NodeList nodeList = config.getElementsByTagNameNS(ComponentLoader.NS,
"component-mappings");
int size = nodeList.getLength();
if (size == 1) {
Element componentMappings = (Element) nodeList.item(0);
nodeList = componentMappings.getElementsByTagNameNS(ComponentLoader.NS,
"component-mapping");
size = nodeList.getLength();
}
if (size > 0) {
for (int i = 0; i < size; i++) {
Element componentMapping = (Element) nodeList.item(i);
NodeList nodes = componentMapping.getElementsByTagNameNS(ComponentLoader.NS,
"component-name");
if (nodes.getLength() == 0) {
continue;
}
String name = nodes.item(0)
.getTextContent()
.trim();
nodes = componentMapping.getElementsByTagNameNS(ComponentLoader.NS,
"url-pattern");
int length = nodes.getLength();
if (length == 0) {
continue;
}
for (int j = 0; j < length; j++) {
String pattern = nodes.item(j)
.getTextContent()
.trim();
this.urlToName.put(pattern, name);
}
}
}
}
}