/**
*
*/
package com.taobao.top.analysis.node.component;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.taobao.top.analysis.config.JobConfig;
import com.taobao.top.analysis.config.MasterConfig;
import com.taobao.top.analysis.exception.AnalysisException;
import com.taobao.top.analysis.node.IJobBuilder;
import com.taobao.top.analysis.node.job.Job;
import com.taobao.top.analysis.node.job.JobResource;
import com.taobao.top.analysis.node.job.JobTask;
import com.taobao.top.analysis.node.operation.JobDataOperation;
import com.taobao.top.analysis.statistics.data.Alias;
import com.taobao.top.analysis.statistics.data.InnerKey;
import com.taobao.top.analysis.statistics.data.ObjectColumn;
import com.taobao.top.analysis.statistics.data.Report;
import com.taobao.top.analysis.statistics.data.ReportEntry;
import com.taobao.top.analysis.statistics.data.Rule;
import com.taobao.top.analysis.statistics.data.impl.SimpleCalculator;
import com.taobao.top.analysis.statistics.data.impl.SimpleCondition;
import com.taobao.top.analysis.statistics.data.impl.SimpleFilter;
import com.taobao.top.analysis.statistics.map.DefaultMapper;
import com.taobao.top.analysis.statistics.map.IMapper;
import com.taobao.top.analysis.statistics.reduce.DefaultReducer;
import com.taobao.top.analysis.statistics.reduce.IReducer;
import com.taobao.top.analysis.statistics.reduce.group.GroupFunctionFactory;
import com.taobao.top.analysis.util.AnalyzerFilenameFilter;
import com.taobao.top.analysis.util.ReportUtil;
/**
*
* 通过读取文件,创建多个任务
*
* @author fangweng
* @Email fangweng@taobao.com
* 2011-11-24
*
*/
public class FileJobBuilder implements IJobBuilder{
private static final Log logger = LogFactory.getLog(FileJobBuilder.class);
private MasterConfig config;
private AtomicBoolean needRebuild = new AtomicBoolean(false);
private IMapper defaultMapper = new DefaultMapper();
private IReducer defaultReducer = new DefaultReducer();
/**
* 可用于rebuild,缓存上次的编译文件路径
*/
private String jobResource;
/**
* 将读取的job规则配置进行缓存
*/
private Map<String, JobResource> jobConfigs;
/**
* jobs.properties上次修改时间
*/
private long lastFileModify;
@Override
public boolean isNeedRebuild() {
return needRebuild.get();
}
@Override
public void setNeedRebuild(boolean needRebuild) {
this.needRebuild.compareAndSet(false, needRebuild);
}
@Override
public MasterConfig getConfig() {
return config;
}
@Override
public void setConfig(MasterConfig config) {
this.config = config;
}
public Map<String,Job> build() throws AnalysisException
{
if(config == null)
throw new AnalysisException("master config is null!");
return build(config.getJobsSource());
}
/**
* 从某一个位置获取任务集
* @param 可以自己扩展是从本地文件载入还是http等其他方式载入
* @return
* @throws AnalysisException
*/
public Map<String,Job> build(String config) throws AnalysisException
{
if (logger.isInfoEnabled())
logger.info("start build job from :" + config);
jobResource = config;
Map<String,Job> jobs = new HashMap<String,Job>();
InputStream in = null;
try
{
in = ReportUtil.getInputStreamFromFile(config);
Properties prop = new Properties();
prop.load(in);
String js = (String)prop.get("jobs");
if (js != null)
{
String[] instances = StringUtils.split(js,",");
Set<String> allMasters = new HashSet<String>();
for(String j : instances)
{
try {
Job job = new Job();
Rule rule = new Rule();
JobConfig jobconfig = new JobConfig();
job.setStatisticsRule(rule);
job.setJobConfig(jobconfig);
job.setJobName(j);
getConfigFromProps(j, jobconfig, prop);
if (jobconfig.getReportConfigs() == null
|| (jobconfig.getReportConfigs() != null && jobconfig.getReportConfigs().length == 0)) {
throw new AnalysisException("job Config files should not be null!");
}
buildRule(jobconfig.getReportConfigs(), rule);
//增加一个获得当前临时文件数据源游标的操作
// JobDataOperation jobDataOperation = new JobDataOperation(job,
// AnalysisConstants.JOBMANAGER_EVENT_LOADDATA,this.config);
// jobDataOperation.run();
JobDataOperation.getSourceTimeStamp(job, this.config);
// JobDataOperation.loadDataToTmp(job, this.config);
// JobDataOperation.loadData(job, this.config);
buildTasks(job);
jobs.put(job.getJobName(), job);
this.jobConfigs.put(job.getJobName(),
new JobResource(job.getJobName(), jobconfig.getReportConfigs()));
if (job.getJobConfig().getSaveTmpResultToFile() == null && this.config != null)
job.getJobConfig().setSaveTmpResultToFile(
String.valueOf(this.config.getSaveTmpResultToFile()));
if (job.getJobConfig().getAsynLoadDiskFilePrecent() < 0 && this.config != null)
job.getJobConfig().setAsynLoadDiskFilePrecent(
String.valueOf(this.config.getAsynLoadDiskFilePrecent()));
}
catch (Throwable e) {
logger.error("build job error : " + j, e);
}
}
//编译好rule后针对当前是否有mastergroup来做多master的report分配
if (this.config != null && StringUtils.isNotEmpty(this.config.getMasterGroup()))
{
String[] ms = StringUtils.split(this.config.getMasterGroup(),",");
List<String> masters = new ArrayList<String>();
List<String> reports = new ArrayList<String>();
for(String m : ms)
masters.add(m);
for(Job j : jobs.values())
{
Rule rule = j.getStatisticsRule();
reports.clear();
for(Report r : rule.getReportPool().values())
{
reports.add(new StringBuilder().append(r.getId())
.append("|").append(r.getWeight()).toString());
}
//做一下改进,如果原来已经有分配的,为了保证数据一致性,则不再分配(保证中间结果的连贯性)
//考虑原来就是比较平均分配的,然后将新来业务平均分配也是一样的
// Map<String, String> report2Master = ReportUtil.SimpleAllocationAlgorithm(masters, reports, "|");
Map<String, String> report2Master = new HashMap<String, String>();
//此处将report2Master传入方法中进行修改,并非好的代码处理方式
// AnalyzerUtil.loadReportToMaster(masters, reports, report2Master, j);
if(this.config.getReportToMaster() != null && this.config.getReportToMaster().size() > 0) {
report2Master.putAll(this.config.getReportToMaster());
}
for(Report r : rule.getReportPool().values()) {
if(!report2Master.containsKey(r.getId()) && this.config.getDispatchMaster()) {
report2Master.put(r.getId(), ReportUtil.getIp() + ":" + this.config.getMasterPort());
}
}
for(Entry<String,String> rm : report2Master.entrySet())
// if (rule.getReport2Master().get(rm.getKey()) == null)
rule.getReport2Master().put(rm.getKey(), rm.getValue());
if (logger.isWarnEnabled() && rule.getReport2Master() != null)
{
StringBuilder report2MasterStr = new StringBuilder("report2Master Info : ");
for(Entry<String,String> r : rule.getReport2Master().entrySet())
{
report2MasterStr.append("report: ")
.append(r.getKey()).append(" -> master: ").append(r.getValue()).append(" , ");
}
logger.error(report2MasterStr.toString());
}
// AnalyzerUtil.exportReportToMaster(report2Master, j);
allMasters.addAll(report2Master.values());
}
}
if (this.config != null) {
allMasters.add(ReportUtil.getIp() + ":" + this.config.getMasterPort());
StringBuilder sb = new StringBuilder("allMasters:");
for (String master : allMasters) {
sb.append(master).append(",");
}
logger.error(sb.toString());
}
for(Job j : jobs.values()) {
j.getStatisticsRule().setMasters(allMasters);
}
}
lastFileModify = (new File(this.jobResource.substring(jobResource
.indexOf("file:") + "file:".length()))).lastModified();
}
catch(IOException ex)
{
logger.error(ex,ex);
}
finally
{
if (in != null)
{
try
{
in.close();
} catch (IOException e) {
logger.error(e,e);
}
}
}
if (logger.isInfoEnabled())
logger.info("build job complete from :" + config);
return jobs;
}
void getConfigFromProps(String jobName,JobConfig jobConfig,Properties prop)
{
String prefix = new StringBuilder(jobName).append(".").toString();
Map<String,String> tmp = new HashMap<String,String>();
Iterator<Object> keys = prop.keySet().iterator();
while(keys.hasNext())
{
String key = (String)keys.next();
if (key.startsWith(prefix))
{
tmp.put(key.substring(prefix.length()), prop.getProperty(key));
}
}
jobConfig.addAllToConfig(tmp);
}
/**
* 编译分析规则模型
* @param configs
* @param rule
* @throws IOException
* @throws AnalysisException
*/
private void buildRule(String[] configs, Rule rule) throws AnalysisException, IOException {
if (configs != null) {
for (String config : configs) {
if (config.startsWith("dir:")) {
File[] files =
new File(config.substring(config.indexOf("dir:") + "dir:".length()))
.listFiles(new AnalyzerFilenameFilter(".xml"));
if(files == null) {
logger.error("please have a check at " + config);
}
for (File file : files) {
this.buildReportModule(new StringBuilder("file:").append(file.getAbsolutePath()).toString(),
rule);
}
rule.setVersion(Calendar.getInstance().getTimeInMillis());
}
else {
this.buildReportModule(config, rule);
rule.setVersion(Calendar.getInstance().getTimeInMillis());
}
}
// 遍历所有报表的entry,再遍历这些entry中所使用的其他的entry
// 将entry与report的关联关系理清楚,因为在数据清理,多master数据发送的时候,都是按照entry来进行的
for(Report report : rule.getReportPool().values()) {
for(ReportEntry entry : report.getReportEntrys()) {
Set<String> referEntries = ((SimpleCalculator) entry.getCalculator()).getReferEntries();
if(referEntries == null)
continue;
for(String key : referEntries) {
setReport(entry, rule, rule.getEntryPool().get(key));
}
}
}
}
}
/**
* 采用递归来设置每一个entry的报表
*/
private void setReport(ReportEntry entry, Rule rule, ReportEntry referEntry) {
if(referEntry == null) {
logger.error("please have a check at the referEntry. How can it be null?");
return;
}
for(String reportName : entry.getReports()) {
referEntry.addReport(rule.getReportPool().get(reportName));
}
Set<String> referEntries = ((SimpleCalculator) referEntry.getCalculator()).getReferEntries();
if(referEntries == null)
return;
for(String refer : referEntries) {
setReport(referEntry, rule, rule.getEntryPool().get(refer));
}
}
/**
* 编译分析规则模型
*
* @param 配置文件
* @param entry定义池
* @param 父级entry定义池
* @param 报表定义池
* @param 别名定义池
* @param 告警定义池
* @throws IOException
* @throws XMLStreamException
*/
public void buildReportModule(String configFile, Rule rule)
throws AnalysisException, IOException {
if(logger.isInfoEnabled()) {
logger.info("start build rule in " + configFile);
}
InputStream in = null;
XMLEventReader r = null;
Report report = null;
StringBuilder globalConditions = new StringBuilder();
StringBuilder globalValuefilter = new StringBuilder();
List<String> globalMapClass = new ArrayList<String>();
String domain = null;
String localdir = new StringBuilder()
.append(System.getProperty("user.dir"))
.append(File.separatorChar).toString();
if (configFile == null || "".equals(configFile)) {
String error = "configFile can not be null !";
logger.error(error);
throw new AnalysisException(error);
}
try {
XMLInputFactory factory = XMLInputFactory.newInstance();
ClassLoader loader = Thread.currentThread().getContextClassLoader();
if (configFile.startsWith("file:")) {
try {
in = new java.io.FileInputStream(new File(
configFile.substring(configFile.indexOf("file:")
+ "file:".length())));
} catch (Exception e) {
logger.error(e,e);
}
if (in == null)
in = new java.io.FileInputStream(new File(localdir
+ configFile.substring(configFile.indexOf("file:")
+ "file:".length())));
} else {
URL url = loader.getResource(configFile);
if (url == null) {
String error = "configFile: " + configFile + " not exist !";
logger.error(error);
throw new java.lang.RuntimeException(error);
}
in = url.openStream();
}
r = factory.createXMLEventReader(in);
List<String> parents = new ArrayList<String>();
while (r.hasNext()) {
XMLEvent event = r.nextEvent();
if (event.isStartElement()) {
StartElement start = event.asStartElement();
String tag = start.getName().getLocalPart();
if (tag.equalsIgnoreCase("domain")) {
if (start.getAttributeByName(new QName("", "value")) != null) {
domain = start.getAttributeByName(
new QName("", "value")).getValue();
rule.setDomain(domain);
}
continue;
}
if (tag.equalsIgnoreCase("alias")) {
Alias alias = new Alias();
alias.setName(start.getAttributeByName(
new QName("", "name")).getValue());
alias.setKey(Integer.valueOf(start.getAttributeByName(
new QName("", "key")).getValue()));
rule.getAliasPool().put(alias.getName(), alias);
continue;
}
if (tag.equalsIgnoreCase("inner-key")){
InnerKey innerKey = new InnerKey();
innerKey.setKey(Integer.parseInt(start.getAttributeByName(
new QName("", "key")).getValue()));
boolean isExist = false;
for(InnerKey ik : rule.getInnerKeyPool())
{
if (ik.getKey() == innerKey.getKey())
{
logger.error("duplicate innerkey define, key :" + innerKey.getKey());
isExist = true;
break;
}
}
if (!isExist)
{
if (innerKey.setFile(start.getAttributeByName(
new QName("", "file")).getValue()))
rule.getInnerKeyPool().add(innerKey);
else
logger.error("inner-key set error, file : " + innerKey.getFile());
}
continue;
}
if (tag.equalsIgnoreCase("global-condition")) {
if (start.getAttributeByName(new QName("", "value")) != null) {
if (globalConditions.length() > 0) {
globalConditions.append("&"
+ start.getAttributeByName(
new QName("", "value"))
.getValue());
} else {
globalConditions.append(start
.getAttributeByName(
new QName("", "value"))
.getValue());
}
}
continue;
}
if (tag.equalsIgnoreCase("global-mapClass")) {
if (start.getAttributeByName(new QName("", "value")) != null) {
globalMapClass.add(start.getAttributeByName(
new QName("", "value")).getValue());
}
continue;
}
if (tag.equalsIgnoreCase("global-valuefilter")) {
if (start.getAttributeByName(new QName("", "value")) != null) {
globalValuefilter.append(
start.getAttributeByName(
new QName("", "value")).getValue())
.append("&");
}
continue;
}
if (tag.equalsIgnoreCase("ReportEntry")
|| tag.equalsIgnoreCase("entry")) {
ReportEntry entry = new ReportEntry();
if (tag.equalsIgnoreCase("ReportEntry"))
setReportEntry(true, start, entry, report,
rule.getEntryPool(), rule.getAliasPool(),
globalConditions, globalValuefilter,
globalMapClass, parents);
else {
setReportEntry(false, start, entry, report,
rule.getEntryPool(), rule.getAliasPool(),
globalConditions, globalValuefilter,
globalMapClass, parents);
}
if (entry.getId() != null) {
if (rule.getEntryPool().get(entry.getId()) != null)
throw new java.lang.RuntimeException(
"ID confict:" + entry.getId());
rule.getEntryPool().put(entry.getId(), entry);
}
// 增加引用标识
if (tag.equalsIgnoreCase("entry")) {
if (entry.getId() != null)
rule.getReferEntrys().put(entry.getId(), entry);
else if (report.getReportEntrys() != null
&& report.getReportEntrys().size() > 0)
rule.getReferEntrys().put(
report.getReportEntrys()
.get(report.getReportEntrys()
.size() - 1).getId(),
report.getReportEntrys()
.get(report.getReportEntrys()
.size() - 1));
}
ReportEntry _tmpEntry = entry;
if (_tmpEntry.getId() == null
&& report.getReportEntrys() != null
&& report.getReportEntrys().size() > 0)
_tmpEntry = report.getReportEntrys().get(
report.getReportEntrys().size() - 1);
List<Object> bindingStack = ((SimpleCalculator)_tmpEntry.getCalculator()).getBindingStack();
String valueExpression = ((SimpleCalculator)_tmpEntry.getCalculator()).getValue();
if (bindingStack != null) {
if (valueExpression != null
&& valueExpression.indexOf(
"entry(") >= 0)
for (Object k : bindingStack) {
rule.getReferEntrys().put((String)k, null);
}
}
continue;
}
if (tag.equalsIgnoreCase("report")) {
if (report != null) {
if(report.getId() != null) {
if(rule.getReportPool().get(report.getId()) != null)
throw new java.lang.RuntimeException(
"ID confict:" + report.getId());
}
rule.getReportPool().put(report.getId(), report);
}
report = new Report();
setReport(start, report, rule.getReportPool());
continue;
}
if (tag.equalsIgnoreCase("entryList")) {
report.setReportEntrys(new ArrayList<ReportEntry>());
continue;
}
}
if (event.isEndElement()) {
EndElement end = event.asEndElement();
String tag = end.getName().getLocalPart();
if (tag.equalsIgnoreCase("reports") && report != null) {
rule.getReportPool().put(report.getId(), report);
continue;
}
}
}
// 给刚刚记录的parent Entry对象打上标识
for (Iterator<String> iterator = parents.iterator(); iterator
.hasNext();) {
String parent = iterator.next();
ReportEntry parentEntry = rule.getEntryPool().get(parent);
rule.getParentEntryPool().put(parent, parentEntry);
}
// 删除没有被引用的公用的定义
// 考虑到每一个job(也就是过去的instance的概念)对应于同一个rule,并非是一个配置文件对应一个rule,因此
// rule中删除不用的key这一操作,应该是在所有配置文件读取完后进行
if (rule.getReferEntrys() != null
&& rule.getReferEntrys().size() > 0) {
Iterator<Entry<String, ReportEntry>> iter = rule.getEntryPool()
.entrySet().iterator();
StringBuilder invalidKeys = new StringBuilder();
while (iter.hasNext()) {
Entry<String, ReportEntry> e = iter.next();
if (!rule.getReferEntrys().containsKey(e.getKey())) {
iter.remove();
invalidKeys.append(e.getKey()).append(",");
}
}
if (invalidKeys.length() > 0)
logger.error("File: " + configFile
+ " ----- remove invalid entry define : "
+ invalidKeys.toString());
}
}
catch(Throwable ex)
{
logger.error("the error config file is " + configFile,ex);
}
finally {
if (r != null)
try {
r.close();
} catch (XMLStreamException e) {
logger.error(e,e);
}
if (in != null)
in.close();
r = null;
in = null;
}
if(logger.isInfoEnabled()) {
logger.info("complete build rule in " + configFile);
}
}
/**
* 构建报表对象
*
* @param 数据节点
* @param 上一个节点
* @param 报表池
*/
public void setReport(StartElement start, Report report,
Map<String, Report> reportPool) {
if (start.getAttributeByName(new QName("", "id")) != null) {
report.setId(start.getAttributeByName(new QName("", "id"))
.getValue());
}
if (start.getAttributeByName(new QName("", "weight")) != null) {
report.setWeight(Integer.valueOf(start.getAttributeByName(new QName("", "weight"))
.getValue()));
}
if (start.getAttributeByName(new QName("", "key")) != null) {
report.setKey(start.getAttributeByName(new QName("", "key"))
.getValue());
}
if (start.getAttributeByName(new QName("", "condition")) != null) {
report.setCondition(start.getAttributeByName(new QName("", "condition"))
.getValue());
}
if (start.getAttributeByName(new QName("", "file")) != null) {
report.setFile(start.getAttributeByName(new QName("", "file"))
.getValue());
}
if (start.getAttributeByName(new QName("", "period")) != null) {
report.setPeriod(Boolean.valueOf(start.getAttributeByName(
new QName("", "period")).getValue()));
}
if (start.getAttributeByName(new QName("", "append")) != null) {
report.setAppend(Boolean.valueOf(start.getAttributeByName(
new QName("", "append")).getValue()));
}
if (start.getAttributeByName(new QName("", "exportInterval")) != null) {
report.setExportInterval(Long.valueOf(start.getAttributeByName(
new QName("", "exportInterval")).getValue()));
}
if (start.getAttributeByName(new QName("", "orderby")) != null) {
report.setOrderby(start
.getAttributeByName(new QName("", "orderby")).getValue());
}
if (start.getAttributeByName(new QName("", "rowCount")) != null) {
report.setRowCount(Integer.valueOf(start.getAttributeByName(
new QName("", "rowCount")).getValue()));
}
}
/**
* 构建报表entry对象
*
* @param 是否是公用的entry
* ,非公用的定义在report对象中
* @param 数据节点
* @param 当前处理的entry
* @param 当前隶属的report
* ,这个值只有在非公用的entry解析的时候用到
* @param entry池
* @param 别名池
* @param 全局条件池
* @param 全局的valueFilter定义
* @param 全局的mapClass定义
* @param 父entry定义池
* @throws AnalysisException
*/
public void setReportEntry(boolean isPublic, StartElement start,
ReportEntry entry, Report report,
Map<String, ReportEntry> entryPool, Map<String, Alias> aliasPool,
StringBuilder globalConditions, StringBuilder globalValuefilter,
List<String> globalMapClass, List<String> parents) throws AnalysisException {
// 引用的方式,只在非共享模式下有用
if ((!isPublic && start.getAttributeByName(new QName("", "id")) != null
&& start.getAttributeByName(new QName("", "name")) == null)
||(start.getAttributeByName(new QName("", "refId")) != null))
{
ReportEntry node = entryPool.get(start.getAttributeByName(
new QName("", "id")).getValue());
if (node != null) {
report.getReportEntrys().add(node);
//给node增加report的属性
node.addReport(report);
} else {
String errorMsg = new StringBuilder()
.append("reportEntry not exist :")
.append(start.getAttributeByName(new QName("", "id"))
.getValue()).toString();
throw new java.lang.RuntimeException(errorMsg);
}
return;
}
if (report != null)
{
//给node增加report的属性
entry.addReport(report);
}
if (start.getAttributeByName(new QName("", "name")) != null) {
entry.setName(start.getAttributeByName(new QName("", "name"))
.getValue());
}
if (start.getAttributeByName(new QName("", "id")) != null) {
entry.setId(start.getAttributeByName(new QName("", "id"))
.getValue());
} else {
if (!isPublic && report != null) {
entry.setId(new StringBuilder().append("report:")
.append(report.getId()).append(entry.getName())
.toString());
}
}
if (entry.getId() == null)
throw new java.lang.RuntimeException("entry id can't be null...");
if (start.getAttributeByName(new QName("", "mapClass")) != null) {
String className = start.getAttributeByName(
new QName("", "mapClass")).getValue();
IMapper mapper = ReportUtil.getInstance(IMapper.class,
Thread.currentThread().getContextClassLoader(),className,
true);
assert mapper != null;
entry.setMapClass(mapper);
}else{
entry.setMapClass(defaultMapper);
}
if (start.getAttributeByName(new QName("", "reduceClass")) != null) {
String className = start.getAttributeByName(
new QName("", "reduceClass")).getValue();
IReducer reducer = ReportUtil.getInstance(IReducer.class,
Thread.currentThread().getContextClassLoader(),className,
true);
assert reducer != null;
entry.setReduceClass(reducer);
}else{
entry.setReduceClass(defaultReducer);
}
// 添加period字段
if (start.getAttributeByName(new QName("", "period")) != null) {
entry.setPeriod(Boolean.valueOf(start.getAttributeByName(new QName("", "period")).getValue()));
}
if (start.getAttributeByName(new QName("", "mapParams")) != null) {
entry.setMapParams(start.getAttributeByName(
new QName("", "mapParams")).getValue());
}
if (start.getAttributeByName(new QName("", "reduceParams")) != null) {
entry.setReduceParams(start.getAttributeByName(
new QName("", "reduceParams")).getValue());
}
if (start.getAttributeByName(new QName("", "additions")) != null) {
entry.setAdditions(start.getAttributeByName(
new QName("", "additions")).getValue());
}
if (start.getAttributeByName(new QName("", "key")) != null) {
String[] ks = start.getAttributeByName(new QName("", "key")).getValue().split(",");
List<ObjectColumn> subKeys = new ArrayList<ObjectColumn>();
int[] keys = ReportUtil.transformVars(ks, aliasPool,subKeys);
// 用alias替换部分key
entry.setKeys(keys);
if (subKeys.size() > 0)
entry.setSubKeys(subKeys);
}
else
{
//直接继承report的key
if (!isPublic && report != null && report.getKey() != null)
{
String[] ks = report.getKey().split(",");
List<ObjectColumn> subKeys = new ArrayList<ObjectColumn>();
int[] keys = ReportUtil.transformVars(ks, aliasPool,subKeys);
// 用alias替换部分key
entry.setKeys(keys);
if (subKeys.size() > 0)
entry.setSubKeys(subKeys);
}
else
throw new AnalysisException("entry key should not be null! entry name :" + entry.getName() + ", report:" + report.getFile());
}
if (start.getAttributeByName(new QName("", "value")) != null) {
String content = start.getAttributeByName(new QName("", "value"))
.getValue();
String type = content.substring(0, content.indexOf("("));
String expression = content.substring(content.indexOf("(") + 1,
content.lastIndexOf(")"));
entry.setGroupFunction(GroupFunctionFactory.getFunction(type));
if (content.indexOf("entry(") >= 0){
entry.setLazy(true);
}
entry.setCalculator(new SimpleCalculator(expression, aliasPool));
}
if (start.getAttributeByName(new QName("", "lazy")) != null) {
entry.setLazy(Boolean.valueOf(start.getAttributeByName(
new QName("", "lazy")).getValue()));
}
// 以下修改conditions的设置方式 modify by fangliang 2010-05-26
StringBuilder conditions = new StringBuilder();
if (globalConditions != null && globalConditions.length() > 0) {
conditions.append(globalConditions);
}
//add by fangweng report 也可以有condition
if (report != null && report.getCondition() != null && report.getCondition().length() > 0)
{
conditions.append("&").append(report.getCondition());
}
Attribute attr = start.getAttributeByName(new QName("", "condition"));
if (attr != null) {
conditions.append("&" + attr.getValue());
}
if (conditions.length() > 0) {
entry.setCondition(new SimpleCondition(conditions.toString(), aliasPool));
}
String filter = null;
if (start.getAttributeByName(new QName("", "valuefilter")) != null) {
if (globalValuefilter != null && globalValuefilter.length() > 0)
filter = new StringBuilder(globalValuefilter)
.append(start.getAttributeByName(
new QName("", "valuefilter")).getValue()).toString();
else
filter = start.getAttributeByName(new QName("", "valuefilter")).getValue();
} else {
if (globalValuefilter != null && globalValuefilter.length() > 0)
filter = globalValuefilter.toString();
}
entry.setValueFilter(new SimpleFilter(filter));
if (report != null)
report.getReportEntrys().add(entry);
}
@Override
public void init() {
this.jobConfigs = new HashMap<String, JobResource>();
}
@Override
public void releaseResource() {
this.jobConfigs.clear();
this.jobConfigs = null;
}
@Override
public Map<String,Job> rebuild(Map<String,Job> jobs) throws AnalysisException {
if (this.needRebuild.getAndSet(false))
{
Map<String,Job> result = build();
if(jobs != null) {
for(Entry<String, Job> entry : result.entrySet()) {
if(jobs.containsKey(entry.getKey())) {
// entry.getValue().getEpoch().set(jobs.get(entry.getKey()).getEpoch().incrementAndGet());
// entry.getValue().setJobSourceTimeStamp(jobs.get(entry.getKey()).getJobSourceTimeStamp());
// entry.getValue().setLastExportTime(jobs.get(entry.getKey()).getLastExportTime());
// entry.getValue().setJobResult(jobs.get(entry.getKey()).getJobResult());
jobs.get(entry.getKey()).rebuild(1, entry.getValue(), null);
} else {
jobs.put(entry.getKey(), entry.getValue());
jobs.get(entry.getKey()).rebuild(2, entry.getValue(), null);
}
}
for(Entry<String, Job> entry : jobs.entrySet()) {
if(!result.containsKey(entry.getKey())) {
entry.getValue().rebuild(-1, entry.getValue(), null);
}
}
}
return jobs;
}
else
return null;
}
private String generateJobInputAddition(String input,Job job)
{
StringBuilder result = new StringBuilder(input);
if (input != null && input.startsWith("http"))
{
result.append("&jobSourceTimeStamp=").append(job.getJobSourceTimeStamp())
.append("&epoch=").append(job.getEpoch());
}
return result.toString();
}
@Override
public void buildTasks(Job job) throws AnalysisException {
if (job.getJobTasks() != null)
job.getJobTasks().clear();
JobConfig jobConfig = job.getJobConfig();
if (jobConfig == null)
throw new AnalysisException("generateJobTasks error, jobConfig is null.");
//允许定义多个job通过逗号分割
if (jobConfig.getInputParams() == null && jobConfig.getInput().indexOf(",") <= 0)
{
JobTask jobTask = new JobTask(jobConfig);
jobTask.setStatisticsRule(job.getStatisticsRule());
jobTask.setJobName(job.getJobName());
jobTask.setUrl(jobTask.getInput());
jobTask.setJobSourceTimeStamp(job.getJobSourceTimeStamp());
jobTask.setInput(generateJobInputAddition(jobTask.getInput(),job));
jobTask.setTaskId(getTaskIdFromUrl(job.getJobName(), jobTask.getUrl(), job.getTaskCount()));
/**
* 目前使用master游标管理方式的只有hub
*/
Long begin = jobConfig.getBegin();
if(begin == null)
begin = 0L;
if(jobTask.getUrl().startsWith("hub://")) {
String key = jobTask.getUrl().substring(0, jobTask.getUrl().indexOf('?'));
job.getCursorMap().putIfAbsent(key, begin);
job.getTimestampMap().putIfAbsent(key, -1L);
jobTask.setJobSourceTimeStamp(job.getTimestampMap().get(key));
}
job.addTaskCount();
jobTask.getTailCursor().set(jobConfig.getInit());
job.getJobTasks().add(jobTask);
}
else
{
if (jobConfig.getInputParams() != null)
{
String[] p = StringUtils.split(jobConfig.getInputParams(),":");
String key = new StringBuilder("$").append(p[0]).append("$").toString();
if (p.length != 2 || jobConfig.getInput().indexOf(key) < 0)
throw new AnalysisException("inputParams invalidate : " + jobConfig.getInputParams());
String[] params = StringUtils.split(p[1],",");
for(String ps : params)
{
JobTask jobTask = new JobTask(jobConfig);
jobTask.setStatisticsRule(job.getStatisticsRule());
jobTask.setJobName(job.getJobName());
jobTask.setUrl(jobConfig.getInput().replace(key, ps));
jobTask.setJobSourceTimeStamp(job.getJobSourceTimeStamp());
jobTask.setInput(generateJobInputAddition(jobConfig.getInput().replace(key, ps),job));
jobTask.setTaskId(getTaskIdFromUrl(job.getJobName(), jobTask.getUrl(), job.getTaskCount()));
/**
* 目前使用master游标管理方式的只有hub
*/
Long begin = jobConfig.getBegin();
if(begin == null)
begin = 0L;
if(jobTask.getUrl().startsWith("hub://")) {
String keyU = jobTask.getUrl().substring(0, jobTask.getUrl().indexOf('?'));
job.getCursorMap().putIfAbsent(keyU, begin);
job.getTimestampMap().putIfAbsent(keyU, -1L);
jobTask.setJobSourceTimeStamp(job.getTimestampMap().get(keyU));
}
jobTask.getTailCursor().set(jobConfig.getInit());
job.addTaskCount();
job.getJobTasks().add(jobTask);
}
}
else
{
String[] inputs = StringUtils.split(jobConfig.getInput(),",");
for(String input : inputs)
{
JobTask jobTask = new JobTask(jobConfig);
jobTask.setStatisticsRule(job.getStatisticsRule());
jobTask.setJobName(job.getJobName());
jobTask.setInput(generateJobInputAddition(input,job));
jobTask.setUrl(input);
jobTask.setTaskId(getTaskIdFromUrl(job.getJobName(), jobTask.getUrl(), job.getTaskCount()));
/**
* 目前使用master游标管理方式的只有hub
*/
Long begin = jobConfig.getBegin();
if(begin == null)
begin = 0L;
if(jobTask.getUrl().startsWith("hub://")) {
String key = jobTask.getUrl().substring(0, jobTask.getUrl().indexOf('?'));
job.getCursorMap().putIfAbsent(key, begin);
job.getTimestampMap().putIfAbsent(key, -1L);
jobTask.setJobSourceTimeStamp(job.getTimestampMap().get(key));
}
jobTask.getTailCursor().set(jobConfig.getInit());
job.addTaskCount();
job.getJobTasks().add(jobTask);
}
}
}
}
/*
* (non-Javadoc)
* @see com.taobao.top.analysis.node.IJobBuilder#getJobResource()
*/
public String getJobResource() {
return jobResource;
}
/* (non-Javadoc)
* @see com.taobao.top.analysis.node.IJobBuilder#isModified()
*/
@Override
public boolean isModified() {
if(this.jobResource == null)
return false;
File file = new File(this.jobResource.substring(jobResource
.indexOf("file:") + "file:".length()));
if(file.lastModified() > lastFileModify)
return true;
for(String job : jobConfigs.keySet()) {
if(jobConfigs.get(job).isModify())
return true;
}
return false;
}
private String getTaskIdFromUrl(final String jobName, final String url, final int taskCount) {
String temp = url.substring(url.indexOf("://") + 3);
if(temp.indexOf(':') >= 0)
temp = temp.substring(0, temp.indexOf(':'));
else if(temp.indexOf('/') >= 0)
temp = temp.substring(0, temp.indexOf('/'));
temp = jobName + "-" + temp + "-" + taskCount;
return temp;
}
}