package com.taobao.tddl.executor.common;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.lang.StringUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import com.taobao.tddl.common.exception.TddlException;
import com.taobao.tddl.common.exception.TddlRuntimeException;
import com.taobao.tddl.common.model.Group;
import com.taobao.tddl.common.model.Group.GroupType;
import com.taobao.tddl.common.model.Matrix;
import com.taobao.tddl.common.model.lifecycle.AbstractLifecycle;
import com.taobao.tddl.common.utils.XmlHelper;
import com.taobao.tddl.config.ConfigDataHandler;
import com.taobao.tddl.config.ConfigDataListener;
import com.taobao.tddl.config.impl.ConfigDataHandlerCity;
import com.taobao.tddl.executor.spi.IGroupExecutor;
import com.taobao.tddl.executor.spi.IRepository;
import com.taobao.tddl.optimizer.config.table.parse.MatrixParser;
import com.taobao.tddl.common.utils.logger.Logger;
import com.taobao.tddl.common.utils.logger.LoggerFactory;
/**
* group以及其对应的执行器
*
* @author mengshi.sunmengshi 2013-11-27 下午4:00:33
* @since 5.0.0
*/
public class TopologyHandler extends AbstractLifecycle {
public final static Logger logger = LoggerFactory.getLogger(TopologyHandler.class);
public final static String xmlHead = "<matrix xmlns=\"https://github.com/tddl/tddl/schema/matrix\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"https://github.com/tddl/tddl/schema/matrix https://raw.github.com/tddl/tddl/master/tddl-common/src/main/resources/META-INF/matrix.xsd\">";
public final static MessageFormat topologyNullError = new MessageFormat("get topology info error, appName is:{0}, unitName is:{1}, filePath is: {2}, dataId is: {3}");
// public final static MessageFormat TOPOLOGY = new
// MessageFormat("com.taobao.and_orV0.{0}_MACHINE_TAPOLOGY");
public final static MessageFormat TOPOLOGY = new MessageFormat("com.taobao.tddl.v1_{0}_topology");
public final static MessageFormat GROUP_TOPOLOGY = new MessageFormat("com.taobao.tddl.v1_{0}_dbgroups");
private final Map<String/* group key */, IGroupExecutor> executorMap = new HashMap<String, IGroupExecutor>();
private String appName;
private String unitName;
private String topologyFilePath;
private ConfigDataHandler cdh = null;
private Matrix matrix;
public TopologyHandler(String appName, String unitName, String topologyFilePath){
this.appName = appName;
this.unitName = unitName;
this.topologyFilePath = topologyFilePath;
}
@Override
protected void doInit() {
if (topologyFilePath != null) {
cdh = ConfigDataHandlerCity.getFileFactory(appName).getConfigDataHandler(topologyFilePath,
new TopologyListener(this));
} else {
cdh = ConfigDataHandlerCity.getFactory(appName, unitName)
.getConfigDataHandler(TOPOLOGY.format(new Object[] { appName }), new TopologyListener(this));
}
String data = cdh.getData(ConfigDataHandler.GET_DATA_TIMEOUT, ConfigDataHandler.FIRST_SERVER_STRATEGY);
if (data == null) {
// 尝试读一次tddl的appName规则
data = generateTopologyXML(appName, unitName);
}
if (data == null) {
String dataId = TOPOLOGY.format(new Object[] { appName });
throw new TddlRuntimeException(topologyNullError.format(new String[] { appName, unitName, topologyFilePath,
dataId }));
}
try {
Matrix matrix = MatrixParser.parse(data);
this.matrix = matrix;
} catch (Exception ex) {
logger.error("matrix topology init error,file is: " + this.getTopologyFilePath() + ", appname is: "
+ this.getAppName(),
ex);
throw new TddlRuntimeException(ex);
}
}
protected void doDestory() throws TddlException {
Map<String, IRepository> repos = ExecutorContext.getContext().getRepositoryHolder().getRepository();
for (IRepository repo : repos.values()) {
repo.destory();
}
cdh.destory();
}
/**
* 指定Group配置,创建一个GroupExecutor
*
* @param group
* @return
*/
public IGroupExecutor createOne(Group group) {
group.setAppName(this.appName);
IRepository repo = ExecutorContext.getContext()
.getRepositoryHolder()
.getOrCreateRepository(group.getType().toString(), matrix.getProperties());
IGroupExecutor groupExecutor = repo.getGroupExecutor(group);
putOne(group.getName(), groupExecutor);
return groupExecutor;
}
/**
* 添加指定groupKey的GroupExecutor,返回之前已有的
*
* @param groupKey
* @param groupExecutor
* @return
*/
public IGroupExecutor putOne(String groupKey, IGroupExecutor groupExecutor) {
return putOne(groupKey, groupExecutor, true);
}
public IGroupExecutor putOne(String groupKey, IGroupExecutor groupExecutor, boolean singleton) {
if (singleton && executorMap.containsKey(groupKey)) {
throw new IllegalArgumentException("group key is already exists . group key : " + groupKey + " . map "
+ executorMap);
}
return executorMap.put(groupKey, groupExecutor);
}
public IGroupExecutor get(String key) {
IGroupExecutor groupExecutor = executorMap.get(key);
if (groupExecutor == null) {
Group group = matrix.getGroup(key);
if (group != null) {
synchronized (executorMap) {
// double-check,避免并发创建
groupExecutor = executorMap.get(key);
if (groupExecutor == null) {
return createOne(group);
} else {
return executorMap.get(key);
}
}
}
}
return groupExecutor;
}
private String generateTopologyXML(String appName, String unitName) {
ConfigDataHandler groupHanlder = null;
try {
String matrixKey = GROUP_TOPOLOGY.format(new Object[] { appName });
groupHanlder = ConfigDataHandlerCity.getFactory(appName, unitName).getConfigDataHandler(matrixKey);
String keys = groupHanlder.getData(ConfigDataHandler.GET_DATA_TIMEOUT,
ConfigDataHandler.FIRST_SERVER_STRATEGY);
if (keys == null) {
throw new IllegalArgumentException("dataId : " + matrixKey + " is null");
}
String[] keysArray = keys.split(",");
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dbf.newDocumentBuilder();
Document doc = builder.newDocument();
Element matrix = doc.createElement("matrix");
// matrix.setAttribute("name", appName);
doc.appendChild(matrix); // 将根元素添加到文档上
Element appNameNode = doc.createElement("appName");
appNameNode.appendChild(doc.createTextNode(appName));
matrix.appendChild(appNameNode);
for (String str : keysArray) {
Element group = doc.createElement("group");
group.setAttribute("name", str);
group.setAttribute("type", GroupType.MYSQL_JDBC.name());// 默认为mysql类型
matrix.appendChild(group);
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
OutputStreamWriter outwriter = new OutputStreamWriter(baos);
XmlHelper.callWriteXmlFile(doc, outwriter, "utf-8");
outwriter.close();
String xml = baos.toString();
return StringUtils.replace(xml, "<matrix>", xmlHead);
} catch (IOException e) {
throw new TddlRuntimeException(e);
} catch (ParserConfigurationException e) {
throw new TddlRuntimeException(e);
} finally {
try {
groupHanlder.destory();
} catch (TddlException e) {
throw new TddlRuntimeException(e);
}
}
}
@Override
public String toString() {
return "TopologyHandler [executorMap=" + executorMap + "]";
}
public class TopologyListener implements ConfigDataListener {
@SuppressWarnings("unused")
private final TopologyHandler topologyHandler;
public TopologyListener(TopologyHandler topologyHandler){
this.topologyHandler = topologyHandler;
}
@Override
public void onDataRecieved(String dataId, String data) {
// do nothing
}
}
public String getAppName() {
return appName;
}
public void setAppName(String appName) {
this.appName = appName;
}
public String getUnitName() {
return unitName;
}
public void setUnitName(String unitName) {
this.unitName = unitName;
}
public String getTopologyFilePath() {
return topologyFilePath;
}
public void setTopologyFilePath(String topologyFilePath) {
this.topologyFilePath = topologyFilePath;
}
public Matrix getMatrix() {
return this.matrix;
}
}