Browse Source

交互重构

xueli.xue 9 years ago
parent
commit
0e80541360
25 changed files with 11223 additions and 284 deletions
  1. 0 0
      doc/db/tables_xxl_job.sql
  2. 22 0
      xxl-job-admin/src/main/java/com/xxl/job/controller/JobCodeController.java
  3. 32 41
      xxl-job-admin/src/main/java/com/xxl/job/controller/JobInfoController.java
  4. 93 92
      xxl-job-admin/src/main/java/com/xxl/job/service/job/LocalNomalJobBean.java
  5. 29 28
      xxl-job-admin/src/main/java/com/xxl/job/service/job/impl/DemoConcurrentJobBean.java
  6. 31 30
      xxl-job-admin/src/main/java/com/xxl/job/service/job/impl/DemoNomalJobBean.java
  7. 1 1
      xxl-job-admin/src/main/resources/jdbc.properties
  8. 1 1
      xxl-job-admin/src/main/webapp/WEB-INF/template/common/common.exception.ftl
  9. 4 2
      xxl-job-admin/src/main/webapp/WEB-INF/template/help.ftl
  10. 105 0
      xxl-job-admin/src/main/webapp/WEB-INF/template/jobcode/index.ftl
  11. 29 45
      xxl-job-admin/src/main/webapp/WEB-INF/template/jobinfo/index.ftl
  12. 4 2
      xxl-job-admin/src/main/webapp/WEB-INF/template/joblog/index.ftl
  13. 1 1
      xxl-job-admin/src/main/webapp/static/js/common.1.js
  14. 16 0
      xxl-job-admin/src/main/webapp/static/js/jobcode.index.1.js
  15. 18 35
      xxl-job-admin/src/main/webapp/static/js/jobinfo.index.1.js
  16. 41 0
      xxl-job-admin/src/main/webapp/static/plugins/codemirror/addon/hint/anyword-hint.js
  17. 38 0
      xxl-job-admin/src/main/webapp/static/plugins/codemirror/addon/hint/show-hint.css
  18. 447 0
      xxl-job-admin/src/main/webapp/static/plugins/codemirror/addon/hint/show-hint.js
  19. 338 0
      xxl-job-admin/src/main/webapp/static/plugins/codemirror/lib/codemirror.css
  20. 8895 0
      xxl-job-admin/src/main/webapp/static/plugins/codemirror/lib/codemirror.js
  21. 781 0
      xxl-job-admin/src/main/webapp/static/plugins/codemirror/mode/clike/clike.js
  22. 51 0
      xxl-job-admin/src/main/webapp/static/plugins/codemirror/mode/clike/test.js
  23. 230 0
      xxl-job-admin/src/main/webapp/static/plugins/codemirror/mode/groovy/groovy.js
  24. 10 6
      xxl-job-client/src/main/java/com/xxl/job/client/handler/HandlerThread.java
  25. 6 0
      xxl-job-client/src/main/java/com/xxl/job/client/util/HttpUtil.java

doc/db/tables_xxl_log.sql → doc/db/tables_xxl_job.sql


+ 22 - 0
xxl-job-admin/src/main/java/com/xxl/job/controller/JobCodeController.java

@@ -0,0 +1,22 @@
+package com.xxl.job.controller;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+/**
+ * job code controller
+ * @author xuxueli 2015-12-19 16:13:16
+ */
+@Controller
+@RequestMapping("/jobcode")
+public class JobCodeController {
+
+	@RequestMapping
+	public String index(Model model, HttpServletRequest request) {
+		return "jobcode/index";
+	}
+	
+}

+ 32 - 41
xxl-job-admin/src/main/java/com/xxl/job/controller/JobInfoController.java

@@ -1,6 +1,5 @@
 package com.xxl.job.controller;
 package com.xxl.job.controller;
 
 
-import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashMap;
 import java.util.List;
 import java.util.List;
 import java.util.Map;
 import java.util.Map;
@@ -9,7 +8,6 @@ import javax.annotation.Resource;
 
 
 import org.apache.commons.lang.StringUtils;
 import org.apache.commons.lang.StringUtils;
 import org.quartz.CronExpression;
 import org.quartz.CronExpression;
-import org.quartz.Job;
 import org.quartz.SchedulerException;
 import org.quartz.SchedulerException;
 import org.springframework.stereotype.Controller;
 import org.springframework.stereotype.Controller;
 import org.springframework.ui.Model;
 import org.springframework.ui.Model;
@@ -25,14 +23,11 @@ import com.xxl.job.core.model.XxlJobInfo;
 import com.xxl.job.core.util.DynamicSchedulerUtil;
 import com.xxl.job.core.util.DynamicSchedulerUtil;
 import com.xxl.job.dao.IXxlJobInfoDao;
 import com.xxl.job.dao.IXxlJobInfoDao;
 import com.xxl.job.service.job.RemoteHttpJobBean;
 import com.xxl.job.service.job.RemoteHttpJobBean;
-import com.xxl.job.service.job.impl.DemoConcurrentJobBean;
-import com.xxl.job.service.job.impl.DemoNomalJobBean;
 
 
 /**
 /**
  * index controller
  * index controller
  * @author xuxueli 2015-12-19 16:13:16
  * @author xuxueli 2015-12-19 16:13:16
  */
  */
