package com.vip.saturn.job.shell;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.vip.saturn.job.SaturnJobReturn;
import com.vip.saturn.job.SaturnSystemErrorGroup;
import com.vip.saturn.job.SaturnSystemReturnCode;
import com.vip.saturn.job.basic.CrondJob;
import com.vip.saturn.job.basic.JavaShardingItemCallable;
import com.vip.saturn.job.basic.SaturnConstant;
import com.vip.saturn.job.basic.SaturnExecutionContext;
import com.vip.saturn.job.basic.ShardingItemCallable;
import com.vip.saturn.job.utils.ScriptPidUtils;
import com.vip.saturn.job.utils.SystemEnvProperties;
/**
* 处理通用Script的调度(也支持PHP)
* @author linzhaoming
*/
public class SaturnScriptJob extends CrondJob {
private static Logger log = LoggerFactory.getLogger(SaturnScriptJob.class);
private Object watchDogLock = new Object();
protected List<SaturnExecuteWatchdog> watchDogList = new ArrayList<SaturnExecuteWatchdog>();
protected List<ShardingItemCallable> shardingItemCallableList = new ArrayList<>();
private Random random = new Random();
@Override
public Map<Integer, SaturnJobReturn> handleJob(final SaturnExecutionContext shardingContext) {
watchDogList.clear();
shardingItemCallableList.clear();
final Map<Integer, SaturnJobReturn> retMap = new ConcurrentHashMap<Integer, SaturnJobReturn>();
Map<Integer, String> shardingItemParameters = shardingContext.getShardingItemParameters();
final String jobName = shardingContext.getJobName();
ExecutorService executorService = getExecutorService();
// 处理自定义参数
String jobParameter = shardingContext.getJobParameter();
final CountDownLatch latch = new CountDownLatch(shardingItemParameters.size());
for (final Entry<Integer, String> shardingItem : shardingItemParameters.entrySet()) {
final Integer key = shardingItem.getKey();
String jobValue = shardingItem.getValue();
final String execParameter = getRealItemValue(jobParameter, jobValue); // 作业分片的对应值
log.debug("jobname={}, key= {}, jobParameter={}", jobName, key, execParameter);
executorService.submit(new Runnable() {
@Override
public void run() {
SaturnJobReturn jobReturn = null;
try {
jobReturn = innerHandleWithListener(jobName, key, execParameter, shardingContext);
} catch (Throwable e) {
log.error(String.format(SaturnConstant.ERROR_LOG_FORMAT, jobName, e.getMessage()), e);
jobReturn = new SaturnJobReturn(SaturnSystemReturnCode.USER_FAIL, "Error: " + e.getMessage(), SaturnSystemErrorGroup.FAIL);
} finally {
retMap.put(key, jobReturn);
latch.countDown();
}
}
});
}
try {
latch.await();
} catch (final InterruptedException ex) {
log.error("[{}] msg=SaturnScriptJob: Job {} is interrupted", jobName, jobName);
Thread.currentThread().interrupt();
}
return retMap;
}
public void beforeExecution(ShardingItemCallable callable){
}
public void afterExecution(ShardingItemCallable callable){
}
public ShardingItemCallable createShardingItemCallable(String jobName, Integer item, String execParameter, SaturnExecutionContext shardingContext){
ShardingItemCallable callable = new ShardingItemCallable(jobName, item, execParameter,
getTimeoutSeconds(), shardingContext, this);
return callable;
}
protected SaturnJobReturn innerHandleWithListener(String jobName, Integer item, String execParameter, SaturnExecutionContext shardingContext) {
ShardingItemCallable callable = createShardingItemCallable(jobName, item, execParameter, shardingContext);
shardingItemCallableList.add(callable);
beforeExecution(callable);
SaturnJobReturn saturnJobReturn = null;
try{
saturnJobReturn = innerHandle(callable);
} catch (Throwable t) {
log.error(String.format(SaturnConstant.ERROR_LOG_FORMAT, jobName, t.getMessage()), t);
saturnJobReturn = new SaturnJobReturn(SaturnSystemReturnCode.USER_FAIL, t.getMessage(), SaturnSystemErrorGroup.FAIL);
}
if(saturnJobReturn == null) {
saturnJobReturn = new SaturnJobReturn(SaturnSystemReturnCode.USER_FAIL, "The returned SaturnJobReturn can not be null", SaturnSystemErrorGroup.FAIL);
}
callable.setSaturnJobReturn(saturnJobReturn);
afterExecution(callable);
return saturnJobReturn;
}
protected SaturnJobReturn innerHandle(ShardingItemCallable callable) {
SaturnJobReturn saturnJobReturn = null;
try {
String saturnOutputPath = String.format(ScriptPidUtils.JOBITEMOUTPUTPATH, callable.getShardingContext().getExecutorName(), jobName, callable.getItem(), random.nextInt(10000), System.currentTimeMillis());
callable.getEnvMap().put(SystemEnvProperties.NAME_VIP_SATURN_OUTPUT_PATH, saturnOutputPath);
ScriptJobRunner scriptJobRunner = new ScriptJobRunner(callable.getEnvMap(), this, callable.getItem(), callable.getItemValue(), callable.getShardingContext());
SaturnExecuteWatchdog watchDog = scriptJobRunner.getWatchdog();
watchDogList.add(watchDog);
saturnJobReturn = scriptJobRunner.runJob();
synchronized(watchDogLock){
watchDogList.remove(watchDog);
}
callable.setBusinessReturned(scriptJobRunner.isBusinessReturned());
} catch (Throwable t) {
log.error(String.format(SaturnConstant.ERROR_LOG_FORMAT, jobName, t.getMessage()), t);
saturnJobReturn = new SaturnJobReturn(SaturnSystemReturnCode.USER_FAIL, t.getMessage(), SaturnSystemErrorGroup.FAIL);
}
return saturnJobReturn;
}
@Override
public void forceStop() {
super.forceStop();
log.info("[{}] msg=shell executor invoked forceStop, watchDogList = {}", jobName, watchDogList);
if(watchDogList == null || watchDogList.isEmpty()){
ScriptPidUtils.forceStopRunningShellJob(executorName,jobName);
}else{
List<SaturnExecuteWatchdog> tmp = new ArrayList<SaturnExecuteWatchdog>();
synchronized(watchDogLock){
tmp.addAll(watchDogList);
}
for (SaturnExecuteWatchdog watchDog : tmp) {
log.info("[{}] msg=Job {}-{} is stopped, force the script {} to exit.", jobName, watchDog.getJobName(), watchDog.getJobItem(), watchDog.getExecParam());
// kill processes.
watchDog.destroyProcess();
int jobItem = watchDog.getJobItem();
long pid = ScriptPidUtils.getFirstPidFromFile(serverService.getExecutorName(), watchDog.getJobName(), ""+Integer.toString(jobItem));
if(pid > 0 && ScriptPidUtils.isPidRunning(pid)){
try {
ScriptPidUtils.killAllChildrenByPid(pid, true);
} catch (InterruptedException e) {
log.error(String.format(SaturnConstant.ERROR_LOG_FORMAT, jobName, e.getMessage()), e);
}
}
ScriptPidUtils.removeAllPidFile(serverService.getExecutorName(), watchDog.getJobName(), jobItem);
onForceStop(jobItem);
}
}
}
@Override
public void abort() {
super.abort();
forceStop();
}
@Override
public void onForceStop(int item) {
}
@Override
public void onTimeout(int item) {
}
@Override
public SaturnJobReturn doExecution(String jobName, Integer key, String value,
SaturnExecutionContext shardingContext, JavaShardingItemCallable callable) throws Throwable {
return null;
}
}