package com.vip.saturn.job.java; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import org.quartz.SchedulerException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.vip.saturn.job.SaturnJobExecutionContext; import com.vip.saturn.job.SaturnJobReturn; import com.vip.saturn.job.SaturnSystemErrorGroup; import com.vip.saturn.job.SaturnSystemReturnCode; import com.vip.saturn.job.basic.AbstractSaturnJob; import com.vip.saturn.job.basic.CrondJob; import com.vip.saturn.job.basic.JavaShardingItemCallable; import com.vip.saturn.job.basic.JobRegistry; import com.vip.saturn.job.basic.SaturnApi; import com.vip.saturn.job.basic.SaturnConstant; import com.vip.saturn.job.basic.SaturnExecutionContext; import com.vip.saturn.job.basic.ShardingItemFutureTask; import com.vip.saturn.job.basic.TimeoutSchedulerExecutor; import com.vip.saturn.job.exception.JobException; import com.vip.saturn.job.internal.config.JobConfiguration; public class SaturnJavaJob extends CrondJob { private static Logger log = LoggerFactory.getLogger(SaturnJavaJob.class); private Map<Integer, ShardingItemFutureTask> futureTaskMap; private Object jobBusinessInstance = null; public JavaShardingItemCallable createCallable(String jobName, Integer item, String itemValue, int timeoutSeconds, SaturnExecutionContext shardingContext, AbstractSaturnJob saturnJob){ return new JavaShardingItemCallable(jobName, item, itemValue, timeoutSeconds, shardingContext, saturnJob); } @Override public void init() throws SchedulerException{ super.init(); createJobBusinessInstanceIfNecessary(); } private void createJobBusinessInstanceIfNecessary() throws SchedulerException { JobConfiguration currentConf = configService.getJobConfiguration(); String jobClassStr = currentConf.getJobClass(); if (jobClassStr != null && !jobClassStr.trim().isEmpty()) { jobBusinessInstance = JobRegistry.getJobBusinessInstance(executorName, jobName); if (jobBusinessInstance == null) { ClassLoader jobClassLoader = saturnExecutorService.getJobClassLoader(); ClassLoader executorClassLoader = saturnExecutorService.getExecutorClassLoader(); Thread.currentThread().setContextClassLoader(jobClassLoader); try { Class<?> jobClass = saturnExecutorService.getJobClassLoader().loadClass(currentConf.getJobClass()); try { Method getObject = jobClass.getMethod("getObject"); if (getObject != null) { try { jobBusinessInstance = getObject.invoke(null); } catch (Throwable t) { log.error(String.format(SaturnConstant.ERROR_LOG_FORMAT, jobName, jobClassStr + " getObject error"), t); } } } catch (Exception ex) {//NOSONAR //log.error("",ex); } if (jobBusinessInstance == null) { jobBusinessInstance = jobClass.newInstance(); } SaturnApi saturnApi = new SaturnApi(); saturnApi.setConfigService(getConfigService()); jobClass.getMethod("setSaturnApi", Object.class).invoke(jobBusinessInstance, saturnApi); JobRegistry.addJobBusinessInstance(executorName, jobName, jobBusinessInstance); } catch (Throwable t) { log.error(String.format(SaturnConstant.ERROR_LOG_FORMAT, jobName, "create job business instance error"), t); throw new SchedulerException(t); } finally { Thread.currentThread().setContextClassLoader(executorClassLoader); } } } if (jobBusinessInstance == null) { throw new SchedulerException("init job business instance failed, the job class is " + jobClassStr); } } @Override protected Map<Integer, SaturnJobReturn> handleJob(final SaturnExecutionContext shardingContext) { final Map<Integer, SaturnJobReturn> retMap = new HashMap<Integer, SaturnJobReturn>(); final String jobName = shardingContext.getJobName(); final int timeoutSeconds = getTimeoutSeconds(); ExecutorService executorService = getExecutorService(); futureTaskMap = new HashMap<Integer, ShardingItemFutureTask>(); // 处理自定义参数 String jobParameter = shardingContext.getJobParameter(); // shardingItemParameters为参数表解析出来的Key/Value值 Map<Integer, String> shardingItemParameters = shardingContext.getShardingItemParameters(); for (final Entry<Integer, String> shardingItem : shardingItemParameters.entrySet()) { final Integer key = shardingItem.getKey(); try { String jobValue = shardingItem.getValue(); final String itemVal = getRealItemValue(jobParameter, jobValue); // 作业分片的对应值 ShardingItemFutureTask shardingItemFutureTask = new ShardingItemFutureTask(createCallable(jobName, key, itemVal, timeoutSeconds, shardingContext, this),null); shardingItemFutureTask.setExecutorService(executorService); Future<?> callFuture = executorService.submit(shardingItemFutureTask); if (timeoutSeconds > 0) { TimeoutSchedulerExecutor.scheduleTimeoutJob(shardingContext.getExecutorName(), timeoutSeconds, shardingItemFutureTask); } shardingItemFutureTask.setCallFuture(callFuture); futureTaskMap.put(key, shardingItemFutureTask); } catch (Throwable t) { log.error(String.format(SaturnConstant.ERROR_LOG_FORMAT, jobName, t.getMessage()), t); retMap.put(key, new SaturnJobReturn(SaturnSystemReturnCode.SYSTEM_FAIL, t.getMessage(), SaturnSystemErrorGroup.FAIL)); } } for (Entry<Integer, ShardingItemFutureTask> entry : futureTaskMap.entrySet()) { Integer item = entry.getKey(); ShardingItemFutureTask futureTask = entry.getValue(); try { futureTask.getCallFuture().get(); } catch (Exception e) { log.error(String.format(SaturnConstant.ERROR_LOG_FORMAT, jobName, e.getMessage()), e); retMap.put(item, new SaturnJobReturn(SaturnSystemReturnCode.SYSTEM_FAIL, e.getMessage(), SaturnSystemErrorGroup.FAIL)); continue; } retMap.put(item, futureTask.getCallable().getSaturnJobReturn()); } return retMap; } @Override public void abort() { super.abort(); forceStop(); } @Override public void forceStop() { super.forceStop(); if (futureTaskMap != null) { for (ShardingItemFutureTask shardingItemFutureTask : futureTaskMap.values()) { JavaShardingItemCallable shardingItemCallable = shardingItemFutureTask.getCallable(); Thread currentThread = shardingItemCallable.getCurrentThread(); if (currentThread != null) { try { log.info("[{}] msg=force stop {} - {}", jobName, shardingItemCallable.getJobName(), shardingItemCallable.getItem()); if (shardingItemCallable.forceStop()) { ShardingItemFutureTask.killRunningBusinessThread(shardingItemFutureTask); } } catch (Throwable t) { log.error(String.format(SaturnConstant.ERROR_LOG_FORMAT, jobName, t.getMessage()), t); } } } } } @Override public SaturnJobReturn doExecution(String jobName, Integer key, String value, SaturnExecutionContext shardingContext, JavaShardingItemCallable callable) throws Throwable{ return handleJavaJob(jobName, key, value, shardingContext,callable); } public SaturnJobReturn handleJavaJob(String jobName, Integer key, String value, SaturnExecutionContext shardingContext, JavaShardingItemCallable callable) throws Throwable { String jobClass = shardingContext.getJobConfiguration().getJobClass(); log.info("[{}] msg=Running SaturnJavaJob, jobClass is {} ", jobName, jobClass); try { if( jobBusinessInstance == null){ throw new JobException("the jobClass is not found"); } ClassLoader jobClassLoader = saturnExecutorService.getJobClassLoader(); ClassLoader executorClassLoader = saturnExecutorService.getExecutorClassLoader(); Thread.currentThread().setContextClassLoader(jobClassLoader); try { Class<?> saturnJobExecutionContextClazz = jobClassLoader .loadClass(SaturnJobExecutionContext.class.getCanonicalName()); Object ret = jobBusinessInstance.getClass() .getMethod("handleJavaJob", String.class, Integer.class, String.class, saturnJobExecutionContextClazz) .invoke(jobBusinessInstance, jobName, key, value, callable.getContextForJob(jobClassLoader)); SaturnJobReturn saturnJobReturn = (SaturnJobReturn) JavaShardingItemCallable.cloneObject(ret, executorClassLoader); if(saturnJobReturn != null) { callable.setBusinessReturned(true); } return saturnJobReturn; } finally { Thread.currentThread().setContextClassLoader(executorClassLoader); } } catch (Exception e) { if (e.getCause() instanceof ThreadDeath) { throw e.getCause(); } String message = logBusinessExceptionIfNecessary(jobName, e); return new SaturnJobReturn(SaturnSystemReturnCode.USER_FAIL, message, SaturnSystemErrorGroup.FAIL); } } public void postTimeout(String jobName, Integer key, String value, SaturnExecutionContext shardingContext, JavaShardingItemCallable callable) { String jobClass = shardingContext.getJobConfiguration().getJobClass(); log.info("[{}] msg=SaturnJavaJob onTimeout, jobClass is {} ", jobName, jobClass); try { if( jobBusinessInstance == null){ throw new JobException("the jobClass is not found"); } ClassLoader executorClassLoader = saturnExecutorService.getExecutorClassLoader(); ClassLoader jobClassLoader = saturnExecutorService.getJobClassLoader(); Thread.currentThread().setContextClassLoader(jobClassLoader); try { Class<?> saturnJobExecutionContextClazz = jobClassLoader .loadClass(SaturnJobExecutionContext.class.getCanonicalName()); jobBusinessInstance.getClass() .getMethod("onTimeout", String.class, Integer.class, String.class, saturnJobExecutionContextClazz) .invoke(jobBusinessInstance, jobName, key, value, callable.getContextForJob(jobClassLoader)); } finally { Thread.currentThread().setContextClassLoader(executorClassLoader); } } catch (Exception e) { logBusinessExceptionIfNecessary(jobName, e); } } public void beforeTimeout(String jobName, Integer key, String value, SaturnExecutionContext shardingContext, JavaShardingItemCallable callable) { String jobClass = shardingContext.getJobConfiguration().getJobClass(); log.info("[{}] msg=SaturnJavaJob beforeTimeout, jobClass is {} ", jobName, jobClass); try { if( jobBusinessInstance == null){ throw new JobException("the jobClass is not found"); } ClassLoader executorClassLoader = saturnExecutorService.getExecutorClassLoader(); ClassLoader jobClassLoader = saturnExecutorService.getJobClassLoader(); Thread.currentThread().setContextClassLoader(jobClassLoader); try { Class<?> saturnJobExecutionContextClazz = jobClassLoader .loadClass(SaturnJobExecutionContext.class.getCanonicalName()); jobBusinessInstance.getClass() .getMethod("beforeTimeout", String.class, Integer.class, String.class, saturnJobExecutionContextClazz) .invoke(jobBusinessInstance, jobName, key, value, callable.getContextForJob(jobClassLoader)); } finally { Thread.currentThread().setContextClassLoader(executorClassLoader); } } catch (Exception e) { logBusinessExceptionIfNecessary(jobName, e); } } public void postForceStop(String jobName, Integer key, String value, SaturnExecutionContext shardingContext, JavaShardingItemCallable callable) { String jobClass = shardingContext.getJobConfiguration().getJobClass(); log.info("[{}] msg=SaturnJavaJob postForceStop, jobClass is {} ", jobName, jobClass); try { if( jobBusinessInstance == null){ throw new JobException("the jobClass is not found"); } ClassLoader executorClassLoader = saturnExecutorService.getExecutorClassLoader(); ClassLoader jobClassLoader = saturnExecutorService.getJobClassLoader(); Thread.currentThread().setContextClassLoader(jobClassLoader); try { Class<?> saturnJobExecutionContextClazz = jobClassLoader .loadClass(SaturnJobExecutionContext.class.getCanonicalName()); jobBusinessInstance.getClass() .getMethod("postForceStop", String.class, Integer.class, String.class, saturnJobExecutionContextClazz) .invoke(jobBusinessInstance, jobName, key, value, callable.getContextForJob(jobClassLoader)); } finally { Thread.currentThread().setContextClassLoader(executorClassLoader); } } catch (Exception e) { logBusinessExceptionIfNecessary(jobName, e); } } @Override public void onForceStop(int item) { } @Override public void onTimeout(int item) { } }