-@SuppressWarnings("unchecked")
 @Controller
 @Controller
 @RequestMapping("/jobinfo")
 @RequestMapping("/jobinfo")
 public class JobInfoController {
 public class JobInfoController {
@@ -40,19 +35,8 @@ public class JobInfoController {
 	@Resource
 	@Resource
 	private IXxlJobInfoDao xxlJobInfoDao;
 	private IXxlJobInfoDao xxlJobInfoDao;
 	
 	
-	// remote job bean
-	public static Class <? extends Job> remoteJobBean = RemoteHttpJobBean.class;
-	// loacal job bean
-	public static List<Class <? extends Job>> localJobBeanList = new ArrayList<Class<? extends Job>>();
-	static{
-		localJobBeanList.add((Class<? extends Job>) DemoNomalJobBean.class);
-		localJobBeanList.add((Class<? extends Job>) DemoConcurrentJobBean.class);
-	}
-	
 	@RequestMapping
 	@RequestMapping
 	public String index(Model model) {
 	public String index(Model model) {
-		model.addAttribute("localJobBeanList", localJobBeanList);			// 本地任务-列表
-		model.addAttribute("remoteJobBean", remoteJobBean);	// 远程任务-jobBean
 		model.addAttribute("JobGroupList", JobGroupEnum.values());			// 任务组列表
 		model.addAttribute("JobGroupList", JobGroupEnum.values());			// 任务组列表
 		return "jobinfo/index";
 		return "jobinfo/index";
 	}
 	}
@@ -84,8 +68,8 @@ public class JobInfoController {
 	
 	
 	@RequestMapping("/add")
 	@RequestMapping("/add")
 	@ResponseBody
 	@ResponseBody
-	public ReturnT<String> add(String jobGroup, String jobName, String jobCron, String jobDesc, String jobClass,
-			String handler_params, String handler_address, String handler_name, 
+	public ReturnT<String> add(String jobGroup, String jobName, String jobCron, String jobDesc,
+			String handler_address, String handler_name, String handler_params, 
 			String author, String alarmEmail, int alarmThreshold) {
 			String author, String alarmEmail, int alarmThreshold) {
 		
 		
 		// valid
 		// valid
@@ -101,25 +85,11 @@ public class JobInfoController {
 		if (StringUtils.isBlank(jobDesc)) {
 		if (StringUtils.isBlank(jobDesc)) {
 			return new ReturnT<String>(500, "请输入“任务描述”");
 			return new ReturnT<String>(500, "请输入“任务描述”");
 		}
 		}
-		Class<? extends Job> jobClass_ = null;
-		try {
-			Class<?> clazz = Class.forName(jobClass);
-			if (clazz!=null) {
-				jobClass_ = (Class<? extends Job>) clazz;
-			}
-		} catch (ClassNotFoundException e1) {
-			e1.printStackTrace();
+		if (StringUtils.isBlank(handler_address)) {
+			return new ReturnT<String>(500, "请输入“机器地址”");
 		}
 		}
-		if (jobClass_ == null) {
-			return new ReturnT<String>(500, "请选择“JobBean”");
-		}
-		if (jobClass_.getClass().getName().equals(remoteJobBean.getName())) {
-			if (StringUtils.isBlank(handler_address)) {
-				return new ReturnT<String>(500, "请输入“远程-机器地址”");
-			}
-			if (StringUtils.isBlank(handler_name)) {
-				return new ReturnT<String>(500, "请输入“远程-执行器”");
-			}
+		if (StringUtils.isBlank(handler_name)) {
+			return new ReturnT<String>(500, "请输入“执行器”");
 		}
 		}
 		if (StringUtils.isBlank(author)) {
 		if (StringUtils.isBlank(author)) {
 			return new ReturnT<String>(500, "请输入“负责人”");
 			return new ReturnT<String>(500, "请输入“负责人”");
@@ -127,6 +97,9 @@ public class JobInfoController {
 		if (StringUtils.isBlank(alarmEmail)) {
 		if (StringUtils.isBlank(alarmEmail)) {
 			return new ReturnT<String>(500, "请输入“报警邮件”");
 			return new ReturnT<String>(500, "请输入“报警邮件”");
 		}
 		}
+		if (alarmThreshold < 0) {
+			alarmThreshold = 0;
+		}
 		
 		
 		try {
 		try {
 			if (DynamicSchedulerUtil.checkExists(jobName, jobGroup)) {
 			if (DynamicSchedulerUtil.checkExists(jobName, jobGroup)) {
@@ -139,9 +112,9 @@ public class JobInfoController {
 		
 		
 		// parse jobDataMap
 		// parse jobDataMap
 		HashMap<String, String> jobDataMap = new HashMap<String, String>();
 		HashMap<String, String> jobDataMap = new HashMap<String, String>();
-		jobDataMap.put(HandlerRepository.HANDLER_PARAMS, handler_params);
 		jobDataMap.put(HandlerRepository.HANDLER_ADDRESS, handler_address);
 		jobDataMap.put(HandlerRepository.HANDLER_ADDRESS, handler_address);
 		jobDataMap.put(HandlerRepository.HANDLER_NAME, handler_name);
 		jobDataMap.put(HandlerRepository.HANDLER_NAME, handler_name);
+		jobDataMap.put(HandlerRepository.HANDLER_PARAMS, handler_params);
 		
 		
 		// Backup to the database
 		// Backup to the database
 		XxlJobInfo jobInfo = new XxlJobInfo();
 		XxlJobInfo jobInfo = new XxlJobInfo();
@@ -149,7 +122,7 @@ public class JobInfoController {
 		jobInfo.setJobName(jobName);
 		jobInfo.setJobName(jobName);
 		jobInfo.setJobCron(jobCron);
 		jobInfo.setJobCron(jobCron);
 		jobInfo.setJobDesc(jobDesc);
 		jobInfo.setJobDesc(jobDesc);
-		jobInfo.setJobClass(jobClass);
+		jobInfo.setJobClass(RemoteHttpJobBean.class.getName());
 		jobInfo.setJobData(JacksonUtil.writeValueAsString(jobDataMap));
 		jobInfo.setJobData(JacksonUtil.writeValueAsString(jobDataMap));
 		jobInfo.setAuthor(author);
 		jobInfo.setAuthor(author);
 		jobInfo.setAlarmEmail(alarmEmail);
 		jobInfo.setAlarmEmail(alarmEmail);
@@ -173,8 +146,8 @@ public class JobInfoController {
 	
 	
 	@RequestMapping("/reschedule")
 	@RequestMapping("/reschedule")
 	@ResponseBody
 	@ResponseBody
-	public ReturnT<String> reschedule(String jobGroup, String jobName, String jobCron, String jobDesc, String jobClass,
-			String handler_params, String handler_address, String handler_name, 
+	public ReturnT<String> reschedule(String jobGroup, String jobName, String jobCron, String jobDesc,
+			String handler_address, String handler_name, String handler_params, 
 			String author, String alarmEmail, int alarmThreshold) {
 			String author, String alarmEmail, int alarmThreshold) {
 		
 		
 		// valid
 		// valid
@@ -187,12 +160,30 @@ public class JobInfoController {
 		if (!CronExpression.isValidExpression(jobCron)) {
 		if (!CronExpression.isValidExpression(jobCron)) {
 			return new ReturnT<String>(500, "“corn”不合法");
 			return new ReturnT<String>(500, "“corn”不合法");
 		}
 		}
+		if (StringUtils.isBlank(jobDesc)) {
+			return new ReturnT<String>(500, "请输入“任务描述”");
+		}
+		if (StringUtils.isBlank(handler_address)) {
+			return new ReturnT<String>(500, "请输入“机器地址”");
+		}
+		if (StringUtils.isBlank(handler_name)) {
+			return new ReturnT<String>(500, "请输入“执行器”");
+		}
+		if (StringUtils.isBlank(author)) {
+			return new ReturnT<String>(500, "请输入“负责人”");
+		}
+		if (StringUtils.isBlank(alarmEmail)) {
+			return new ReturnT<String>(500, "请输入“报警邮件”");
+		}
+		if (alarmThreshold < 0) {
+			alarmThreshold = 0;
+		}
 		
 		
 		// parse jobDataMap
 		// parse jobDataMap
 		HashMap<String, String> jobDataMap = new HashMap<String, String>();
 		HashMap<String, String> jobDataMap = new HashMap<String, String>();
-		jobDataMap.put(HandlerRepository.HANDLER_PARAMS, handler_params);
 		jobDataMap.put(HandlerRepository.HANDLER_ADDRESS, handler_address);
 		jobDataMap.put(HandlerRepository.HANDLER_ADDRESS, handler_address);
 		jobDataMap.put(HandlerRepository.HANDLER_NAME, handler_name);
 		jobDataMap.put(HandlerRepository.HANDLER_NAME, handler_name);
+		jobDataMap.put(HandlerRepository.HANDLER_PARAMS, handler_params);
 		
 		
 		XxlJobInfo jobInfo = xxlJobInfoDao.load(jobGroup, jobName);
 		XxlJobInfo jobInfo = xxlJobInfoDao.load(jobGroup, jobName);
 		jobInfo.setJobCron(jobCron);
 		jobInfo.setJobCron(jobCron);

+ 93 - 92
xxl-job-admin/src/main/java/com/xxl/job/service/job/LocalNomalJobBean.java

@@ -1,92 +1,93 @@
-package com.xxl.job.service.job;
-
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.commons.lang.StringUtils;
-import org.quartz.JobExecutionContext;
-import org.quartz.JobExecutionException;
-import org.quartz.JobKey;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.scheduling.quartz.QuartzJobBean;
-
-import com.xxl.job.client.handler.HandlerRepository;
-import com.xxl.job.client.util.HttpUtil.RemoteCallBack;
-import com.xxl.job.client.util.JacksonUtil;
-import com.xxl.job.core.model.XxlJobInfo;
-import com.xxl.job.core.model.XxlJobLog;
-import com.xxl.job.core.thread.JobMonitorHelper;
-import com.xxl.job.core.util.DynamicSchedulerUtil;
-
-/**
- * http job bean
- * @author xuxueli 2015-12-17 18:20:34
- */
-public abstract class LocalNomalJobBean extends QuartzJobBean {
-	private static Logger logger = LoggerFactory.getLogger(LocalNomalJobBean.class);
-
-	@Override
-	protected void executeInternal(JobExecutionContext context)
-			throws JobExecutionException {
-		JobKey jobKey = context.getTrigger().getJobKey();
-		
-		XxlJobInfo jobInfo = DynamicSchedulerUtil.xxlJobInfoDao.load(jobKey.getGroup(), jobKey.getName());
-		@SuppressWarnings("unchecked")
-		HashMap<String, String> jobDataMap = (HashMap<String, String>) JacksonUtil.readValueRefer(jobInfo.getJobData(), Map.class);
-		
-		// save log
-		XxlJobLog jobLog = new XxlJobLog();
-		jobLog.setJobGroup(jobInfo.getJobGroup());
-		jobLog.setJobName(jobInfo.getJobName());
-		jobLog.setJobCron(jobInfo.getJobCron());
-		jobLog.setJobDesc(jobInfo.getJobDesc());
-		jobLog.setJobClass(jobInfo.getJobClass());
-		jobLog.setJobData(jobInfo.getJobData());
-		
-		jobLog.setJobClass(RemoteHttpJobBean.class.getName());
-		jobLog.setJobData(jobInfo.getJobData());
-		DynamicSchedulerUtil.xxlJobLogDao.save(jobLog);
-		logger.info(">>>>>>>>>>> xxl-job trigger start, jobLog:{}", jobLog);
-		
-		// trigger request
-		String handler_params = jobDataMap.get(HandlerRepository.HANDLER_PARAMS);
-		String[] handlerParams = null;
-		if (StringUtils.isNotBlank(handler_params)) {
-			handlerParams = handler_params.split(",");
-		}
-		
-		jobLog.setTriggerTime(new Date());
-		jobLog.setTriggerStatus(RemoteCallBack.SUCCESS);
-		jobLog.setTriggerMsg(null);
-		
-		try {
-			Object responseMsg = this.handle(handlerParams);
-			
-			jobLog.setHandleTime(new Date());
-			jobLog.setHandleStatus(RemoteCallBack.SUCCESS);
-			jobLog.setHandleMsg(JacksonUtil.writeValueAsString(responseMsg));
-		} catch (Exception e) {
-			logger.info("HandlerThread Exception:", e);
-			StringWriter out = new StringWriter();
-			e.printStackTrace(new PrintWriter(out));
-			
-			jobLog.setHandleTime(new Date());
-			jobLog.setHandleStatus(RemoteCallBack.FAIL);
-			jobLog.setHandleMsg(out.toString());
-		}
-		
-		// update trigger info
-		DynamicSchedulerUtil.xxlJobLogDao.updateTriggerInfo(jobLog);
-		DynamicSchedulerUtil.xxlJobLogDao.updateHandleInfo(jobLog);
-		JobMonitorHelper.monitor(jobLog.getId());
-		logger.info(">>>>>>>>>>> xxl-job trigger end, jobLog.id:{}, jobLog:{}", jobLog.getId(), jobLog);
-		
-    }
-	
-	public abstract Object handle(String... param);
-	
-}
+//package com.xxl.job.service.job;
+//
+//import java.io.PrintWriter;
+//import java.io.StringWriter;
+//import java.util.Date;
+//import java.util.HashMap;
+//import java.util.Map;
+//
+//import org.apache.commons.lang.StringUtils;
+//import org.quartz.JobExecutionContext;
+//import org.quartz.JobExecutionException;
+//import org.quartz.JobKey;
+//import org.slf4j.Logger;
+//import org.slf4j.LoggerFactory;
+//import org.springframework.scheduling.quartz.QuartzJobBean;
+//
+//import com.xxl.job.client.handler.HandlerRepository;
+//import com.xxl.job.client.util.HttpUtil.RemoteCallBack;
+//import com.xxl.job.client.util.JacksonUtil;
+//import com.xxl.job.core.model.XxlJobInfo;
+//import com.xxl.job.core.model.XxlJobLog;
+//import com.xxl.job.core.thread.JobMonitorHelper;
+//import com.xxl.job.core.util.DynamicSchedulerUtil;
+//
+///**
+// * http job bean
+// * @author xuxueli 2015-12-17 18:20:34
+// */
+//@Deprecated
+//public abstract class LocalNomalJobBean extends QuartzJobBean {
+//	private static Logger logger = LoggerFactory.getLogger(LocalNomalJobBean.class);
+//
+//	@Override
+//	protected void executeInternal(JobExecutionContext context)
+//			throws JobExecutionException {
+//		JobKey jobKey = context.getTrigger().getJobKey();
+//		
+//		XxlJobInfo jobInfo = DynamicSchedulerUtil.xxlJobInfoDao.load(jobKey.getGroup(), jobKey.getName());
+//		@SuppressWarnings("unchecked")
+//		HashMap<String, String> jobDataMap = (HashMap<String, String>) JacksonUtil.readValueRefer(jobInfo.getJobData(), Map.class);
+//		
+//		// save log
+//		XxlJobLog jobLog = new XxlJobLog();
+//		jobLog.setJobGroup(jobInfo.getJobGroup());
+//		jobLog.setJobName(jobInfo.getJobName());
+//		jobLog.setJobCron(jobInfo.getJobCron());
+//		jobLog.setJobDesc(jobInfo.getJobDesc());
+//		jobLog.setJobClass(jobInfo.getJobClass());
+//		jobLog.setJobData(jobInfo.getJobData());
+//		
+//		jobLog.setJobClass(RemoteHttpJobBean.class.getName());
+//		jobLog.setJobData(jobInfo.getJobData());
+//		DynamicSchedulerUtil.xxlJobLogDao.save(jobLog);
+//		logger.info(">>>>>>>>>>> xxl-job trigger start, jobLog:{}", jobLog);
+//		
+//		// trigger request
+//		String handler_params = jobDataMap.get(HandlerRepository.HANDLER_PARAMS);
+//		String[] handlerParams = null;
+//		if (StringUtils.isNotBlank(handler_params)) {
+//			handlerParams = handler_params.split(",");
+//		}
+//		
+//		jobLog.setTriggerTime(new Date());
+//		jobLog.setTriggerStatus(RemoteCallBack.SUCCESS);
+//		jobLog.setTriggerMsg(null);
+//		
+//		try {
+//			Object responseMsg = this.handle(handlerParams);
+//			
+//			jobLog.setHandleTime(new Date());
+//			jobLog.setHandleStatus(RemoteCallBack.SUCCESS);
+//			jobLog.setHandleMsg(JacksonUtil.writeValueAsString(responseMsg));
+//		} catch (Exception e) {
+//			logger.info("HandlerThread Exception:", e);
+//			StringWriter out = new StringWriter();
+//			e.printStackTrace(new PrintWriter(out));
+//			
+//			jobLog.setHandleTime(new Date());
+//			jobLog.setHandleStatus(RemoteCallBack.FAIL);
+//			jobLog.setHandleMsg(out.toString());
+//		}
+//		
+//		// update trigger info
+//		DynamicSchedulerUtil.xxlJobLogDao.updateTriggerInfo(jobLog);
+//		DynamicSchedulerUtil.xxlJobLogDao.updateHandleInfo(jobLog);
+//		JobMonitorHelper.monitor(jobLog.getId());
+//		logger.info(">>>>>>>>>>> xxl-job trigger end, jobLog.id:{}, jobLog:{}", jobLog.getId(), jobLog);
+//		
+//    }
+//	
+//	public abstract Object handle(String... param);
+//	
+//}

+ 29 - 28
xxl-job-admin/src/main/java/com/xxl/job/service/job/impl/DemoConcurrentJobBean.java

@@ -1,28 +1,29 @@
-package com.xxl.job.service.job.impl;
-
-import java.util.concurrent.TimeUnit;
-
-import org.quartz.DisallowConcurrentExecution;
-
-import com.xxl.job.service.job.LocalNomalJobBean;
-
-/**
- * demo job bean for no-concurrent
- * @author xuxueli 2016-3-12 14:25:14
- */
-@DisallowConcurrentExecution	// 串行;线程数要多配置几个,否则不生效;
-public class DemoConcurrentJobBean extends LocalNomalJobBean {
-
-	@Override
-	public Object handle(String... param) {
-		
-		try {
-			TimeUnit.SECONDS.sleep(10);
-		} catch (InterruptedException e) {
-			e.printStackTrace();
-		}
-		
-		return false;
-	}
-
-}
+//package com.xxl.job.service.job.impl;
+//
+//import java.util.concurrent.TimeUnit;
+//
+//import org.quartz.DisallowConcurrentExecution;
+//
+//import com.xxl.job.service.job.LocalNomalJobBean;
+//
+///**
+// * demo job bean for no-concurrent
+// * @author xuxueli 2016-3-12 14:25:14
+// */
+//@Deprecated
+//@DisallowConcurrentExecution	// 串行;线程数要多配置几个,否则不生效;
+//public class DemoConcurrentJobBean extends LocalNomalJobBean {
+//
+//	@Override
+//	public Object handle(String... param) {
+//		
+//		try {
+//			TimeUnit.SECONDS.sleep(10);
+//		} catch (InterruptedException e) {
+//			e.printStackTrace();
+//		}
+//		
+//		return false;
+//	}
+//
+//}

+ 31 - 30
xxl-job-admin/src/main/java/com/xxl/job/service/job/impl/DemoNomalJobBean.java

@@ -1,30 +1,31 @@
-package com.xxl.job.service.job.impl;
-
-import java.util.concurrent.TimeUnit;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.xxl.job.service.job.LocalNomalJobBean;
-
-/**
- * demo job bean for concurrent
- * @author xuxueli 2016-3-12 14:25:57
- */
-public class DemoNomalJobBean extends LocalNomalJobBean {
-	private static Logger Logger = LoggerFactory.getLogger(DemoNomalJobBean.class);
-	
-	@Override
-	public Object handle(String... param) {
-		Logger.info("DemoNomalJobBean run :" + param);
-		
-		try {
-			TimeUnit.SECONDS.sleep(10);
-		} catch (InterruptedException e) {
-			e.printStackTrace();
-		}
-		
-		return false;
-	}
-
-}
+//package com.xxl.job.service.job.impl;
+//
+//import java.util.concurrent.TimeUnit;
+//
+//import org.slf4j.Logger;
+//import org.slf4j.LoggerFactory;
+//
+//import com.xxl.job.service.job.LocalNomalJobBean;
+//
+///**
+// * demo job bean for concurrent
+// * @author xuxueli 2016-3-12 14:25:57
+// */
+//@Deprecated
+//public class DemoNomalJobBean extends LocalNomalJobBean {
+//	private static Logger Logger = LoggerFactory.getLogger(DemoNomalJobBean.class);
+//	
+//	@Override
+//	public Object handle(String... param) {
+//		Logger.info("DemoNomalJobBean run :" + param);
+//		
+//		try {
+//			TimeUnit.SECONDS.sleep(10);
+//		} catch (InterruptedException e) {
+//			e.printStackTrace();
+//		}
+//		
+//		return false;
+//	}
+//
+//}

+ 1 - 1
xxl-job-admin/src/main/resources/jdbc.properties

@@ -1,4 +1,4 @@
 c3p0.driverClass=com.mysql.jdbc.Driver
 c3p0.driverClass=com.mysql.jdbc.Driver
-c3p0.url=jdbc:mysql://localhost:3306/test?Unicode=true&amp;characterEncoding=UTF-8
+c3p0.url=jdbc:mysql://localhost:3306/xxl-job?Unicode=true&amp;characterEncoding=UTF-8
 c3p0.user=root
 c3p0.user=root
 c3p0.password=root_pwd
 c3p0.password=root_pwd

+ 1 - 1
xxl-job-admin/src/main/webapp/WEB-INF/template/common/common.exception.ftl

@@ -24,7 +24,7 @@
 	    <h1>应用程序异常</h1> 
 	    <h1>应用程序异常</h1> 
 	    <p>抱歉!您访问的页面出现异常,请稍后重试或联系管理员。</p> 
 	    <p>抱歉!您访问的页面出现异常,请稍后重试或联系管理员。</p> 
 	    <p><a href="javascript:showErr();">详 情</a> 
 	    <p><a href="javascript:showErr();">详 情</a> 
-		<a href="javascript:window.location.href='${request.contextPath}'">返 回</a> 
+		<a href="javascript:window.location.href='${request.contextPath}/'">返 回</a> 
 	    </p> 
 	    </p> 
 	    <div style="display:none;text-align: left;" id="err">${exceptionMsg}</div>
 	    <div style="display:none;text-align: left;" id="err">${exceptionMsg}</div>
 	</div>
 	</div>

+ 4 - 2
xxl-job-admin/src/main/webapp/WEB-INF/template/help.ftl

@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <!DOCTYPE html>
 <html>
 <html>
 <head>
 <head>
-  	<title>调度中心</title>
+  	<title>任务调度中心</title>
   	<#import "/common/common.macro.ftl" as netCommon>
   	<#import "/common/common.macro.ftl" as netCommon>
 	<@netCommon.commonStyle />
 	<@netCommon.commonStyle />
 </head>
 </head>
@@ -16,11 +16,13 @@
 	<div class="content-wrapper">
 	<div class="content-wrapper">
 		<!-- Content Header (Page header) -->
 		<!-- Content Header (Page header) -->
 		<section class="content-header">
 		<section class="content-header">
-			<h1>使用教程<small>调度管理平台</small></h1>
+			<h1>任务调度中心<small>使用教程</small></h1>
+			<!--
 			<ol class="breadcrumb">
 			<ol class="breadcrumb">
 				<li><a><i class="fa fa-dashboard"></i>调度中心</a></li>
 				<li><a><i class="fa fa-dashboard"></i>调度中心</a></li>
 				<li class="active">使用教程</li>
 				<li class="active">使用教程</li>
 			</ol>
 			</ol>
+			-->
 		</section>
 		</section>
 
 
 		<!-- Main content -->
 		<!-- Main content -->

+ 105 - 0
xxl-job-admin/src/main/webapp/WEB-INF/template/jobcode/index.ftl

@@ -0,0 +1,105 @@
+<!DOCTYPE html>
+<html>
+<head>
+  	<title>任务调度中心</title>
+  	<#import "/common/common.macro.ftl" as netCommon>
+	<@netCommon.commonStyle />
+	<link rel="stylesheet" href="${request.contextPath}/static/plugins/codemirror/lib/codemirror.css">
+	<link rel="stylesheet" href="${request.contextPath}/static/plugins/codemirror/addon/hint/show-hint.css">
+	<style type="text/css">
+		.CodeMirror {
+      		border: 0px solid black;
+      		font-size:16px;
+      		height: 95%;
+      		row:200;
+		}
+    </style>
+</head>
+<body>
+
+<div class="wrapper">
+	
+	<div class="content-wrapper">
+		<!-- Content Header (Page header) -->
+		<section class="content-header">
+			<h1>任务调度中心<small>任务CODE管理</small></h1>
+		</section>
+		
+		<!-- Main content -->
+	    <section class="content">
+	    
+	    	<div class="row">
+	    		<div class="col-xs-4">
+					<div class="input-group margin">
+                    	<div class="input-group-btn">
+                      		<button type="button" class="btn btn-default">版本回溯</button>
+                    	</div>
+                    	<select class="form-control" id="jobGroup" >
+            				<option value="999" >逻辑调整版本C</option>
+            				<option value="999" >逻辑调整版本B</option>
+            				<option value="999" >逻辑调整版本A</option>
+            				<option value="999" >代码初始化</option>
+	                  	</select>
+					</div>
+	            </div>
+	            <div class="col-xs-4">
+	            	<div class="input-group margin">
+                    	<div class="input-group-btn">
+                      		<button type="button" class="btn btn-default">Action</button>
+                    	</div>
+                    	<input type="text" class="form-control" id="jobName" value="${jobName}" autocomplete="on" >
+                  	</div>
+	            </div>
+	            <div class="col-xs-2">
+	            	<div class="input-group margin">
+                    	<div class="input-group-btn">
+                      		<button type="button" class="btn btn-info">保存</button>
+                    	</div>
+                  	</div>
+	            </div>
+          	</div>
+	    	
+			<div class="row">
+				<div class="col-xs-12">
+					<div class="box">
+						<textarea id="editor" >
+package com.xxl.job.service.handler;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.xxl.job.client.handler.IJobHandler;
+
+public class DemoJobHandler extends IJobHandler {
+	private static transient Logger logger = LoggerFactory.getLogger(DemoJobHandler.class);
+	
+	@Override
+	public JobHandleStatus handle(String... params) throws Exception {
+		logger.info(" ... params:" + params);
+		for (int i = 0; i < 5; i++) {
+			TimeUnit.SECONDS.sleep(1);
+			logger.info("handler run:{}", i);
+		}
+		return JobHandleStatus.SUCCESS;
+	}
+}
+						</textarea>
+					</div>
+				</div>
+			</div>
+	    </section>
+	</div>
+	
+	<!-- footer -->
+	<@netCommon.commonFooter />
+</div>
+	
+<@netCommon.comAlert />
+<@netCommon.commonScript />
+<script src="${request.contextPath}/static/plugins/codemirror/lib/codemirror.js"></script>
+<script src="${request.contextPath}/static/plugins/codemirror/mode/clike/clike.js"></script>
+<script src="${request.contextPath}/static/plugins/codemirror/addon/hint/show-hint.js"></script>
+<script src="${request.contextPath}/static/plugins/codemirror/addon/hint/anyword-hint.js"></script>
+<script src="${request.contextPath}/static/js/jobcode.index.1.js"></script>
+</body>
+</html>

+ 29 - 45
xxl-job-admin/src/main/webapp/WEB-INF/template/jobinfo/index.ftl

@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <!DOCTYPE html>
 <html>
 <html>
 <head>
 <head>
-  	<title>调度中心</title>
+  	<title>任务调度中心</title>
   	<#import "/common/common.macro.ftl" as netCommon>
   	<#import "/common/common.macro.ftl" as netCommon>
 	<@netCommon.commonStyle />
 	<@netCommon.commonStyle />
 	<!-- DataTables -->
 	<!-- DataTables -->
@@ -19,11 +19,13 @@
 	<div class="content-wrapper">
 	<div class="content-wrapper">
 		<!-- Content Header (Page header) -->
 		<!-- Content Header (Page header) -->
 		<section class="content-header">
 		<section class="content-header">
-			<h1>调度管理<small>调度中心</small></h1>
+			<h1>任务调度中心<small>调度管理</small></h1>
+			<!--
 			<ol class="breadcrumb">
 			<ol class="breadcrumb">
 				<li><a><i class="fa fa-dashboard"></i>调度管理</a></li>
 				<li><a><i class="fa fa-dashboard"></i>调度管理</a></li>
 				<li class="active">调度中心</li>
 				<li class="active">调度中心</li>
 			</ol>
 			</ol>
+			-->
 		</section>
 		</section>
 		
 		
 		<!-- Main content -->
 		<!-- Main content -->
@@ -104,7 +106,7 @@
          	<div class="modal-body">
          	<div class="modal-body">
 				<form class="form-horizontal form" role="form" >
 				<form class="form-horizontal form" role="form" >
 					<div class="form-group">
 					<div class="form-group">
-						<label for="firstname" class="col-sm-2 control-label">任务组</label>
+						<label for="firstname" class="col-sm-2 control-label">任务组<font color="red">*</font></label>
 						<div class="col-sm-4">
 						<div class="col-sm-4">
 							<select class="form-control" name="jobGroup" >
 							<select class="form-control" name="jobGroup" >
 		            			<#list JobGroupList as group>
 		            			<#list JobGroupList as group>
@@ -112,46 +114,31 @@
 		            			</#list>
 		            			</#list>
 		                  	</select>
 		                  	</select>
 						</div>
 						</div>
-						<label for="firstname" class="col-sm-2 control-label">任务名</label>
+						<label for="firstname" class="col-sm-2 control-label">任务名<font color="red">*</font></label>
 						<div class="col-sm-4"><input type="text" class="form-control" name="jobName" placeholder="请输入“任务名”" minlength="4" maxlength="100" ></div>
 						<div class="col-sm-4"><input type="text" class="form-control" name="jobName" placeholder="请输入“任务名”" minlength="4" maxlength="100" ></div>
 					</div>
 					</div>
 					<div class="form-group">
 					<div class="form-group">
-						<label for="lastname" class="col-sm-2 control-label">Corn</label>
+						<label for="lastname" class="col-sm-2 control-label">Corn<font color="red">*</font></label>
 						<div class="col-sm-4"><input type="text" class="form-control" name="jobCron" placeholder="请输入“Corn”" maxlength="100" ></div>
 						<div class="col-sm-4"><input type="text" class="form-control" name="jobCron" placeholder="请输入“Corn”" maxlength="100" ></div>
-						<label for="lastname" class="col-sm-2 control-label">描述</label>
+						<label for="lastname" class="col-sm-2 control-label">描述<font color="red">*</font></label>
 						<div class="col-sm-4"><input type="text" class="form-control" name="jobDesc" placeholder="请输入“描述”" maxlength="200" ></div>
 						<div class="col-sm-4"><input type="text" class="form-control" name="jobDesc" placeholder="请输入“描述”" maxlength="200" ></div>
 					</div>
 					</div>
 					<div class="form-group">
 					<div class="form-group">
-						<label for="firstname" class="col-sm-2 control-label">JobBean</label>
-						<div class="col-sm-4">
-							<select class="form-control" name="jobClass" >
-								<#if remoteJobBean?exists >
-								<option value="${remoteJobBean.name}" jobClassType="remote" >【远程任务】</option>
-								</#if>
-								<#if localJobBeanList?exists && localJobBeanList?size gt 0 >
-								<#list localJobBeanList as localJobBean>
-									<option value="${localJobBean.name}" jobClassType="local" >${localJobBean.name}</option>
-								</#list>
-								</#if>
-		                  	</select>
-						</div>
-						<label for="firstname" class="col-sm-2 control-label">执行参数</label>
-						<div class="col-sm-4"><input type="text" class="form-control" name="handler_params" placeholder="请输入“执行参数”" maxlength="100" ></div>
-					</div>
-					<div class="form-group remote_panel">
-						<label for="lastname" class="col-sm-2 control-label">远程-机器地址</label>
+						<label for="lastname" class="col-sm-2 control-label">机器地址<font color="red">*</font></label>
 						<div class="col-sm-4"><input type="text" class="form-control" name="handler_address" placeholder="请输入“远程-机器地址”" maxlength="200" ></div>
 						<div class="col-sm-4"><input type="text" class="form-control" name="handler_address" placeholder="请输入“远程-机器地址”" maxlength="200" ></div>
-						<label for="lastname" class="col-sm-2 control-label">远程-执行器</label>
+						<label for="lastname" class="col-sm-2 control-label">执行器<font color="red">*</font></label>
 						<div class="col-sm-4"><input type="text" class="form-control" name="handler_name" placeholder="请输入“远程-执行器”" maxlength="200" ></div>
 						<div class="col-sm-4"><input type="text" class="form-control" name="handler_name" placeholder="请输入“远程-执行器”" maxlength="200" ></div>
 					</div>
 					</div>
 					<div class="form-group">
 					<div class="form-group">
-						<label for="lastname" class="col-sm-2 control-label">负责人</label>
+						<label for="firstname" class="col-sm-2 control-label">执行参数<font color="black">*</font></label>
+						<div class="col-sm-4"><input type="text" class="form-control" name="handler_params" placeholder="请输入“执行参数”" maxlength="100" ></div>
+						<label for="lastname" class="col-sm-2 control-label">负责人<font color="red">*</font></label>
 						<div class="col-sm-4"><input type="text" class="form-control" name="author" placeholder="请输入“负责人”" maxlength="200" ></div>
 						<div class="col-sm-4"><input type="text" class="form-control" name="author" placeholder="请输入“负责人”" maxlength="200" ></div>
-						<label for="lastname" class="col-sm-2 control-label">报警邮件</label>
-						<div class="col-sm-4"><input type="text" class="form-control" name="alarmEmail" placeholder="请输入“报警邮件”,多个邮件地址逗号分隔" maxlength="200" ></div>
 					</div>
 					</div>
 					<div class="form-group">
 					<div class="form-group">
-						<label for="lastname" class="col-sm-2 control-label">报警阈值</label>
+						<label for="lastname" class="col-sm-2 control-label">报警邮件<font color="red">*</font></label>
+						<div class="col-sm-4"><input type="text" class="form-control" name="alarmEmail" placeholder="请输入“报警邮件”,多个邮件地址逗号分隔" maxlength="200" ></div>
+						<label for="lastname" class="col-sm-2 control-label">报警阈值<font color="red">*</font></label>
 						<div class="col-sm-4"><input type="text" class="form-control" name="alarmThreshold" placeholder="请输入“报警阈值”" maxlength="200" ></div>
 						<div class="col-sm-4"><input type="text" class="form-control" name="alarmThreshold" placeholder="请输入“报警阈值”" maxlength="200" ></div>
 					</div>
 					</div>
 					<div class="form-group">
 					<div class="form-group">
@@ -176,37 +163,34 @@
          	<div class="modal-body">
          	<div class="modal-body">
 				<form class="form-horizontal form" role="form" >
 				<form class="form-horizontal form" role="form" >
 					<div class="form-group">
 					<div class="form-group">
-						<label for="firstname" class="col-sm-2 control-label">任务组</label>
+						<label for="firstname" class="col-sm-2 control-label">任务组<font color="red">*</font></label>
 						<div class="col-sm-4"><input type="text" class="form-control" name="jobGroup" placeholder="请输入“任务组”" minlength="4" maxlength="100" readonly ></div>
 						<div class="col-sm-4"><input type="text" class="form-control" name="jobGroup" placeholder="请输入“任务组”" minlength="4" maxlength="100" readonly ></div>
-						<label for="firstname" class="col-sm-2 control-label">任务名</label>
+						<label for="firstname" class="col-sm-2 control-label">任务名<font color="red">*</font></label>
 						<div class="col-sm-4"><input type="text" class="form-control" name="jobName" placeholder="请输入“任务名”" minlength="4" maxlength="100" readonly ></div>
 						<div class="col-sm-4"><input type="text" class="form-control" name="jobName" placeholder="请输入“任务名”" minlength="4" maxlength="100" readonly ></div>
 					</div>
 					</div>
 					<div class="form-group">
 					<div class="form-group">
-						<label for="lastname" class="col-sm-2 control-label">Corn</label>
+						<label for="lastname" class="col-sm-2 control-label">Corn<font color="red">*</font></label>
 						<div class="col-sm-4"><input type="text" class="form-control" name="jobCron" placeholder="请输入“Corn”" maxlength="100" ></div>
 						<div class="col-sm-4"><input type="text" class="form-control" name="jobCron" placeholder="请输入“Corn”" maxlength="100" ></div>
-						<label for="lastname" class="col-sm-2 control-label">描述</label>
+						<label for="lastname" class="col-sm-2 control-label">描述<font color="red">*</font></label>
 						<div class="col-sm-4"><input type="text" class="form-control" name="jobDesc" placeholder="请输入“描述”" maxlength="200" ></div>
 						<div class="col-sm-4"><input type="text" class="form-control" name="jobDesc" placeholder="请输入“描述”" maxlength="200" ></div>
 					</div>
 					</div>
+					
 					<div class="form-group">
 					<div class="form-group">
-						<label for="firstname" class="col-sm-2 control-label">JobBean</label>
-						<div class="col-sm-4"><input type="text" class="form-control" name="jobClass" placeholder="请输入“执行参数”" maxlength="100" readonly ></div>
-						<label for="firstname" class="col-sm-2 control-label">执行参数</label>
-						<div class="col-sm-4"><input type="text" class="form-control" name="handler_params" placeholder="请输入“执行参数”" maxlength="100" ></div>
-					</div>
-					<div class="form-group remote_panel">
-						<label for="lastname" class="col-sm-2 control-label">远程-机器地址</label>
+						<label for="lastname" class="col-sm-2 control-label">机器地址<font color="red">*</font></label>
 						<div class="col-sm-4"><input type="text" class="form-control" name="handler_address" placeholder="请输入“远程-机器地址”" maxlength="200" ></div>
 						<div class="col-sm-4"><input type="text" class="form-control" name="handler_address" placeholder="请输入“远程-机器地址”" maxlength="200" ></div>
-						<label for="lastname" class="col-sm-2 control-label">远程-执行器</label>
+						<label for="lastname" class="col-sm-2 control-label">执行器<font color="red">*</font></label>
 						<div class="col-sm-4"><input type="text" class="form-control" name="handler_name" placeholder="请输入“远程-执行器”" maxlength="200" ></div>
 						<div class="col-sm-4"><input type="text" class="form-control" name="handler_name" placeholder="请输入“远程-执行器”" maxlength="200" ></div>
 					</div>
 					</div>
 					<div class="form-group">
 					<div class="form-group">
-						<label for="lastname" class="col-sm-2 control-label">负责人</label>
+						<label for="firstname" class="col-sm-2 control-label">执行参数<font color="black">*</font></label>
+						<div class="col-sm-4"><input type="text" class="form-control" name="handler_params" placeholder="请输入“执行参数”" maxlength="100" ></div>
+						<label for="lastname" class="col-sm-2 control-label">负责人<font color="red">*</font></label>
 						<div class="col-sm-4"><input type="text" class="form-control" name="author" placeholder="请输入“负责人”" maxlength="200" ></div>
 						<div class="col-sm-4"><input type="text" class="form-control" name="author" placeholder="请输入“负责人”" maxlength="200" ></div>
-						<label for="lastname" class="col-sm-2 control-label">报警邮件</label>
-						<div class="col-sm-4"><input type="text" class="form-control" name="alarmEmail" placeholder="请输入“报警邮件”,多个邮件地址逗号分隔" maxlength="200" ></div>
 					</div>
 					</div>
 					<div class="form-group">
 					<div class="form-group">
-						<label for="lastname" class="col-sm-2 control-label">报警阈值</label>
+						<label for="lastname" class="col-sm-2 control-label">报警邮件<font color="red">*</font></label>
+						<div class="col-sm-4"><input type="text" class="form-control" name="alarmEmail" placeholder="请输入“报警邮件”,多个邮件地址逗号分隔" maxlength="200" ></div>
+						<label for="lastname" class="col-sm-2 control-label">报警阈值<font color="red">*</font></label>
 						<div class="col-sm-4"><input type="text" class="form-control" name="alarmThreshold" placeholder="请输入“报警阈值”" maxlength="200" ></div>
 						<div class="col-sm-4"><input type="text" class="form-control" name="alarmThreshold" placeholder="请输入“报警阈值”" maxlength="200" ></div>
 					</div>
 					</div>
 					<div class="form-group">
 					<div class="form-group">

+ 4 - 2
xxl-job-admin/src/main/webapp/WEB-INF/template/joblog/index.ftl

@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <!DOCTYPE html>
 <html>
 <html>
 <head>
 <head>
-  	<title>调度日志</title>
+  	<title>任务调度中心</title>
   	<#import "/common/common.macro.ftl" as netCommon>
   	<#import "/common/common.macro.ftl" as netCommon>
 	<@netCommon.commonStyle />
 	<@netCommon.commonStyle />
 	<!-- DataTables -->
 	<!-- DataTables -->
@@ -20,11 +20,13 @@
 	<div class="content-wrapper">
 	<div class="content-wrapper">
 		<!-- Content Header (Page header) -->
 		<!-- Content Header (Page header) -->
 		<section class="content-header">
 		<section class="content-header">
-			<h1>调度日志<small>调度中心</small></h1>
+			<h1>任务调度中心<small>调度日志</small></h1>
+			<!--
 			<ol class="breadcrumb">
 			<ol class="breadcrumb">
 				<li><a><i class="fa fa-dashboard"></i>调度日志</a></li>
 				<li><a><i class="fa fa-dashboard"></i>调度日志</a></li>
 				<li class="active">调度管理</li>
 				<li class="active">调度管理</li>
 			</ol>
 			</ol>
+			-->
 		</section>
 		</section>
 		
 		
 		<!-- Main content -->
 		<!-- Main content -->

+ 1 - 1
xxl-job-admin/src/main/webapp/static/js/common.1.js

@@ -21,7 +21,7 @@ $(function(){
 			$.post(base_url + "/logout", function(data, status) {
 			$.post(base_url + "/logout", function(data, status) {
 				if (data.code == "200") {
 				if (data.code == "200") {
 					ComAlert.show(1, "注销成功", function(){
 					ComAlert.show(1, "注销成功", function(){
-						window.location.href = base_url;
+						window.location.href = base_url + "/";
 					});
 					});
 				} else {
 				} else {
 					ComAlert.show(1, data.msg);
 					ComAlert.show(1, data.msg);

+ 16 - 0
xxl-job-admin/src/main/webapp/static/js/jobcode.index.1.js

@@ -0,0 +1,16 @@
+$(function() {
+
+	var javaEditor = CodeMirror.fromTextArea(document.getElementById("editor"), {
+		mode : "text/x-java",
+		lineNumbers : true,
+		matchBrackets : true,
+		extraKeys: {
+			"F11": function(cm) {
+				cm.setOption("fullScreen", !cm.getOption("fullScreen"));
+			},
+	        "Esc": function(cm) {
+	        	if (cm.getOption("fullScreen")) cm.setOption("fullScreen", false);
+	        }
+		}
+	});
+});

+ 18 - 35
xxl-job-admin/src/main/webapp/static/js/jobinfo.index.1.js

@@ -81,12 +81,14 @@ $(function() {
 	                			// status
 	                			// status
 	                			var pause_resume = "";
 	                			var pause_resume = "";
 	                			if ('NORMAL' == row.jobStatus) {
 	                			if ('NORMAL' == row.jobStatus) {
-	                				pause_resume = '<button class="btn btn-info btn-xs job_operate" type="job_pause" type="button">暂停</button>  ';
+	                				pause_resume = '<button class="btn btn-primary btn-xs job_operate" type="job_pause" type="button">暂停</button>  ';
 								} else if ('PAUSED' == row.jobStatus){
 								} else if ('PAUSED' == row.jobStatus){
-									pause_resume = '<button class="btn btn-info btn-xs job_operate" type="job_resume" type="button">恢复</button>  ';
+									pause_resume = '<button class="btn btn-primary btn-xs job_operate" type="job_resume" type="button">恢复</button>  ';
 								}
 								}
 	                			// log url
 	                			// log url
 	                			var logUrl = base_url +'/joblog?jobGroup='+ row.jobGroup +'&jobName='+ row.jobName;
 	                			var logUrl = base_url +'/joblog?jobGroup='+ row.jobGroup +'&jobName='+ row.jobName;
+	                			// log url
+	                			var codeUrl = base_url +'/jobcode?id='+ row.id;
 	                			
 	                			
 	                			// job data
 	                			// job data
 	                			var jobDataMap = eval('(' + row.jobData + ')');
 	                			var jobDataMap = eval('(' + row.jobData + ')');
@@ -105,11 +107,11 @@ $(function() {
 	                							' handler_address="'+ jobDataMap.handler_address +'" '+
 	                							' handler_address="'+ jobDataMap.handler_address +'" '+
 	                							' handler_name="'+ jobDataMap.handler_name +'" '+
 	                							' handler_name="'+ jobDataMap.handler_name +'" '+
 	                							'>'+
 	                							'>'+
-	                					pause_resume +
-										'<button class="btn btn-info btn-xs job_operate" type="job_trigger" type="button">执行</button>'+
-										'<button class="btn btn-warning btn-xs update" type="button">编辑</button><br>'+
-									  	'<button class="btn btn-warning btn-xs" type="job_del" type="button" '+
-									  		'onclick="javascript:window.open(\'' + logUrl + '\')" >查看日志</button> '+
+										'<button class="btn btn-primary btn-xs job_operate" type="job_trigger" type="button">执行</button>  '+
+										pause_resume +
+										'<button class="btn btn-primary btn-xs" type="job_del" type="button" onclick="javascript:window.open(\'' + logUrl + '\')" >日志</button><br>  '+
+										'<button class="btn btn-warning btn-xs update" type="button">编辑</button>  '+
+										'<button class="btn btn-warning btn-xs" type="button" onclick="javascript:window.open(\'' + codeUrl + '\')" >CODE</button>  '+
 								  		'<button class="btn btn-danger btn-xs job_operate" type="job_del" type="button">删除</button>  '+
 								  		'<button class="btn btn-danger btn-xs job_operate" type="job_del" type="button">删除</button>  '+
 									'</p>';
 									'</p>';
 									
 									
@@ -263,12 +265,12 @@ $(function() {
                 maxlength:"“任务描述”长度不应超过200位"
                 maxlength:"“任务描述”长度不应超过200位"
             },  
             },  
             handler_address : {
             handler_address : {
-            	required :"请输入“远程-机器地址”."  ,
-                maxlength:"“远程-机器地址”长度不应超过200位"
+            	required :"请输入“机器地址”."  ,
+                maxlength:"“机器地址”长度不应超过200位"
             },
             },
             handler_name : {
             handler_name : {
-            	required : "请输入“远程-执行器”."  ,
-                maxlength: "“远程-执行器”长度不应超过200位"
+            	required : "请输入“执行器”."  ,
+                maxlength: "“执行器”长度不应超过200位"
             },
             },
             author : {
             author : {
             	required : "请输入“负责人”."  ,
             	required : "请输入“负责人”."  ,
@@ -316,38 +318,19 @@ $(function() {
 		$(".remote_panel").show();	// remote
 		$(".remote_panel").show();	// remote
 	});
 	});
 	
 	
-	// 远程任务/本地任务,切换
-	$("#addModal select[name='jobClass']").change(function() {
-		//console.log( $(this).val().indexOf('RemoteHttpJobBean') );
-		
-		if($(this).val().indexOf('RemoteHttpJobBean') > -1){
-			$(".remote_panel").show();	// remote
-		} else if($(this).val().indexOf('RemoteHttpJobBean') == -1){
-			$(".remote_panel").hide();	// local
-		}
-    });
-	
 	// 更新
 	// 更新
 	$("#job_list").on('click', '.update',function() {
 	$("#job_list").on('click', '.update',function() {
 		$("#updateModal .form input[name='jobGroup']").val($(this).parent('p').attr("jobGroup"));
 		$("#updateModal .form input[name='jobGroup']").val($(this).parent('p').attr("jobGroup"));
 		$("#updateModal .form input[name='jobName']").val($(this).parent('p').attr("jobName"));
 		$("#updateModal .form input[name='jobName']").val($(this).parent('p').attr("jobName"));
 		$("#updateModal .form input[name='jobCron']").val($(this).parent('p').attr("jobCron"));
 		$("#updateModal .form input[name='jobCron']").val($(this).parent('p').attr("jobCron"));
 		$("#updateModal .form input[name='jobDesc']").val($(this).parent('p').attr("jobDesc"));
 		$("#updateModal .form input[name='jobDesc']").val($(this).parent('p').attr("jobDesc"));
-		$("#updateModal .form input[name='jobClass']").val($(this).parent('p').attr("jobClass"));
-		$("#updateModal .form input[name='handler_params']").val($(this).parent('p').attr("handler_params"));
 		$("#updateModal .form input[name='handler_address']").val($(this).parent('p').attr("handler_address"));
 		$("#updateModal .form input[name='handler_address']").val($(this).parent('p').attr("handler_address"));
 		$("#updateModal .form input[name='handler_name']").val($(this).parent('p').attr("handler_name"));
 		$("#updateModal .form input[name='handler_name']").val($(this).parent('p').attr("handler_name"));
+		$("#updateModal .form input[name='handler_params']").val($(this).parent('p').attr("handler_params"));
 		$("#updateModal .form input[name='author']").val($(this).parent('p').attr("author"));
 		$("#updateModal .form input[name='author']").val($(this).parent('p').attr("author"));
 		$("#updateModal .form input[name='alarmEmail']").val($(this).parent('p').attr("alarmEmail"));
 		$("#updateModal .form input[name='alarmEmail']").val($(this).parent('p').attr("alarmEmail"));
 		$("#updateModal .form input[name='alarmThreshold']").val($(this).parent('p').attr("alarmThreshold"));
 		$("#updateModal .form input[name='alarmThreshold']").val($(this).parent('p').attr("alarmThreshold"));
 		
 		
-		var _jobClass = $(this).parent('p').attr("jobClass");
-		if(_jobClass.indexOf('RemoteHttpJobBean') > -1){
-			$(".remote_panel").show();	// remote
-		} else if($(this).val().indexOf('RemoteHttpJobBean') == -1){
-			$(".remote_panel").hide();	// local
-		}
-		
 		$('#updateModal').modal({backdrop: false, keyboard: false}).modal('show');
 		$('#updateModal').modal({backdrop: false, keyboard: false}).modal('show');
 	});
 	});
 	var updateModalValidate = $("#updateModal .form").validate({
 	var updateModalValidate = $("#updateModal .form").validate({
@@ -394,12 +377,12 @@ $(function() {
                 maxlength:"“任务描述”长度不应超过200位"
                 maxlength:"“任务描述”长度不应超过200位"
             },  
             },  
             handler_address : {
             handler_address : {
-            	required :"请输入“远程-机器地址”."  ,
-                maxlength:"“远程-机器地址”长度不应超过200位"
+            	required :"请输入“机器地址”."  ,
+                maxlength:"“机器地址”长度不应超过200位"
             },
             },
             handler_name : {
             handler_name : {
-            	required : "请输入“远程-执行器”."  ,
-                maxlength: "“远程-执行器”长度不应超过200位"
+            	required : "请输入“执行器”."  ,
+                maxlength: "“执行器”长度不应超过200位"
             },
             },
             author : {
             author : {
             	required : "请输入“负责人”."  ,
             	required : "请输入“负责人”."  ,

+ 41 - 0
xxl-job-admin/src/main/webapp/static/plugins/codemirror/addon/hint/anyword-hint.js

@@ -0,0 +1,41 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  "use strict";
+
+  var WORD = /[\w$]+/, RANGE = 500;
+
+  CodeMirror.registerHelper("hint", "anyword", function(editor, options) {
+    var word = options && options.word || WORD;
+    var range = options && options.range || RANGE;
+    var cur = editor.getCursor(), curLine = editor.getLine(cur.line);
+    var end = cur.ch, start = end;
+    while (start && word.test(curLine.charAt(start - 1))) --start;
+    var curWord = start != end && curLine.slice(start, end);
+
+    var list = options && options.list || [], seen = {};
+    var re = new RegExp(word.source, "g");
+    for (var dir = -1; dir <= 1; dir += 2) {
+      var line = cur.line, endLine = Math.min(Math.max(line + dir * range, editor.firstLine()), editor.lastLine()) + dir;
+      for (; line != endLine; line += dir) {
+        var text = editor.getLine(line), m;
+        while (m = re.exec(text)) {
+          if (line == cur.line && m[0] === curWord) continue;
+          if ((!curWord || m[0].lastIndexOf(curWord, 0) == 0) && !Object.prototype.hasOwnProperty.call(seen, m[0])) {
+            seen[m[0]] = true;
+            list.push(m[0]);
+          }
+        }
+      }
+    }
+    return {list: list, from: CodeMirror.Pos(cur.line, start), to: CodeMirror.Pos(cur.line, end)};
+  });
+});

+ 38 - 0
xxl-job-admin/src/main/webapp/static/plugins/codemirror/addon/hint/show-hint.css

@@ -0,0 +1,38 @@
+.CodeMirror-hints {
+  position: absolute;
+  z-index: 10;
+  overflow: hidden;
+  list-style: none;
+
+  margin: 0;
+  padding: 2px;
+
+  -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
+  -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
+  box-shadow: 2px 3px 5px rgba(0,0,0,.2);
+  border-radius: 3px;
+  border: 1px solid silver;
+
+  background: white;
+  font-size: 90%;
+  font-family: monospace;
+
+  max-height: 20em;
+  overflow-y: auto;
+}
+
+.CodeMirror-hint {
+  margin: 0;
+  padding: 0 4px;
+  border-radius: 2px;
+  max-width: 19em;
+  overflow: hidden;
+  white-space: pre;
+  color: black;
+  cursor: pointer;
+}
+
+li.CodeMirror-hint-active {
+  background: #08f;
+  color: white;
+}

+ 447 - 0
xxl-job-admin/src/main/webapp/static/plugins/codemirror/addon/hint/show-hint.js

@@ -0,0 +1,447 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  "use strict";
+
+  var HINT_ELEMENT_CLASS        = "CodeMirror-hint";
+  var ACTIVE_HINT_ELEMENT_CLASS = "CodeMirror-hint-active";
+
+  // This is the old interface, kept around for now to stay
+  // backwards-compatible.
+  CodeMirror.showHint = function(cm, getHints, options) {
+    if (!getHints) return cm.showHint(options);
+    if (options && options.async) getHints.async = true;
+    var newOpts = {hint: getHints};
+    if (options) for (var prop in options) newOpts[prop] = options[prop];
+    return cm.showHint(newOpts);
+  };
+
+  CodeMirror.defineExtension("showHint", function(options) {
+    options = parseOptions(this, this.getCursor("start"), options);
+    var selections = this.listSelections()
+    if (selections.length > 1) return;
+    // By default, don't allow completion when something is selected.
+    // A hint function can have a `supportsSelection` property to
+    // indicate that it can handle selections.
+    if (this.somethingSelected()) {
+      if (!options.hint.supportsSelection) return;
+      // Don't try with cross-line selections
+      for (var i = 0; i < selections.length; i++)
+        if (selections[i].head.line != selections[i].anchor.line) return;
+    }
+
+    if (this.state.completionActive) this.state.completionActive.close();
+    var completion = this.state.completionActive = new Completion(this, options);
+    if (!completion.options.hint) return;
+
+    CodeMirror.signal(this, "startCompletion", this);
+    completion.update(true);
+  });
+
+  function Completion(cm, options) {
+    this.cm = cm;
+    this.options = options;
+    this.widget = null;
+    this.debounce = 0;
+    this.tick = 0;
+    this.startPos = this.cm.getCursor("start");
+    this.startLen = this.cm.getLine(this.startPos.line).length - this.cm.getSelection().length;
+
+    var self = this;
+    cm.on("cursorActivity", this.activityFunc = function() { self.cursorActivity(); });
+  }
+
+  var requestAnimationFrame = window.requestAnimationFrame || function(fn) {
+    return setTimeout(fn, 1000/60);
+  };
+  var cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout;
+
+  Completion.prototype = {
+    close: function() {
+      if (!this.active()) return;
+      this.cm.state.completionActive = null;
+      this.tick = null;
+      this.cm.off("cursorActivity", this.activityFunc);
+
+      if (this.widget && this.data) CodeMirror.signal(this.data, "close");
+      if (this.widget) this.widget.close();
+      CodeMirror.signal(this.cm, "endCompletion", this.cm);
+    },
+
+    active: function() {
+      return this.cm.state.completionActive == this;
+    },
+
+    pick: function(data, i) {
+      var completion = data.list[i];
+      if (completion.hint) completion.hint(this.cm, data, completion);
+      else this.cm.replaceRange(getText(completion), completion.from || data.from,
+                                completion.to || data.to, "complete");
+      CodeMirror.signal(data, "pick", completion);
+      this.close();
+    },
+
+    cursorActivity: function() {
+      if (this.debounce) {
+        cancelAnimationFrame(this.debounce);
+        this.debounce = 0;
+      }
+
+      var pos = this.cm.getCursor(), line = this.cm.getLine(pos.line);
+      if (pos.line != this.startPos.line || line.length - pos.ch != this.startLen - this.startPos.ch ||
+          pos.ch < this.startPos.ch || this.cm.somethingSelected() ||
+          (pos.ch && this.options.closeCharacters.test(line.charAt(pos.ch - 1)))) {
+        this.close();
+      } else {
+        var self = this;
+        this.debounce = requestAnimationFrame(function() {self.update();});
+        if (this.widget) this.widget.disable();
+      }
+    },
+
+    update: function(first) {
+      if (this.tick == null) return;
+      if (!this.options.hint.async) {
+        this.finishUpdate(this.options.hint(this.cm, this.options), first);
+      } else {
+        var myTick = ++this.tick, self = this;
+        this.options.hint(this.cm, function(data) {
+          if (self.tick == myTick) self.finishUpdate(data, first);
+        }, this.options);
+      }
+    },
+
+    finishUpdate: function(data, first) {
+      if (this.data) CodeMirror.signal(this.data, "update");
+
+      var picked = (this.widget && this.widget.picked) || (first && this.options.completeSingle);
+      if (this.widget) this.widget.close();
+
+      if (data && this.data && isNewCompletion(this.data, data)) return;
+      this.data = data;
+
+      if (data && data.list.length) {
+        if (picked && data.list.length == 1) {
+          this.pick(data, 0);
+        } else {
+          this.widget = new Widget(this, data);
+          CodeMirror.signal(data, "shown");
+        }
+      }
+    }
+  };
+
+  function isNewCompletion(old, nw) {
+    var moved = CodeMirror.cmpPos(nw.from, old.from)
+    return moved > 0 && old.to.ch - old.from.ch != nw.to.ch - nw.from.ch
+  }
+
+  function parseOptions(cm, pos, options) {
+    var editor = cm.options.hintOptions;
+    var out = {};
+    for (var prop in defaultOptions) out[prop] = defaultOptions[prop];
+    if (editor) for (var prop in editor)
+      if (editor[prop] !== undefined) out[prop] = editor[prop];
+    if (options) for (var prop in options)
+      if (options[prop] !== undefined) out[prop] = options[prop];
+    if (out.hint.resolve) out.hint = out.hint.resolve(cm, pos)
+    return out;
+  }
+
+  function getText(completion) {
+    if (typeof completion == "string") return completion;
+    else return completion.text;
+  }
+
+  function buildKeyMap(completion, handle) {
+    var baseMap = {
+      Up: function() {handle.moveFocus(-1);},
+      Down: function() {handle.moveFocus(1);},
+      PageUp: function() {handle.moveFocus(-handle.menuSize() + 1, true);},
+      PageDown: function() {handle.moveFocus(handle.menuSize() - 1, true);},
+      Home: function() {handle.setFocus(0);},
+      End: function() {handle.setFocus(handle.length - 1);},
+      Enter: handle.pick,
+      Tab: handle.pick,
+      Esc: handle.close
+    };
+    var custom = completion.options.customKeys;
+    var ourMap = custom ? {} : baseMap;
+    function addBinding(key, val) {
+      var bound;
+      if (typeof val != "string")
+        bound = function(cm) { return val(cm, handle); };
+      // This mechanism is deprecated
+      else if (baseMap.hasOwnProperty(val))
+        bound = baseMap[val];
+      else
+        bound = val;
+      ourMap[key] = bound;
+    }
+    if (custom)
+      for (var key in custom) if (custom.hasOwnProperty(key))
+        addBinding(key, custom[key]);
+    var extra = completion.options.extraKeys;
+    if (extra)
+      for (var key in extra) if (extra.hasOwnProperty(key))
+        addBinding(key, extra[key]);
+    return ourMap;
+  }
+
+  function getHintElement(hintsElement, el) {
+    while (el && el != hintsElement) {
+      if (el.nodeName.toUpperCase() === "LI" && el.parentNode == hintsElement) return el;
+      el = el.parentNode;
+    }
+  }
+
+  function Widget(completion, data) {
+    this.completion = completion;
+    this.data = data;
+    this.picked = false;
+    var widget = this, cm = completion.cm;
+
+    var hints = this.hints = document.createElement("ul");
+    hints.className = "CodeMirror-hints";
+    this.selectedHint = data.selectedHint || 0;
+
+    var completions = data.list;
+    for (var i = 0; i < completions.length; ++i) {
+      var elt = hints.appendChild(document.createElement("li")), cur = completions[i];
+      var className = HINT_ELEMENT_CLASS + (i != this.selectedHint ? "" : " " + ACTIVE_HINT_ELEMENT_CLASS);
+      if (cur.className != null) className = cur.className + " " + className;
+      elt.className = className;
+      if (cur.render) cur.render(elt, data, cur);
+      else elt.appendChild(document.createTextNode(cur.displayText || getText(cur)));
+      elt.hintId = i;
+    }
+
+    var pos = cm.cursorCoords(completion.options.alignWithWord ? data.from : null);
+    var left = pos.left, top = pos.bottom, below = true;
+    hints.style.left = left + "px";
+    hints.style.top = top + "px";
+    // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.
+    var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth);
+    var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight);
+    (completion.options.container || document.body).appendChild(hints);
+    var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH;
+    if (overlapY > 0) {
+      var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top);
+      if (curTop - height > 0) { // Fits above cursor
+        hints.style.top = (top = pos.top - height) + "px";
+        below = false;
+      } else if (height > winH) {
+        hints.style.height = (winH - 5) + "px";
+        hints.style.top = (top = pos.bottom - box.top) + "px";
+        var cursor = cm.getCursor();
+        if (data.from.ch != cursor.ch) {
+          pos = cm.cursorCoords(cursor);
+          hints.style.left = (left = pos.left) + "px";
+          box = hints.getBoundingClientRect();
+        }
+      }
+    }
+    var overlapX = box.right - winW;
+    if (overlapX > 0) {
+      if (box.right - box.left > winW) {
+        hints.style.width = (winW - 5) + "px";
+        overlapX -= (box.right - box.left) - winW;
+      }
+      hints.style.left = (left = pos.left - overlapX) + "px";
+    }
+
+    cm.addKeyMap(this.keyMap = buildKeyMap(completion, {
+      moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); },
+      setFocus: function(n) { widget.changeActive(n); },
+      menuSize: function() { return widget.screenAmount(); },
+      length: completions.length,
+      close: function() { completion.close(); },
+      pick: function() { widget.pick(); },
+      data: data
+    }));
+
+    if (completion.options.closeOnUnfocus) {
+      var closingOnBlur;
+      cm.on("blur", this.onBlur = function() { closingOnBlur = setTimeout(function() { completion.close(); }, 100); });
+      cm.on("focus", this.onFocus = function() { clearTimeout(closingOnBlur); });
+    }
+
+    var startScroll = cm.getScrollInfo();
+    cm.on("scroll", this.onScroll = function() {
+      var curScroll = cm.getScrollInfo(), editor = cm.getWrapperElement().getBoundingClientRect();
+      var newTop = top + startScroll.top - curScroll.top;
+      var point = newTop - (window.pageYOffset || (document.documentElement || document.body).scrollTop);
+      if (!below) point += hints.offsetHeight;
+      if (point <= editor.top || point >= editor.bottom) return completion.close();
+      hints.style.top = newTop + "px";
+      hints.style.left = (left + startScroll.left - curScroll.left) + "px";
+    });
+
+    CodeMirror.on(hints, "dblclick", function(e) {
+      var t = getHintElement(hints, e.target || e.srcElement);
+      if (t && t.hintId != null) {widget.changeActive(t.hintId); widget.pick();}
+    });
+
+    CodeMirror.on(hints, "click", function(e) {
+      var t = getHintElement(hints, e.target || e.srcElement);
+      if (t && t.hintId != null) {
+        widget.changeActive(t.hintId);
+        if (completion.options.completeOnSingleClick) widget.pick();
+      }
+    });
+
+    CodeMirror.on(hints, "mousedown", function() {
+      setTimeout(function(){cm.focus();}, 20);
+    });
+
+    CodeMirror.signal(data, "select", completions[0], hints.firstChild);
+    return true;
+  }
+
+  Widget.prototype = {
+    close: function() {
+      if (this.completion.widget != this) return;
+      this.completion.widget = null;
+      this.hints.parentNode.removeChild(this.hints);
+      this.completion.cm.removeKeyMap(this.keyMap);
+
+      var cm = this.completion.cm;
+      if (this.completion.options.closeOnUnfocus) {
+        cm.off("blur", this.onBlur);
+        cm.off("focus", this.onFocus);
+      }
+      cm.off("scroll", this.onScroll);
+    },
+
+    disable: function() {
+      this.completion.cm.removeKeyMap(this.keyMap);
+      var widget = this;
+      this.keyMap = {Enter: function() { widget.picked = true; }};
+      this.completion.cm.addKeyMap(this.keyMap);
+    },
+
+    pick: function() {
+      this.completion.pick(this.data, this.selectedHint);
+    },
+
+    changeActive: function(i, avoidWrap) {
+      if (i >= this.data.list.length)
+        i = avoidWrap ? this.data.list.length - 1 : 0;
+      else if (i < 0)
+        i = avoidWrap ? 0  : this.data.list.length - 1;
+      if (this.selectedHint == i) return;
+      var node = this.hints.childNodes[this.selectedHint];
+      node.className = node.className.replace(" " + ACTIVE_HINT_ELEMENT_CLASS, "");
+      node = this.hints.childNodes[this.selectedHint = i];
+      node.className += " " + ACTIVE_HINT_ELEMENT_CLASS;
+      if (node.offsetTop < this.hints.scrollTop)
+        this.hints.scrollTop = node.offsetTop - 3;
+      else if (node.offsetTop + node.offsetHeight > this.hints.scrollTop + this.hints.clientHeight)
+        this.hints.scrollTop = node.offsetTop + node.offsetHeight - this.hints.clientHeight + 3;
+      CodeMirror.signal(this.data, "select", this.data.list[this.selectedHint], node);
+    },
+
+    screenAmount: function() {
+      return Math.floor(this.hints.clientHeight / this.hints.firstChild.offsetHeight) || 1;
+    }
+  };
+
+  function applicableHelpers(cm, helpers) {
+    if (!cm.somethingSelected()) return helpers
+    var result = []
+    for (var i = 0; i < helpers.length; i++)
+      if (helpers[i].supportsSelection) result.push(helpers[i])
+    return result
+  }
+
+  function resolveAutoHints(cm, pos) {
+    var helpers = cm.getHelpers(pos, "hint"), words
+    if (helpers.length) {
+      var async = false, resolved
+      for (var i = 0; i < helpers.length; i++) if (helpers[i].async) async = true
+      if (async) {
+        resolved = function(cm, callback, options) {
+          var app = applicableHelpers(cm, helpers)
+          function run(i, result) {
+            if (i == app.length) return callback(null)
+            var helper = app[i]
+            if (helper.async) {
+              helper(cm, function(result) {
+                if (result) callback(result)
+                else run(i + 1)
+              }, options)
+            } else {
+              var result = helper(cm, options)
+              if (result) callback(result)
+              else run(i + 1)
+            }
+          }
+          run(0)
+        }
+        resolved.async = true
+      } else {
+        resolved = function(cm, options) {
+          var app = applicableHelpers(cm, helpers)
+          for (var i = 0; i < app.length; i++) {
+            var cur = app[i](cm, options)
+            if (cur && cur.list.length) return cur
+          }
+        }
+      }
+      resolved.supportsSelection = true
+      return resolved
+    } else if (words = cm.getHelper(cm.getCursor(), "hintWords")) {
+      return function(cm) { return CodeMirror.hint.fromList(cm, {words: words}) }
+    } else if (CodeMirror.hint.anyword) {
+      return function(cm, options) { return CodeMirror.hint.anyword(cm, options) }
+    } else {
+      return function() {}
+    }
+  }
+
+  CodeMirror.registerHelper("hint", "auto", {
+    resolve: resolveAutoHints
+  });
+
+  CodeMirror.registerHelper("hint", "fromList", function(cm, options) {
+    var cur = cm.getCursor(), token = cm.getTokenAt(cur);
+    var to = CodeMirror.Pos(cur.line, token.end);
+    if (token.string && /\w/.test(token.string[token.string.length - 1])) {
+      var term = token.string, from = CodeMirror.Pos(cur.line, token.start);
+    } else {
+      var term = "", from = to;
+    }
+    var found = [];
+    for (var i = 0; i < options.words.length; i++) {
+      var word = options.words[i];
+      if (word.slice(0, term.length) == term)
+        found.push(word);
+    }
+
+    if (found.length) return {list: found, from: from, to: to};
+  });
+
+  CodeMirror.commands.autocomplete = CodeMirror.showHint;
+
+  var defaultOptions = {
+    hint: CodeMirror.hint.auto,
+    completeSingle: true,
+    alignWithWord: true,
+    closeCharacters: /[\s()\[\]{};:>,]/,
+    closeOnUnfocus: true,
+    completeOnSingleClick: true,
+    container: null,
+    customKeys: null,
+    extraKeys: null
+  };
+
+  CodeMirror.defineOption("hintOptions", null);
+});

+ 338 - 0
xxl-job-admin/src/main/webapp/static/plugins/codemirror/lib/codemirror.css

@@ -0,0 +1,338 @@
+/* BASICS */
+
+.CodeMirror {
+  /* Set height, width, borders, and global font properties here */
+  font-family: monospace;
+  height: 300px;
+  color: black;
+}
+
+/* PADDING */
+
+.CodeMirror-lines {
+  padding: 4px 0; /* Vertical padding around content */
+}
+.CodeMirror pre {
+  padding: 0 4px; /* Horizontal padding of content */
+}
+
+.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
+  background-color: white; /* The little square between H and V scrollbars */
+}
+
+/* GUTTER */
+
+.CodeMirror-gutters {
+  border-right: 1px solid #ddd;
+  background-color: #f7f7f7;
+  white-space: nowrap;
+}
+.CodeMirror-linenumbers {}
+.CodeMirror-linenumber {
+  padding: 0 3px 0 5px;
+  min-width: 20px;
+  text-align: right;
+  color: #999;
+  white-space: nowrap;
+}
+
+.CodeMirror-guttermarker { color: black; }
+.CodeMirror-guttermarker-subtle { color: #999; }
+
+/* CURSOR */
+
+.CodeMirror-cursor {
+  border-left: 1px solid black;
+  border-right: none;
+  width: 0;
+}
+/* Shown when moving in bi-directional text */
+.CodeMirror div.CodeMirror-secondarycursor {
+  border-left: 1px solid silver;
+}
+.cm-fat-cursor .CodeMirror-cursor {
+  width: auto;
+  border: 0;
+  background: #7e7;
+}
+.cm-fat-cursor div.CodeMirror-cursors {
+  z-index: 1;
+}
+
+.cm-animate-fat-cursor {
+  width: auto;
+  border: 0;
+  -webkit-animation: blink 1.06s steps(1) infinite;
+  -moz-animation: blink 1.06s steps(1) infinite;
+  animation: blink 1.06s steps(1) infinite;
+  background-color: #7e7;
+}
+@-moz-keyframes blink {
+  0% {}
+  50% { background-color: transparent; }
+  100% {}
+}
+@-webkit-keyframes blink {
+  0% {}
+  50% { background-color: transparent; }
+  100% {}
+}
+@keyframes blink {
+  0% {}
+  50% { background-color: transparent; }
+  100% {}
+}
+
+/* Can style cursor different in overwrite (non-insert) mode */
+.CodeMirror-overwrite .CodeMirror-cursor {}
+
+.cm-tab { display: inline-block; text-decoration: inherit; }
+
+.CodeMirror-ruler {
+  border-left: 1px solid #ccc;
+  position: absolute;
+}
+
+/* DEFAULT THEME */
+
+.cm-s-default .cm-header {color: blue;}
+.cm-s-default .cm-quote {color: #090;}
+.cm-negative {color: #d44;}
+.cm-positive {color: #292;}
+.cm-header, .cm-strong {font-weight: bold;}
+.cm-em {font-style: italic;}
+.cm-link {text-decoration: underline;}
+.cm-strikethrough {text-decoration: line-through;}
+
+.cm-s-default .cm-keyword {color: #708;}
+.cm-s-default .cm-atom {color: #219;}
+.cm-s-default .cm-number {color: #164;}
+.cm-s-default .cm-def {color: #00f;}
+.cm-s-default .cm-variable,
+.cm-s-default .cm-punctuation,
+.cm-s-default .cm-property,
+.cm-s-default .cm-operator {}
+.cm-s-default .cm-variable-2 {color: #05a;}
+.cm-s-default .cm-variable-3 {color: #085;}
+.cm-s-default .cm-comment {color: #a50;}
+.cm-s-default .cm-string {color: #a11;}
+.cm-s-default .cm-string-2 {color: #f50;}
+.cm-s-default .cm-meta {color: #555;}
+.cm-s-default .cm-qualifier {color: #555;}
+.cm-s-default .cm-builtin {color: #30a;}
+.cm-s-default .cm-bracket {color: #997;}
+.cm-s-default .cm-tag {color: #170;}
+.cm-s-default .cm-attribute {color: #00c;}
+.cm-s-default .cm-hr {color: #999;}
+.cm-s-default .cm-link {color: #00c;}
+
+.cm-s-default .cm-error {color: #f00;}
+.cm-invalidchar {color: #f00;}
+
+.CodeMirror-composing { border-bottom: 2px solid; }
+
+/* Default styles for common addons */
+
+div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
+div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
+.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
+.CodeMirror-activeline-background {background: #e8f2ff;}
+
+/* STOP */
+
+/* The rest of this file contains styles related to the mechanics of
+   the editor. You probably shouldn't touch them. */
+
+.CodeMirror {
+  position: relative;
+  overflow: hidden;
+  background: white;
+}
+
+.CodeMirror-scroll {
+  overflow: scroll !important; /* Things will break if this is overridden */
+  /* 30px is the magic margin used to hide the element's real scrollbars */
+  /* See overflow: hidden in .CodeMirror */
+  margin-bottom: -30px; margin-right: -30px;
+  padding-bottom: 30px;
+  height: 100%;
+  outline: none; /* Prevent dragging from highlighting the element */
+  position: relative;
+}
+.CodeMirror-sizer {
+  position: relative;
+  border-right: 30px solid transparent;
+}
+
+/* The fake, visible scrollbars. Used to force redraw during scrolling
+   before actual scrolling happens, thus preventing shaking and
+   flickering artifacts. */
+.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
+  position: absolute;
+  z-index: 6;
+  display: none;
+}
+.CodeMirror-vscrollbar {
+  right: 0; top: 0;
+  overflow-x: hidden;
+  overflow-y: scroll;
+}
+.CodeMirror-hscrollbar {
+  bottom: 0; left: 0;
+  overflow-y: hidden;
+  overflow-x: scroll;
+}
+.CodeMirror-scrollbar-filler {
+  right: 0; bottom: 0;
+}
+.CodeMirror-gutter-filler {
+  left: 0; bottom: 0;
+}
+
+.CodeMirror-gutters {
+  position: absolute; left: 0; top: 0;
+  min-height: 100%;
+  z-index: 3;
+}
+.CodeMirror-gutter {
+  white-space: normal;
+  height: 100%;
+  display: inline-block;
+  vertical-align: top;
+  margin-bottom: -30px;
+  /* Hack to make IE7 behave */
+  *zoom:1;
+  *display:inline;
+}
+.CodeMirror-gutter-wrapper {
+  position: absolute;
+  z-index: 4;
+  background: none !important;
+  border: none !important;
+}
+.CodeMirror-gutter-background {
+  position: absolute;
+  top: 0; bottom: 0;
+  z-index: 4;
+}
+.CodeMirror-gutter-elt {
+  position: absolute;
+  cursor: default;
+  z-index: 4;
+}
+.CodeMirror-gutter-wrapper {
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  user-select: none;
+}
+
+.CodeMirror-lines {
+  cursor: text;
+  min-height: 1px; /* prevents collapsing before first draw */
+}
+.CodeMirror pre {
+  /* Reset some styles that the rest of the page might have set */
+  -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
+  border-width: 0;
+  background: transparent;
+  font-family: inherit;
+  font-size: inherit;
+  margin: 0;
+  white-space: pre;
+  word-wrap: normal;
+  line-height: inherit;
+  color: inherit;
+  z-index: 2;
+  position: relative;
+  overflow: visible;
+  -webkit-tap-highlight-color: transparent;
+  -webkit-font-variant-ligatures: none;
+  font-variant-ligatures: none;
+}
+.CodeMirror-wrap pre {
+  word-wrap: break-word;
+  white-space: pre-wrap;
+  word-break: normal;
+}
+
+.CodeMirror-linebackground {
+  position: absolute;
+  left: 0; right: 0; top: 0; bottom: 0;
+  z-index: 0;
+}
+
+.CodeMirror-linewidget {
+  position: relative;
+  z-index: 2;
+  overflow: auto;
+}
+
+.CodeMirror-widget {}
+
+.CodeMirror-code {
+  outline: none;
+}
+
+/* Force content-box sizing for the elements where we expect it */
+.CodeMirror-scroll,
+.CodeMirror-sizer,
+.CodeMirror-gutter,
+.CodeMirror-gutters,
+.CodeMirror-linenumber {
+  -moz-box-sizing: content-box;
+  box-sizing: content-box;
+}
+
+.CodeMirror-measure {
+  position: absolute;
+  width: 100%;
+  height: 0;
+  overflow: hidden;
+  visibility: hidden;
+}
+
+.CodeMirror-cursor { position: absolute; }
+.CodeMirror-measure pre { position: static; }
+
+div.CodeMirror-cursors {
+  visibility: hidden;
+  position: relative;
+  z-index: 3;
+}
+div.CodeMirror-dragcursors {
+  visibility: visible;
+}
+
+.CodeMirror-focused div.CodeMirror-cursors {
+  visibility: visible;
+}
+
+.CodeMirror-selected { background: #d9d9d9; }
+.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
+.CodeMirror-crosshair { cursor: crosshair; }
+.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }
+.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
+
+.cm-searching {
+  background: #ffa;
+  background: rgba(255, 255, 0, .4);
+}
+
+/* IE7 hack to prevent it from returning funny offsetTops on the spans */
+.CodeMirror span { *vertical-align: text-bottom; }
+
+/* Used to force a border model for a node */
+.cm-force-border { padding-right: .1px; }
+
+@media print {
+  /* Hide the cursor when printing */
+  .CodeMirror div.CodeMirror-cursors {
+    visibility: hidden;
+  }
+}
+
+/* See issue #2901 */
+.cm-tab-wrap-hack:after { content: ''; }
+
+/* Help users use markselection to safely style text background */
+span.CodeMirror-selectedtext { background: none; }

File diff suppressed because it is too large
+ 8895 - 0
xxl-job-admin/src/main/webapp/static/plugins/codemirror/lib/codemirror.js


+ 781 - 0
xxl-job-admin/src/main/webapp/static/plugins/codemirror/mode/clike/clike.js

@@ -0,0 +1,781 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+function Context(indented, column, type, align, prev) {
+  this.indented = indented;
+  this.column = column;
+  this.type = type;
+  this.align = align;
+  this.prev = prev;
+}
+function isStatement(type) {
+  return type == "statement" || type == "switchstatement" || type == "namespace";
+}
+function pushContext(state, col, type) {
+  var indent = state.indented;
+  if (state.context && isStatement(state.context.type) && !isStatement(type))
+    indent = state.context.indented;
+  return state.context = new Context(indent, col, type, null, state.context);
+}
+function popContext(state) {
+  var t = state.context.type;
+  if (t == ")" || t == "]" || t == "}")
+    state.indented = state.context.indented;
+  return state.context = state.context.prev;
+}
+
+function typeBefore(stream, state) {
+  if (state.prevToken == "variable" || state.prevToken == "variable-3") return true;
+  if (/\S(?:[^- ]>|[*\]])\s*$|\*$/.test(stream.string.slice(0, stream.start))) return true;
+}
+
+function isTopScope(context) {
+  for (;;) {
+    if (!context || context.type == "top") return true;
+    if (context.type == "}" && context.prev.type != "namespace") return false;
+    context = context.prev;
+  }
+}
+
+CodeMirror.defineMode("clike", function(config, parserConfig) {
+  var indentUnit = config.indentUnit,
+      statementIndentUnit = parserConfig.statementIndentUnit || indentUnit,
+      dontAlignCalls = parserConfig.dontAlignCalls,
+      keywords = parserConfig.keywords || {},
+      types = parserConfig.types || {},
+      builtin = parserConfig.builtin || {},
+      blockKeywords = parserConfig.blockKeywords || {},
+      defKeywords = parserConfig.defKeywords || {},
+      atoms = parserConfig.atoms || {},
+      hooks = parserConfig.hooks || {},
+      multiLineStrings = parserConfig.multiLineStrings,
+      indentStatements = parserConfig.indentStatements !== false,
+      indentSwitch = parserConfig.indentSwitch !== false,
+      namespaceSeparator = parserConfig.namespaceSeparator,
+      isPunctuationChar = parserConfig.isPunctuationChar || /[\[\]{}\(\),;\:\.]/,
+      numberStart = parserConfig.numberStart || /[\d\.]/,
+      number = parserConfig.number || /^(?:0x[a-f\d]+|0b[01]+|(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?)(u|ll?|l|f)?/i,
+      isOperatorChar = parserConfig.isOperatorChar || /[+\-*&%=<>!?|\/]/,
+      endStatement = parserConfig.endStatement || /^[;:,]$/;
+
+  var curPunc, isDefKeyword;
+
+  function tokenBase(stream, state) {
+    var ch = stream.next();
+    if (hooks[ch]) {
+      var result = hooks[ch](stream, state);
+      if (result !== false) return result;
+    }
+    if (ch == '"' || ch == "'") {
+      state.tokenize = tokenString(ch);
+      return state.tokenize(stream, state);
+    }
+    if (isPunctuationChar.test(ch)) {
+      curPunc = ch;
+      return null;
+    }
+    if (numberStart.test(ch)) {
+      stream.backUp(1)
+      if (stream.match(number)) return "number"
+      stream.next()
+    }
+    if (ch == "/") {
+      if (stream.eat("*")) {
+        state.tokenize = tokenComment;
+        return tokenComment(stream, state);
+      }
+      if (stream.eat("/")) {
+        stream.skipToEnd();
+        return "comment";
+      }
+    }
+    if (isOperatorChar.test(ch)) {
+      while (!stream.match(/^\/[\/*]/, false) && stream.eat(isOperatorChar)) {}
+      return "operator";
+    }
+    stream.eatWhile(/[\w\$_\xa1-\uffff]/);
+    if (namespaceSeparator) while (stream.match(namespaceSeparator))
+      stream.eatWhile(/[\w\$_\xa1-\uffff]/);
+
+    var cur = stream.current();
+    if (contains(keywords, cur)) {
+      if (contains(blockKeywords, cur)) curPunc = "newstatement";
+      if (contains(defKeywords, cur)) isDefKeyword = true;
+      return "keyword";
+    }
+    if (contains(types, cur)) return "variable-3";
+    if (contains(builtin, cur)) {
+      if (contains(blockKeywords, cur)) curPunc = "newstatement";
+      return "builtin";
+    }
+    if (contains(atoms, cur)) return "atom";
+    return "variable";
+  }
+
+  function tokenString(quote) {
+    return function(stream, state) {
+      var escaped = false, next, end = false;
+      while ((next = stream.next()) != null) {
+        if (next == quote && !escaped) {end = true; break;}
+        escaped = !escaped && next == "\\";
+      }
+      if (end || !(escaped || multiLineStrings))
+        state.tokenize = null;
+      return "string";
+    };
+  }
+
+  function tokenComment(stream, state) {
+    var maybeEnd = false, ch;
+    while (ch = stream.next()) {
+      if (ch == "/" && maybeEnd) {
+        state.tokenize = null;
+        break;
+      }
+      maybeEnd = (ch == "*");
+    }
+    return "comment";
+  }
+
+  // Interface
+
+  return {
+    startState: function(basecolumn) {
+      return {
+        tokenize: null,
+        context: new Context((basecolumn || 0) - indentUnit, 0, "top", false),
+        indented: 0,
+        startOfLine: true,
+        prevToken: null
+      };
+    },
+
+    token: function(stream, state) {
+      var ctx = state.context;
+      if (stream.sol()) {
+        if (ctx.align == null) ctx.align = false;
+        state.indented = stream.indentation();
+        state.startOfLine = true;
+      }
+      if (stream.eatSpace()) return null;
+      curPunc = isDefKeyword = null;
+      var style = (state.tokenize || tokenBase)(stream, state);
+      if (style == "comment" || style == "meta") return style;
+      if (ctx.align == null) ctx.align = true;
+
+      if (endStatement.test(curPunc)) while (isStatement(state.context.type)) popContext(state);
+      else if (curPunc == "{") pushContext(state, stream.column(), "}");
+      else if (curPunc == "[") pushContext(state, stream.column(), "]");
+      else if (curPunc == "(") pushContext(state, stream.column(), ")");
+      else if (curPunc == "}") {
+        while (isStatement(ctx.type)) ctx = popContext(state);
+        if (ctx.type == "}") ctx = popContext(state);
+        while (isStatement(ctx.type)) ctx = popContext(state);
+      }
+      else if (curPunc == ctx.type) popContext(state);
+      else if (indentStatements &&
+               (((ctx.type == "}" || ctx.type == "top") && curPunc != ";") ||
+                (isStatement(ctx.type) && curPunc == "newstatement"))) {
+        var type = "statement";
+        if (curPunc == "newstatement" && indentSwitch && stream.current() == "switch")
+          type = "switchstatement";
+        else if (style == "keyword" && stream.current() == "namespace")
+          type = "namespace";
+        pushContext(state, stream.column(), type);
+      }
+
+      if (style == "variable" &&
+          ((state.prevToken == "def" ||
+            (parserConfig.typeFirstDefinitions && typeBefore(stream, state) &&
+             isTopScope(state.context) && stream.match(/^\s*\(/, false)))))
+        style = "def";
+
+      if (hooks.token) {
+        var result = hooks.token(stream, state, style);
+        if (result !== undefined) style = result;
+      }
+
+      if (style == "def" && parserConfig.styleDefs === false) style = "variable";
+
+      state.startOfLine = false;
+      state.prevToken = isDefKeyword ? "def" : style || curPunc;
+      return style;
+    },
+
+    indent: function(state, textAfter) {
+      if (state.tokenize != tokenBase && state.tokenize != null) return CodeMirror.Pass;
+      var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
+      if (isStatement(ctx.type) && firstChar == "}") ctx = ctx.prev;
+      if (hooks.indent) {
+        var hook = hooks.indent(state, ctx, textAfter);
+        if (typeof hook == "number") return hook
+      }
+      var closing = firstChar == ctx.type;
+      var switchBlock = ctx.prev && ctx.prev.type == "switchstatement";
+      if (parserConfig.allmanIndentation && /[{(]/.test(firstChar)) {
+        while (ctx.type != "top" && ctx.type != "}") ctx = ctx.prev
+        return ctx.indented
+      }
+      if (isStatement(ctx.type))
+        return ctx.indented + (firstChar == "{" ? 0 : statementIndentUnit);
+      if (ctx.align && (!dontAlignCalls || ctx.type != ")"))
+        return ctx.column + (closing ? 0 : 1);
+      if (ctx.type == ")" && !closing)
+        return ctx.indented + statementIndentUnit;
+
+      return ctx.indented + (closing ? 0 : indentUnit) +
+        (!closing && switchBlock && !/^(?:case|default)\b/.test(textAfter) ? indentUnit : 0);
+    },
+
+    electricInput: indentSwitch ? /^\s*(?:case .*?:|default:|\{\}?|\})$/ : /^\s*[{}]$/,
+    blockCommentStart: "/*",
+    blockCommentEnd: "*/",
+    lineComment: "//",
+    fold: "brace"
+  };
+});
+
+  function words(str) {
+    var obj = {}, words = str.split(" ");
+    for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
+    return obj;
+  }
+  function contains(words, word) {
+    if (typeof words === "function") {
+      return words(word);
+    } else {
+      return words.propertyIsEnumerable(word);
+    }
+  }
+  var cKeywords = "auto if break case register continue return default do sizeof " +
+    "static else struct switch extern typedef union for goto while enum const volatile";
+  var cTypes = "int long char short double float unsigned signed void size_t ptrdiff_t";
+
+  function cppHook(stream, state) {
+    if (!state.startOfLine) return false
+    for (var ch, next = null; ch = stream.peek();) {
+      if (ch == "\\" && stream.match(/^.$/)) {
+        next = cppHook
+        break
+      } else if (ch == "/" && stream.match(/^\/[\/\*]/, false)) {
+        break
+      }
+      stream.next()
+    }
+    state.tokenize = next
+    return "meta"
+  }
+
+  function pointerHook(_stream, state) {
+    if (state.prevToken == "variable-3") return "variable-3";
+    return false;
+  }
+
+  function cpp14Literal(stream) {
+    stream.eatWhile(/[\w\.']/);
+    return "number";
+  }
+
+  function cpp11StringHook(stream, state) {
+    stream.backUp(1);
+    // Raw strings.
+    if (stream.match(/(R|u8R|uR|UR|LR)/)) {
+      var match = stream.match(/"([^\s\\()]{0,16})\(/);
+      if (!match) {
+        return false;
+      }
+      state.cpp11RawStringDelim = match[1];
+      state.tokenize = tokenRawString;
+      return tokenRawString(stream, state);
+    }
+    // Unicode strings/chars.
+    if (stream.match(/(u8|u|U|L)/)) {
+      if (stream.match(/["']/, /* eat */ false)) {
+        return "string";
+      }
+      return false;
+    }
+    // Ignore this hook.
+    stream.next();
+    return false;
+  }
+
+  function cppLooksLikeConstructor(word) {
+    var lastTwo = /(\w+)::(\w+)$/.exec(word);
+    return lastTwo && lastTwo[1] == lastTwo[2];
+  }
+
+  // C#-style strings where "" escapes a quote.
+  function tokenAtString(stream, state) {
+    var next;
+    while ((next = stream.next()) != null) {
+      if (next == '"' && !stream.eat('"')) {
+        state.tokenize = null;
+        break;
+      }
+    }
+    return "string";
+  }
+
+  // C++11 raw string literal is <prefix>"<delim>( anything )<delim>", where
+  // <delim> can be a string up to 16 characters long.
+  function tokenRawString(stream, state) {
+    // Escape characters that have special regex meanings.
+    var delim = state.cpp11RawStringDelim.replace(/[^\w\s]/g, '\\$&');
+    var match = stream.match(new RegExp(".*?\\)" + delim + '"'));
+    if (match)
+      state.tokenize = null;
+    else
+      stream.skipToEnd();
+    return "string";
+  }
+
+  function def(mimes, mode) {
+    if (typeof mimes == "string") mimes = [mimes];
+    var words = [];
+    function add(obj) {
+      if (obj) for (var prop in obj) if (obj.hasOwnProperty(prop))
+        words.push(prop);
+    }
+    add(mode.keywords);
+    add(mode.types);
+    add(mode.builtin);
+    add(mode.atoms);
+    if (words.length) {
+      mode.helperType = mimes[0];
+      CodeMirror.registerHelper("hintWords", mimes[0], words);
+    }
+
+    for (var i = 0; i < mimes.length; ++i)
+      CodeMirror.defineMIME(mimes[i], mode);
+  }
+
+  def(["text/x-csrc", "text/x-c", "text/x-chdr"], {
+    name: "clike",
+    keywords: words(cKeywords),
+    types: words(cTypes + " bool _Complex _Bool float_t double_t intptr_t intmax_t " +
+                 "int8_t int16_t int32_t int64_t uintptr_t uintmax_t uint8_t uint16_t " +
+                 "uint32_t uint64_t"),
+    blockKeywords: words("case do else for if switch while struct"),
+    defKeywords: words("struct"),
+    typeFirstDefinitions: true,
+    atoms: words("null true false"),
+    hooks: {"#": cppHook, "*": pointerHook},
+    modeProps: {fold: ["brace", "include"]}
+  });
+
+  def(["text/x-c++src", "text/x-c++hdr"], {
+    name: "clike",
+    keywords: words(cKeywords + " asm dynamic_cast namespace reinterpret_cast try explicit new " +
+                    "static_cast typeid catch operator template typename class friend private " +
+                    "this using const_cast inline public throw virtual delete mutable protected " +
+                    "alignas alignof constexpr decltype nullptr noexcept thread_local final " +
+                    "static_assert override"),
+    types: words(cTypes + " bool wchar_t"),
+    blockKeywords: words("catch class do else finally for if struct switch try while"),
+    defKeywords: words("class namespace struct enum union"),
+    typeFirstDefinitions: true,
+    atoms: words("true false null"),
+    hooks: {
+      "#": cppHook,
+      "*": pointerHook,
+      "u": cpp11StringHook,
+      "U": cpp11StringHook,
+      "L": cpp11StringHook,
+      "R": cpp11StringHook,
+      "0": cpp14Literal,
+      "1": cpp14Literal,
+      "2": cpp14Literal,
+      "3": cpp14Literal,
+      "4": cpp14Literal,
+      "5": cpp14Literal,
+      "6": cpp14Literal,
+      "7": cpp14Literal,
+      "8": cpp14Literal,
+      "9": cpp14Literal,
+      token: function(stream, state, style) {
+        if (style == "variable" && stream.peek() == "(" &&
+            (state.prevToken == ";" || state.prevToken == null ||
+             state.prevToken == "}") &&
+            cppLooksLikeConstructor(stream.current()))
+          return "def";
+      }
+    },
+    namespaceSeparator: "::",
+    modeProps: {fold: ["brace", "include"]}
+  });
+
+  def("text/x-java", {
+    name: "clike",
+    keywords: words("abstract assert break case catch class const continue default " +
+                    "do else enum extends final finally float for goto if implements import " +
+                    "instanceof interface native new package private protected public " +
+                    "return static strictfp super switch synchronized this throw throws transient " +
+                    "try volatile while"),
+    types: words("byte short int long float double boolean char void Boolean Byte Character Double Float " +
+                 "Integer Long Number Object Short String StringBuffer StringBuilder Void"),
+    blockKeywords: words("catch class do else finally for if switch try while"),
+    defKeywords: words("class interface package enum"),
+    typeFirstDefinitions: true,
+    atoms: words("true false null"),
+    endStatement: /^[;:]$/,
+    hooks: {
+      "@": function(stream) {
+        stream.eatWhile(/[\w\$_]/);
+        return "meta";
+      }
+    },
+    modeProps: {fold: ["brace", "import"]}
+  });
+
+  def("text/x-csharp", {
+    name: "clike",
+    keywords: words("abstract as async await base break case catch checked class const continue" +
+                    " default delegate do else enum event explicit extern finally fixed for" +
+                    " foreach goto if implicit in interface internal is lock namespace new" +
+                    " operator out override params private protected public readonly ref return sealed" +
+                    " sizeof stackalloc static struct switch this throw try typeof unchecked" +
+                    " unsafe using virtual void volatile while add alias ascending descending dynamic from get" +
+                    " global group into join let orderby partial remove select set value var yield"),
+    types: words("Action Boolean Byte Char DateTime DateTimeOffset Decimal Double Func" +
+                 " Guid Int16 Int32 Int64 Object SByte Single String Task TimeSpan UInt16 UInt32" +
+                 " UInt64 bool byte char decimal double short int long object"  +
+                 " sbyte float string ushort uint ulong"),
+    blockKeywords: words("catch class do else finally for foreach if struct switch try while"),
+    defKeywords: words("class interface namespace struct var"),
+    typeFirstDefinitions: true,
+    atoms: words("true false null"),
+    hooks: {
+      "@": function(stream, state) {
+        if (stream.eat('"')) {
+          state.tokenize = tokenAtString;
+          return tokenAtString(stream, state);
+        }
+        stream.eatWhile(/[\w\$_]/);
+        return "meta";
+      }
+    }
+  });
+
+  function tokenTripleString(stream, state) {
+    var escaped = false;
+    while (!stream.eol()) {
+      if (!escaped && stream.match('"""')) {
+        state.tokenize = null;
+        break;
+      }
+      escaped = stream.next() == "\\" && !escaped;
+    }
+    return "string";
+  }
+
+  def("text/x-scala", {
+    name: "clike",
+    keywords: words(
+
+      /* scala */
+      "abstract case catch class def do else extends final finally for forSome if " +
+      "implicit import lazy match new null object override package private protected return " +
+      "sealed super this throw trait try type val var while with yield _ : = => <- <: " +
+      "<% >: # @ " +
+
+      /* package scala */
+      "assert assume require print println printf readLine readBoolean readByte readShort " +
+      "readChar readInt readLong readFloat readDouble " +
+
+      ":: #:: "
+    ),
+    types: words(
+      "AnyVal App Application Array BufferedIterator BigDecimal BigInt Char Console Either " +
+      "Enumeration Equiv Error Exception Fractional Function IndexedSeq Int Integral Iterable " +
+      "Iterator List Map Numeric Nil NotNull Option Ordered Ordering PartialFunction PartialOrdering " +
+      "Product Proxy Range Responder Seq Serializable Set Specializable Stream StringBuilder " +
+      "StringContext Symbol Throwable Traversable TraversableOnce Tuple Unit Vector " +
+
+      /* package java.lang */
+      "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " +
+      "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " +
+      "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " +
+      "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"
+    ),
+    multiLineStrings: true,
+    blockKeywords: words("catch class do else finally for forSome if match switch try while"),
+    defKeywords: words("class def object package trait type val var"),
+    atoms: words("true false null"),
+    indentStatements: false,
+    indentSwitch: false,
+    hooks: {
+      "@": function(stream) {
+        stream.eatWhile(/[\w\$_]/);
+        return "meta";
+      },
+      '"': function(stream, state) {
+        if (!stream.match('""')) return false;
+        state.tokenize = tokenTripleString;
+        return state.tokenize(stream, state);
+      },
+      "'": function(stream) {
+        stream.eatWhile(/[\w\$_\xa1-\uffff]/);
+        return "atom";
+      },
+      "=": function(stream, state) {
+        var cx = state.context
+        if (cx.type == "}" && cx.align && stream.eat(">")) {
+          state.context = new Context(cx.indented, cx.column, cx.type, null, cx.prev)
+          return "operator"
+        } else {
+          return false
+        }
+      }
+    },
+    modeProps: {closeBrackets: {triples: '"'}}
+  });
+
+  function tokenKotlinString(tripleString){
+    return function (stream, state) {
+      var escaped = false, next, end = false;
+      while (!stream.eol()) {
+        if (!tripleString && !escaped && stream.match('"') ) {end = true; break;}
+        if (tripleString && stream.match('"""')) {end = true; break;}
+        next = stream.next();
+        if(!escaped && next == "$" && stream.match('{'))
+          stream.skipTo("}");
+        escaped = !escaped && next == "\\" && !tripleString;
+      }
+      if (end || !tripleString)
+        state.tokenize = null;
+      return "string";
+    }
+  }
+
+  def("text/x-kotlin", {
+    name: "clike",
+    keywords: words(
+      /*keywords*/
+      "package as typealias class interface this super val " +
+      "var fun for is in This throw return " +
+      "break continue object if else while do try when !in !is as? " +
+
+      /*soft keywords*/
+      "file import where by get set abstract enum open inner override private public internal " +
+      "protected catch finally out final vararg reified dynamic companion constructor init " +
+      "sealed field property receiver param sparam lateinit data inline noinline tailrec " +
+      "external annotation crossinline const operator infix"
+    ),
+    types: words(
+      /* package java.lang */
+      "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " +
+      "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " +
+      "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " +
+      "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"
+    ),
+    intendSwitch: false,
+    indentStatements: false,
+    multiLineStrings: true,
+    blockKeywords: words("catch class do else finally for if where try while enum"),
+    defKeywords: words("class val var object package interface fun"),
+    atoms: words("true false null this"),
+    hooks: {
+      '"': function(stream, state) {
+        state.tokenize = tokenKotlinString(stream.match('""'));
+        return state.tokenize(stream, state);
+      }
+    },
+    modeProps: {closeBrackets: {triples: '"'}}
+  });
+
+  def(["x-shader/x-vertex", "x-shader/x-fragment"], {
+    name: "clike",
+    keywords: words("sampler1D sampler2D sampler3D samplerCube " +
+                    "sampler1DShadow sampler2DShadow " +
+                    "const attribute uniform varying " +
+                    "break continue discard return " +
+                    "for while do if else struct " +
+                    "in out inout"),
+    types: words("float int bool void " +
+                 "vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4 " +
+                 "mat2 mat3 mat4"),
+    blockKeywords: words("for while do if else struct"),
+    builtin: words("radians degrees sin cos tan asin acos atan " +
+                    "pow exp log exp2 sqrt inversesqrt " +
+                    "abs sign floor ceil fract mod min max clamp mix step smoothstep " +
+                    "length distance dot cross normalize ftransform faceforward " +
+                    "reflect refract matrixCompMult " +
+                    "lessThan lessThanEqual greaterThan greaterThanEqual " +
+                    "equal notEqual any all not " +
+                    "texture1D texture1DProj texture1DLod texture1DProjLod " +
+                    "texture2D texture2DProj texture2DLod texture2DProjLod " +
+                    "texture3D texture3DProj texture3DLod texture3DProjLod " +
+                    "textureCube textureCubeLod " +
+                    "shadow1D shadow2D shadow1DProj shadow2DProj " +
+                    "shadow1DLod shadow2DLod shadow1DProjLod shadow2DProjLod " +
+                    "dFdx dFdy fwidth " +
+                    "noise1 noise2 noise3 noise4"),
+    atoms: words("true false " +
+                "gl_FragColor gl_SecondaryColor gl_Normal gl_Vertex " +
+                "gl_MultiTexCoord0 gl_MultiTexCoord1 gl_MultiTexCoord2 gl_MultiTexCoord3 " +
+                "gl_MultiTexCoord4 gl_MultiTexCoord5 gl_MultiTexCoord6 gl_MultiTexCoord7 " +
+                "gl_FogCoord gl_PointCoord " +
+                "gl_Position gl_PointSize gl_ClipVertex " +
+                "gl_FrontColor gl_BackColor gl_FrontSecondaryColor gl_BackSecondaryColor " +
+                "gl_TexCoord gl_FogFragCoord " +
+                "gl_FragCoord gl_FrontFacing " +
+                "gl_FragData gl_FragDepth " +
+                "gl_ModelViewMatrix gl_ProjectionMatrix gl_ModelViewProjectionMatrix " +
+                "gl_TextureMatrix gl_NormalMatrix gl_ModelViewMatrixInverse " +
+                "gl_ProjectionMatrixInverse gl_ModelViewProjectionMatrixInverse " +
+                "gl_TexureMatrixTranspose gl_ModelViewMatrixInverseTranspose " +
+                "gl_ProjectionMatrixInverseTranspose " +
+                "gl_ModelViewProjectionMatrixInverseTranspose " +
+                "gl_TextureMatrixInverseTranspose " +
+                "gl_NormalScale gl_DepthRange gl_ClipPlane " +
+                "gl_Point gl_FrontMaterial gl_BackMaterial gl_LightSource gl_LightModel " +
+                "gl_FrontLightModelProduct gl_BackLightModelProduct " +
+                "gl_TextureColor gl_EyePlaneS gl_EyePlaneT gl_EyePlaneR gl_EyePlaneQ " +
+                "gl_FogParameters " +
+                "gl_MaxLights gl_MaxClipPlanes gl_MaxTextureUnits gl_MaxTextureCoords " +
+                "gl_MaxVertexAttribs gl_MaxVertexUniformComponents gl_MaxVaryingFloats " +
+                "gl_MaxVertexTextureImageUnits gl_MaxTextureImageUnits " +
+                "gl_MaxFragmentUniformComponents gl_MaxCombineTextureImageUnits " +
+                "gl_MaxDrawBuffers"),
+    indentSwitch: false,
+    hooks: {"#": cppHook},
+    modeProps: {fold: ["brace", "include"]}
+  });
+
+  def("text/x-nesc", {
+    name: "clike",
+    keywords: words(cKeywords + "as atomic async call command component components configuration event generic " +
+                    "implementation includes interface module new norace nx_struct nx_union post provides " +
+                    "signal task uses abstract extends"),
+    types: words(cTypes),
+    blockKeywords: words("case do else for if switch while struct"),
+    atoms: words("null true false"),
+    hooks: {"#": cppHook},
+    modeProps: {fold: ["brace", "include"]}
+  });
+
+  def("text/x-objectivec", {
+    name: "clike",
+    keywords: words(cKeywords + "inline restrict _Bool _Complex _Imaginery BOOL Class bycopy byref id IMP in " +
+                    "inout nil oneway out Protocol SEL self super atomic nonatomic retain copy readwrite readonly"),
+    types: words(cTypes),
+    atoms: words("YES NO NULL NILL ON OFF true false"),
+    hooks: {
+      "@": function(stream) {
+        stream.eatWhile(/[\w\$]/);
+        return "keyword";
+      },
+      "#": cppHook,
+      indent: function(_state, ctx, textAfter) {
+        if (ctx.type == "statement" && /^@\w/.test(textAfter)) return ctx.indented
+      }
+    },
+    modeProps: {fold: "brace"}
+  });
+
+  def("text/x-squirrel", {
+    name: "clike",
+    keywords: words("base break clone continue const default delete enum extends function in class" +
+                    " foreach local resume return this throw typeof yield constructor instanceof static"),
+    types: words(cTypes),
+    blockKeywords: words("case catch class else for foreach if switch try while"),
+    defKeywords: words("function local class"),
+    typeFirstDefinitions: true,
+    atoms: words("true false null"),
+    hooks: {"#": cppHook},
+    modeProps: {fold: ["brace", "include"]}
+  });
+
+  // Ceylon Strings need to deal with interpolation
+  var stringTokenizer = null;
+  function tokenCeylonString(type) {
+    return function(stream, state) {
+      var escaped = false, next, end = false;
+      while (!stream.eol()) {
+        if (!escaped && stream.match('"') &&
+              (type == "single" || stream.match('""'))) {
+          end = true;
+          break;
+        }
+        if (!escaped && stream.match('``')) {
+          stringTokenizer = tokenCeylonString(type);
+          end = true;
+          break;
+        }
+        next = stream.next();
+        escaped = type == "single" && !escaped && next == "\\";
+      }
+      if (end)
+          state.tokenize = null;
+      return "string";
+    }
+  }
+
+  def("text/x-ceylon", {
+    name: "clike",
+    keywords: words("abstracts alias assembly assert assign break case catch class continue dynamic else" +
+                    " exists extends finally for function given if import in interface is let module new" +
+                    " nonempty object of out outer package return satisfies super switch then this throw" +
+                    " try value void while"),
+    types: function(word) {
+        // In Ceylon all identifiers that start with an uppercase are types
+        var first = word.charAt(0);
+        return (first === first.toUpperCase() && first !== first.toLowerCase());
+    },
+    blockKeywords: words("case catch class dynamic else finally for function if interface module new object switch try while"),
+    defKeywords: words("class dynamic function interface module object package value"),
+    builtin: words("abstract actual aliased annotation by default deprecated doc final formal late license" +
+                   " native optional sealed see serializable shared suppressWarnings tagged throws variable"),
+    isPunctuationChar: /[\[\]{}\(\),;\:\.`]/,
+    isOperatorChar: /[+\-*&%=<>!?|^~:\/]/,
+    numberStart: /[\d#$]/,
+    number: /^(?:#[\da-fA-F_]+|\$[01_]+|[\d_]+[kMGTPmunpf]?|[\d_]+\.[\d_]+(?:[eE][-+]?\d+|[kMGTPmunpf]|)|)/i,
+    multiLineStrings: true,
+    typeFirstDefinitions: true,
+    atoms: words("true false null larger smaller equal empty finished"),
+    indentSwitch: false,
+    styleDefs: false,
+    hooks: {
+      "@": function(stream) {
+        stream.eatWhile(/[\w\$_]/);
+        return "meta";
+      },
+      '"': function(stream, state) {
+          state.tokenize = tokenCeylonString(stream.match('""') ? "triple" : "single");
+          return state.tokenize(stream, state);
+        },
+      '`': function(stream, state) {
+          if (!stringTokenizer || !stream.match('`')) return false;
+          state.tokenize = stringTokenizer;
+          stringTokenizer = null;
+          return state.tokenize(stream, state);
+        },
+      "'": function(stream) {
+        stream.eatWhile(/[\w\$_\xa1-\uffff]/);
+        return "atom";
+      },
+      token: function(_stream, state, style) {
+          if ((style == "variable" || style == "variable-3") &&
+              state.prevToken == ".") {
+            return "variable-2";
+          }
+        }
+    },
+    modeProps: {
+        fold: ["brace", "import"],
+        closeBrackets: {triples: '"'}
+    }
+  });
+
+});

+ 51 - 0
xxl-job-admin/src/main/webapp/static/plugins/codemirror/mode/clike/test.js

@@ -0,0 +1,51 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function() {
+  var mode = CodeMirror.getMode({indentUnit: 2}, "text/x-c");
+  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }
+
+  MT("indent",
+     "[variable-3 void] [def foo]([variable-3 void*] [variable a], [variable-3 int] [variable b]) {",
+     "  [variable-3 int] [variable c] [operator =] [variable b] [operator +]",
+     "    [number 1];",
+     "  [keyword return] [operator *][variable a];",
+     "}");
+
+  MT("indent_switch",
+     "[keyword switch] ([variable x]) {",
+     "  [keyword case] [number 10]:",
+     "    [keyword return] [number 20];",
+     "  [keyword default]:",
+     "    [variable printf]([string \"foo %c\"], [variable x]);",
+     "}");
+
+  MT("def",
+     "[variable-3 void] [def foo]() {}",
+     "[keyword struct] [def bar]{}",
+     "[variable-3 int] [variable-3 *][def baz]() {}");
+
+  MT("double_block",
+     "[keyword for] (;;)",
+     "  [keyword for] (;;)",
+     "    [variable x][operator ++];",
+     "[keyword return];");
+
+  MT("preprocessor",
+     "[meta #define FOO 3]",
+     "[variable-3 int] [variable foo];",
+     "[meta #define BAR\\]",
+     "[meta 4]",
+     "[variable-3 unsigned] [variable-3 int] [variable bar] [operator =] [number 8];",
+     "[meta #include <baz> ][comment // comment]")
+
+
+  var mode_cpp = CodeMirror.getMode({indentUnit: 2}, "text/x-c++src");
+  function MTCPP(name) { test.mode(name, mode_cpp, Array.prototype.slice.call(arguments, 1)); }
+
+  MTCPP("cpp14_literal",
+    "[number 10'000];",
+    "[number 0b10'000];",
+    "[number 0x10'000];",
+    "[string '100000'];");
+})();

+ 230 - 0
xxl-job-admin/src/main/webapp/static/plugins/codemirror/mode/groovy/groovy.js

@@ -0,0 +1,230 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+CodeMirror.defineMode("groovy", function(config) {
+  function words(str) {
+    var obj = {}, words = str.split(" ");
+    for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
+    return obj;
+  }
+  var keywords = words(
+    "abstract as assert boolean break byte case catch char class const continue def default " +
+    "do double else enum extends final finally float for goto if implements import in " +
+    "instanceof int interface long native new package private protected public return " +
+    "short static strictfp super switch synchronized threadsafe throw throws transient " +
+    "try void volatile while");
+  var blockKeywords = words("catch class do else finally for if switch try while enum interface def");
+  var standaloneKeywords = words("return break continue");
+  var atoms = words("null true false this");
+
+  var curPunc;
+  function tokenBase(stream, state) {
+    var ch = stream.next();
+    if (ch == '"' || ch == "'") {
+      return startString(ch, stream, state);
+    }
+    if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
+      curPunc = ch;
+      return null;
+    }
+    if (/\d/.test(ch)) {
+      stream.eatWhile(/[\w\.]/);
+      if (stream.eat(/eE/)) { stream.eat(/\+\-/); stream.eatWhile(/\d/); }
+      return "number";
+    }
+    if (ch == "/") {
+      if (stream.eat("*")) {
+        state.tokenize.push(tokenComment);
+        return tokenComment(stream, state);
+      }
+      if (stream.eat("/")) {
+        stream.skipToEnd();
+        return "comment";
+      }
+      if (expectExpression(state.lastToken, false)) {
+        return startString(ch, stream, state);
+      }
+    }
+    if (ch == "-" && stream.eat(">")) {
+      curPunc = "->";
+      return null;
+    }
+    if (/[+\-*&%=<>!?|\/~]/.test(ch)) {
+      stream.eatWhile(/[+\-*&%=<>|~]/);
+      return "operator";
+    }
+    stream.eatWhile(/[\w\$_]/);
+    if (ch == "@") { stream.eatWhile(/[\w\$_\.]/); return "meta"; }
+    if (state.lastToken == ".") return "property";
+    if (stream.eat(":")) { curPunc = "proplabel"; return "property"; }
+    var cur = stream.current();
+    if (atoms.propertyIsEnumerable(cur)) { return "atom"; }
+    if (keywords.propertyIsEnumerable(cur)) {
+      if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement";
+      else if (standaloneKeywords.propertyIsEnumerable(cur)) curPunc = "standalone";
+      return "keyword";
+    }
+    return "variable";
+  }
+  tokenBase.isBase = true;
+
+  function startString(quote, stream, state) {
+    var tripleQuoted = false;
+    if (quote != "/" && stream.eat(quote)) {
+      if (stream.eat(quote)) tripleQuoted = true;
+      else return "string";
+    }
+    function t(stream, state) {
+      var escaped = false, next, end = !tripleQuoted;
+      while ((next = stream.next()) != null) {
+        if (next == quote && !escaped) {
+          if (!tripleQuoted) { break; }
+          if (stream.match(quote + quote)) { end = true; break; }
+        }
+        if (quote == '"' && next == "$" && !escaped && stream.eat("{")) {
+          state.tokenize.push(tokenBaseUntilBrace());
+          return "string";
+        }
+        escaped = !escaped && next == "\\";
+      }
+      if (end) state.tokenize.pop();
+      return "string";
+    }
+    state.tokenize.push(t);
+    return t(stream, state);
+  }
+
+  function tokenBaseUntilBrace() {
+    var depth = 1;
+    function t(stream, state) {
+      if (stream.peek() == "}") {
+        depth--;
+        if (depth == 0) {
+          state.tokenize.pop();
+          return state.tokenize[state.tokenize.length-1](stream, state);
+        }
+      } else if (stream.peek() == "{") {
+        depth++;
+      }
+      return tokenBase(stream, state);
+    }
+    t.isBase = true;
+    return t;
+  }
+
+  function tokenComment(stream, state) {
+    var maybeEnd = false, ch;
+    while (ch = stream.next()) {
+      if (ch == "/" && maybeEnd) {
+        state.tokenize.pop();
+        break;
+      }
+      maybeEnd = (ch == "*");
+    }
+    return "comment";
+  }
+
+  function expectExpression(last, newline) {
+    return !last || last == "operator" || last == "->" || /[\.\[\{\(,;:]/.test(last) ||
+      last == "newstatement" || last == "keyword" || last == "proplabel" ||
+      (last == "standalone" && !newline);
+  }
+
+  function Context(indented, column, type, align, prev) {
+    this.indented = indented;
+    this.column = column;
+    this.type = type;
+    this.align = align;
+    this.prev = prev;
+  }
+  function pushContext(state, col, type) {
+    return state.context = new Context(state.indented, col, type, null, state.context);
+  }
+  function popContext(state) {
+    var t = state.context.type;
+    if (t == ")" || t == "]" || t == "}")
+      state.indented = state.context.indented;
+    return state.context = state.context.prev;
+  }
+
+  // Interface
+
+  return {
+    startState: function(basecolumn) {
+      return {
+        tokenize: [tokenBase],
+        context: new Context((basecolumn || 0) - config.indentUnit, 0, "top", false),
+        indented: 0,
+        startOfLine: true,
+        lastToken: null
+      };
+    },
+
+    token: function(stream, state) {
+      var ctx = state.context;
+      if (stream.sol()) {
+        if (ctx.align == null) ctx.align = false;
+        state.indented = stream.indentation();
+        state.startOfLine = true;
+        // Automatic semicolon insertion
+        if (ctx.type == "statement" && !expectExpression(state.lastToken, true)) {
+          popContext(state); ctx = state.context;
+        }
+      }
+      if (stream.eatSpace()) return null;
+      curPunc = null;
+      var style = state.tokenize[state.tokenize.length-1](stream, state);
+      if (style == "comment") return style;
+      if (ctx.align == null) ctx.align = true;
+
+      if ((curPunc == ";" || curPunc == ":") && ctx.type == "statement") popContext(state);
+      // Handle indentation for {x -> \n ... }
+      else if (curPunc == "->" && ctx.type == "statement" && ctx.prev.type == "}") {
+        popContext(state);
+        state.context.align = false;
+      }
+      else if (curPunc == "{") pushContext(state, stream.column(), "}");
+      else if (curPunc == "[") pushContext(state, stream.column(), "]");
+      else if (curPunc == "(") pushContext(state, stream.column(), ")");
+      else if (curPunc == "}") {
+        while (ctx.type == "statement") ctx = popContext(state);
+        if (ctx.type == "}") ctx = popContext(state);
+        while (ctx.type == "statement") ctx = popContext(state);
+      }
+      else if (curPunc == ctx.type) popContext(state);
+      else if (ctx.type == "}" || ctx.type == "top" || (ctx.type == "statement" && curPunc == "newstatement"))
+        pushContext(state, stream.column(), "statement");
+      state.startOfLine = false;
+      state.lastToken = curPunc || style;
+      return style;
+    },
+
+    indent: function(state, textAfter) {
+      if (!state.tokenize[state.tokenize.length-1].isBase) return 0;
+      var firstChar = textAfter && textAfter.charAt(0), ctx = state.context;
+      if (ctx.type == "statement" && !expectExpression(state.lastToken, true)) ctx = ctx.prev;
+      var closing = firstChar == ctx.type;
+      if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : config.indentUnit);
+      else if (ctx.align) return ctx.column + (closing ? 0 : 1);
+      else return ctx.indented + (closing ? 0 : config.indentUnit);
+    },
+
+    electricChars: "{}",
+    closeBrackets: {triples: "'\""},
+    fold: "brace"
+  };
+});
+
+CodeMirror.defineMIME("text/x-groovy", "groovy");
+
+});

+ 10 - 6
xxl-job-client/src/main/java/com/xxl/job/client/handler/HandlerThread.java

@@ -78,6 +78,7 @@ public class HandlerThread extends Thread{
 					String _msg = null;
 					String _msg = null;
 					try {
 					try {
 						XxlJobFileAppender.contextHolder.set(trigger_log_id);
 						XxlJobFileAppender.contextHolder.set(trigger_log_id);
+						logger.info(">>>>>>>>>>> xxl-job handle start.");
 						_status = handler.handle(handlerParams);
 						_status = handler.handle(handlerParams);
 					} catch (Exception e) {
 					} catch (Exception e) {
 						logger.info("HandlerThread Exception:", e);
 						logger.info("HandlerThread Exception:", e);
@@ -85,20 +86,23 @@ public class HandlerThread extends Thread{
 						e.printStackTrace(new PrintWriter(out));
 						e.printStackTrace(new PrintWriter(out));
 						_msg = out.toString();
 						_msg = out.toString();
 					}
 					}
+					logger.info(">>>>>>>>>>> xxl-job handle end, handlerParams:{}, _status:{}, _msg:{}", 
+							new Object[]{handlerParams, _status, _msg});
 
 
 					// callback handler info
 					// callback handler info
+					HashMap<String, String> params = new HashMap<String, String>();
+					params.put("trigger_log_id", trigger_log_id);
+					params.put("status", _status.name());
+					params.put("msg", _msg);
 					RemoteCallBack callback = null;
 					RemoteCallBack callback = null;
+					logger.info(">>>>>>>>>>> xxl-job callback start.");
 					try {
 					try {
-						HashMap<String, String> params = new HashMap<String, String>();
-						params.put("trigger_log_id", trigger_log_id);
-						params.put("status", _status.name());
-						params.put("msg", _msg);
 						callback = HttpUtil.post(trigger_log_url, params);
 						callback = HttpUtil.post(trigger_log_url, params);
 					} catch (Exception e) {
 					} catch (Exception e) {
 						logger.info("HandlerThread Exception:", e);
 						logger.info("HandlerThread Exception:", e);
 					}
 					}
-					logger.info(">>>>>>>>>>> xxl-job thread handle, handlerData:{}, callback_status:{}, callback_msg:{}, callback:{}, thread:{}", 
-							new Object[]{handlerData, _status, _msg, callback, this});
+					logger.info(">>>>>>>>>>> xxl-job callback end, params:{}, result:{}", new Object[]{params, callback.toString()});
+					
 				} else {
 				} else {
 					i++;
 					i++;
 					logIdSet.clear();
 					logIdSet.clear();

+ 6 - 0
xxl-job-client/src/main/java/com/xxl/job/client/util/HttpUtil.java

@@ -46,6 +46,12 @@ public class HttpUtil {
 		public String getMsg() {
 		public String getMsg() {
 			return msg;
 			return msg;
 		}
 		}
+		
+		@Override
+		public String toString() {
+			return "RemoteCallBack [status=" + status + ", msg=" + msg + "]";
+		}
+		
 	}
 	}
 	
 	
 	/**
 	/**