Browse Source

调度信息迁移至DB中

xueli.xue 9 years ago
parent
commit
7dfed8e62e

+ 72 - 8
xxl-job-admin/src/main/java/com/xxl/job/controller/JobController.java

@@ -7,6 +7,7 @@ import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
 
+import javax.annotation.Resource;
 import javax.servlet.http.HttpServletRequest;
 
 import org.apache.commons.lang.StringUtils;
@@ -16,11 +17,15 @@ import org.quartz.SchedulerException;
 import org.springframework.stereotype.Controller;
 import org.springframework.ui.Model;
 import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.ResponseBody;
 
 import com.xxl.job.client.handler.HandlerRepository;
+import com.xxl.job.client.util.JacksonUtil;
 import com.xxl.job.core.model.ReturnT;
+import com.xxl.job.core.model.XxlJobInfo;
 import com.xxl.job.core.util.DynamicSchedulerUtil;
+import com.xxl.job.dao.IXxlJobInfoDao;
 import com.xxl.job.service.job.HttpJobBean;
 
 /**
@@ -31,19 +36,47 @@ import com.xxl.job.service.job.HttpJobBean;
 @RequestMapping("/job")
 public class JobController {
 	
+	@Resource
+	private IXxlJobInfoDao xxlJobInfoDao;
+	
 	@RequestMapping
 	public String index(Model model) {
-		List<Map<String, Object>> jobList = DynamicSchedulerUtil.getJobList();
-		model.addAttribute("jobList", jobList);
+		//List<Map<String, Object>> jobList = DynamicSchedulerUtil.getJobList();
+		//model.addAttribute("jobList", jobList);
 		return "job/index";
 	}
 	
+	@RequestMapping("/pageList")
+	@ResponseBody
+	public Map<String, Object> pageList(@RequestParam(required = false, defaultValue = "0") int start,  
+			@RequestParam(required = false, defaultValue = "10") int length,
+			String jobName, String filterTime) {
+		
+		// page list
+		List<XxlJobInfo> list = xxlJobInfoDao.pageList(start, length, jobName, null, null);
+		int list_count = xxlJobInfoDao.pageListCount(start, length, jobName, null, null);
+		
+		// fill job info
+		if (list!=null && list.size()>0) {
+			for (XxlJobInfo jobInfo : list) {
+				DynamicSchedulerUtil.fillJobInfo(jobInfo);
+			}
+		}
+		
+		// package result
+		Map<String, Object> maps = new HashMap<String, Object>();
+	    maps.put("recordsTotal", list_count);		// 总记录数
+	    maps.put("recordsFiltered", list_count);	// 过滤后的总记录数
+	    maps.put("data", list);  					// 分页列表
+		return maps;
+	}
+	
 	@RequestMapping("/add")
 	@ResponseBody
 	public ReturnT<String> add(HttpServletRequest request) {
 		String triggerKeyName = null;
 		String cronExpression = null;
-		Map<String, Object> jobData = new HashMap<String, Object>();
+		Map<String, String> jobData = new HashMap<String, String>();
 		
 		try {
 			request.setCharacterEncoding("utf-8");
@@ -58,7 +91,7 @@ public class JobController {
 			} else if (param.getKey().equals("cronExpression")) {
 				cronExpression = param.getValue()[0];
 			} else {
-				jobData.put(param.getKey(), param.getValue().length>0?param.getValue()[0]:param.getValue());
+				jobData.put(param.getKey(), (String) (param.getValue().length>0?param.getValue()[0]:param.getValue()));
 			}
 		}
 		
@@ -90,10 +123,19 @@ public class JobController {
 		Class<? extends Job> jobClass = HttpJobBean.class;
 		
 		try {
-			boolean result = DynamicSchedulerUtil.addJob(triggerKeyName, cronExpression, jobClass, jobData);
+			// add job 2 quartz
+			boolean result = DynamicSchedulerUtil.addJob(triggerKeyName, cronExpression, jobClass, null);
 			if (!result) {
 				return new ReturnT<String>(500, "任务ID重复,请更换确认");
 			}
+			// Backup to the database
+			XxlJobInfo jobInfo = new XxlJobInfo();
+			jobInfo.setJobName(triggerKeyName);
+			jobInfo.setJobCron(cronExpression);
+			jobInfo.setJobClass(jobClass.getName());
+			jobInfo.setJobData(JacksonUtil.writeValueAsString(jobData));
+			xxlJobInfoDao.save(jobInfo);
+			
 			return ReturnT.SUCCESS;
 		} catch (SchedulerException e) {
 			e.printStackTrace();
@@ -117,6 +159,13 @@ public class JobController {
 		}
 		try {
 			DynamicSchedulerUtil.rescheduleJob(triggerKeyName, cronExpression);
+			
+			// update
+			XxlJobInfo jobInfo = xxlJobInfoDao.load(triggerKeyName);
+			if (jobInfo!=null) {
+				jobInfo.setJobCron(cronExpression);
+				xxlJobInfoDao.update(jobInfo);
+			}
 			return ReturnT.SUCCESS;
 		} catch (SchedulerException e) {
 			e.printStackTrace();
@@ -128,12 +177,15 @@ public class JobController {
 	@ResponseBody
 	public ReturnT<String> remove(String triggerKeyName) {
 		try {
-			DynamicSchedulerUtil.removeJob(triggerKeyName);
-			return ReturnT.SUCCESS;
+			if (triggerKeyName!=null) {
+				DynamicSchedulerUtil.removeJob(triggerKeyName);
+				xxlJobInfoDao.delete(triggerKeyName);
+				return ReturnT.SUCCESS;
+			}
 		} catch (SchedulerException e) {
 			e.printStackTrace();
-			return ReturnT.FAIL;
 		}
+		return ReturnT.FAIL;
 	}
 	
 	@RequestMapping("/pause")
@@ -141,6 +193,12 @@ public class JobController {
 	public ReturnT<String> pause(String triggerKeyName) {
 		try {
 			DynamicSchedulerUtil.pauseJob(triggerKeyName);
+			// update
+			XxlJobInfo jobInfo = xxlJobInfoDao.load(triggerKeyName);
+			if (jobInfo!=null) {
+				jobInfo.setJobStatus("PAUSED");
+				xxlJobInfoDao.update(jobInfo);
+			}
 			return ReturnT.SUCCESS;
 		} catch (SchedulerException e) {
 			e.printStackTrace();
@@ -153,6 +211,12 @@ public class JobController {
 	public ReturnT<String> resume(String triggerKeyName) {
 		try {
 			DynamicSchedulerUtil.resumeJob(triggerKeyName);
+			// update
+			XxlJobInfo jobInfo = xxlJobInfoDao.load(triggerKeyName);
+			if (jobInfo!=null) {
+				jobInfo.setJobStatus("NORMAL");
+				xxlJobInfoDao.update(jobInfo);
+			}
 			return ReturnT.SUCCESS;
 		} catch (SchedulerException e) {
 			e.printStackTrace();

+ 77 - 0
xxl-job-admin/src/main/java/com/xxl/job/core/model/XxlJobInfo.java

@@ -0,0 +1,77 @@
+package com.xxl.job.core.model;
+
+import java.util.Date;
+
+/**
+ * xxl-job info
+ * @author xuxueli  2016-1-12 18:25:49
+ */
+public class XxlJobInfo {
+	
+	private int id;
+	// job info
+	private String jobName;
+	private String jobCron;		// base on quartz
+	private String jobClass;	// base on quartz
+	private String jobStatus;	// base on quartz
+	private String jobData;		// base on db, Map-JSON-String
+	private Date addTime;
+	private Date updateTime;
+	
+	public int getId() {
+		return id;
+	}
+	public void setId(int id) {
+		this.id = id;
+	}
+	public String getJobName() {
+		return jobName;
+	}
+	public void setJobName(String jobName) {
+		this.jobName = jobName;
+	}
+	public String getJobCron() {
+		return jobCron;
+	}
+	public void setJobCron(String jobCron) {
+		this.jobCron = jobCron;
+	}
+	public String getJobClass() {
+		return jobClass;
+	}
+	public void setJobClass(String jobClass) {
+		this.jobClass = jobClass;
+	}
+	public String getJobStatus() {
+		return jobStatus;
+	}
+	public void setJobStatus(String jobStatus) {
+		this.jobStatus = jobStatus;
+	}
+	public String getJobData() {
+		return jobData;
+	}
+	public void setJobData(String jobData) {
+		this.jobData = jobData;
+	}
+	public Date getAddTime() {
+		return addTime;
+	}
+	public void setAddTime(Date addTime) {
+		this.addTime = addTime;
+	}
+	public Date getUpdateTime() {
+		return updateTime;
+	}
+	public void setUpdateTime(Date updateTime) {
+		this.updateTime = updateTime;
+	}
+	
+	@Override
+	public String toString() {
+		return "XxlJobInfo [id=" + id + ", jobName=" + jobName + ", jobCron=" + jobCron + ", jobClass=" + jobClass
+				+ ", jobStatus=" + jobStatus + ", jobData=" + jobData + ", addTime=" + addTime + ", updateTime="
+				+ updateTime + "]";
+	}
+	
+}

+ 36 - 2
xxl-job-admin/src/main/java/com/xxl/job/core/util/DynamicSchedulerUtil.java

@@ -23,11 +23,14 @@ import org.quartz.Trigger.TriggerState;
 import org.quartz.TriggerBuilder;
 import org.quartz.TriggerKey;
 import org.quartz.impl.matchers.GroupMatcher;
+import org.quartz.impl.triggers.CronTriggerImpl;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.InitializingBean;
 import org.springframework.util.Assert;
 
+import com.xxl.job.core.model.XxlJobInfo;
+import com.xxl.job.dao.IXxlJobInfoDao;
 import com.xxl.job.dao.IXxlJobLogDao;
 
 /**
@@ -43,8 +46,11 @@ public final class DynamicSchedulerUtil implements InitializingBean {
     public void setXxlJobLogDao(IXxlJobLogDao xxlJobLogDao) {
 		DynamicSchedulerUtil.xxlJobLogDao = xxlJobLogDao;
 	}
-    public static IXxlJobLogDao getXxlJobLogDao() {
-		return xxlJobLogDao;
+    // xxlJobInfoDao
+    public static IXxlJobInfoDao xxlJobInfoDao;
+    @Resource
+    public void setXxlJobInfoDao(IXxlJobInfoDao xxlJobInfoDao) {
+		DynamicSchedulerUtil.xxlJobInfoDao = xxlJobInfoDao;
 	}
     
     // Scheduler
@@ -90,6 +96,34 @@ public final class DynamicSchedulerUtil implements InitializingBean {
 		}
 		return jobList;
 	}
+	
+	// fill job info
+	public static void fillJobInfo(XxlJobInfo jobInfo) {
+		// TriggerKey : name + group
+        TriggerKey triggerKey = TriggerKey.triggerKey(jobInfo.getJobName(), Scheduler.DEFAULT_GROUP);
+        JobKey jobKey = new JobKey(jobInfo.getJobName(), Scheduler.DEFAULT_GROUP);
+        try {
+			Trigger trigger = scheduler.getTrigger(triggerKey);
+			JobDetail jobDetail = scheduler.getJobDetail(jobKey);
+			TriggerState triggerState = scheduler.getTriggerState(triggerKey);
+			
+			// parse params
+			if (trigger!=null && trigger instanceof CronTriggerImpl) {
+				String cronExpression = ((CronTriggerImpl) trigger).getCronExpression();
+				jobInfo.setJobCron(cronExpression);
+			}
+			if (jobDetail!=null) {
+				String jobClass = jobDetail.getJobClass().getName();
+				jobInfo.setJobClass(jobClass);
+			}
+			if (triggerState!=null) {
+				jobInfo.setJobStatus(triggerState.name());
+			}
+			
+		} catch (SchedulerException e) {
+			e.printStackTrace();
+		}
+	}
 
 	// addJob 新增
     public static boolean addJob(String triggerKeyName, String cronExpression, Class<? extends Job> jobClass, Map<String, Object> jobData) throws SchedulerException {

+ 25 - 0
xxl-job-admin/src/main/java/com/xxl/job/dao/IXxlJobInfoDao.java

@@ -0,0 +1,25 @@
+package com.xxl.job.dao;
+
+import java.util.Date;
+import java.util.List;
+
+import com.xxl.job.core.model.XxlJobInfo;
+
+/**
+ * job info
+ * @author xuxueli 2016-1-12 18:03:45
+ */
+public interface IXxlJobInfoDao {
+
+	public List<XxlJobInfo> pageList(int offset, int pagesize, String jobName, Date addTimeStart, Date addTimeEnd);
+	public int pageListCount(int offset, int pagesize, String jobName, Date addTimeStart, Date addTimeEnd);
+	
+	public int save(XxlJobInfo info);
+	
+	public XxlJobInfo load(String jobName);
+	
+	public int update(XxlJobInfo item);
+	
+	public int delete(String jobName);
+	
+}

+ 4 - 0
xxl-job-admin/src/main/java/com/xxl/job/dao/IXxlJobLogDao.java

@@ -6,6 +6,10 @@ import java.util.List;
 
 import com.xxl.job.core.model.XxlJobLog;
 
+/**
+ * job log
+ * @author xuxueli 2016-1-12 18:03:06
+ */
 public interface IXxlJobLogDao {
 	
 	public int save(XxlJobLog xxlJobLog);

+ 69 - 0
xxl-job-admin/src/main/java/com/xxl/job/dao/impl/XxlJobInfoDaoImpl.java

@@ -0,0 +1,69 @@
+package com.xxl.job.dao.impl;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+
+import javax.annotation.Resource;
+
+import org.mybatis.spring.SqlSessionTemplate;
+import org.springframework.stereotype.Repository;
+
+import com.xxl.job.core.model.XxlJobInfo;
+import com.xxl.job.dao.IXxlJobInfoDao;
+
+/**
+ * job info
+ * @author xuxueli 2016-1-12 18:03:45
+ */
+@Repository
+public class XxlJobInfoDaoImpl implements IXxlJobInfoDao {
+	
+	@Resource
+	public SqlSessionTemplate sqlSessionTemplate;
+
+	@Override
+	public List<XxlJobInfo> pageList(int offset, int pagesize, String jobName, Date addTimeStart, Date addTimeEnd) {
+		HashMap<String, Object> params = new HashMap<String, Object>();
+		params.put("offset", offset);
+		params.put("pagesize", pagesize);
+		params.put("jobName", jobName);
+		params.put("addTimeStart", addTimeStart);
+		params.put("addTimeEnd", addTimeEnd);
+		
+		return sqlSessionTemplate.selectList("XxlJobInfoMapper.pageList", params);
+	}
+
+	@Override
+	public int pageListCount(int offset, int pagesize, String jobName, Date addTimeStart, Date addTimeEnd) {
+		HashMap<String, Object> params = new HashMap<String, Object>();
+		params.put("offset", offset);
+		params.put("pagesize", pagesize);
+		params.put("jobName", jobName);
+		params.put("addTimeStart", addTimeStart);
+		params.put("addTimeEnd", addTimeEnd);
+		
+		return sqlSessionTemplate.selectOne("XxlJobInfoMapper.pageListCount", params);
+	}
+
+	@Override
+	public int save(XxlJobInfo info) {
+		return sqlSessionTemplate.insert("XxlJobInfoMapper.save", info);
+	}
+
+	@Override
+	public XxlJobInfo load(String jobName) {
+		return sqlSessionTemplate.selectOne("XxlJobInfoMapper.load", jobName);
+	}
+
+	@Override
+	public int update(XxlJobInfo item) {
+		return sqlSessionTemplate.update("XxlJobInfoMapper.update", item);
+	}
+
+	@Override
+	public int delete(String jobName) {
+		return sqlSessionTemplate.update("XxlJobInfoMapper.delete", jobName);
+	}
+	
+}

+ 4 - 0
xxl-job-admin/src/main/java/com/xxl/job/dao/impl/XxlJobLogDaoImpl.java

@@ -12,6 +12,10 @@ import org.springframework.stereotype.Repository;
 import com.xxl.job.core.model.XxlJobLog;
 import com.xxl.job.dao.IXxlJobLogDao;
 
+/**
+ * job log
+ * @author xuxueli 2016-1-12 18:03:06
+ */
 @Repository
 public class XxlJobLogDaoImpl implements IXxlJobLogDao {
 	

+ 8 - 10
xxl-job-admin/src/main/java/com/xxl/job/service/job/HttpJobBean.java

@@ -3,7 +3,6 @@ package com.xxl.job.service.job;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.Map;
-import java.util.Map.Entry;
 
 import org.apache.commons.lang.StringUtils;
 import org.quartz.JobExecutionContext;
@@ -16,6 +15,7 @@ import org.springframework.scheduling.quartz.QuartzJobBean;
 import com.xxl.job.client.handler.HandlerRepository;
 import com.xxl.job.client.util.HttpUtil;
 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.util.DynamicSchedulerUtil;
 import com.xxl.job.core.util.PropertiesUtil;
@@ -27,18 +27,17 @@ import com.xxl.job.core.util.PropertiesUtil;
 public class HttpJobBean extends QuartzJobBean {
 	private static Logger logger = LoggerFactory.getLogger(HttpJobBean.class);
 
+	@SuppressWarnings("unchecked")
 	@Override
 	protected void executeInternal(JobExecutionContext context)
 			throws JobExecutionException {
-		
 		String triggerKey = context.getTrigger().getJobKey().getName();
+		
 		// jobDataMap 2 params
-		Map<String, Object> jobDataMap = context.getMergedJobDataMap().getWrappedMap();
 		Map<String, String> params = new HashMap<String, String>();
-		if (jobDataMap!=null && jobDataMap.size()>0) {
-			for (Entry<String, Object> item : jobDataMap.entrySet()) {
-				params.put(item.getKey(), String.valueOf(item.getValue()));
-			}
+		XxlJobInfo jobInfo = DynamicSchedulerUtil.xxlJobInfoDao.load(triggerKey);
+		if (jobInfo!=null && jobInfo.getJobData()!=null) {
+			params = JacksonUtil.readValue(jobInfo.getJobData(), Map.class);
 		}
 		
 		// corn
@@ -53,7 +52,7 @@ public class HttpJobBean extends QuartzJobBean {
 		jobLog.setJobName(triggerKey);
 		jobLog.setJobCron(cornExp);
 		jobLog.setJobClass(HttpJobBean.class.getName());
-		jobLog.setJobData(JacksonUtil.writeValueAsString(params));
+		jobLog.setJobData(jobInfo.getJobData());
 		DynamicSchedulerUtil.xxlJobLogDao.save(jobLog);
 		logger.info(">>>>>>>>>>> xxl-job trigger start, jobLog:{}", jobLog);
 		
@@ -70,8 +69,7 @@ public class HttpJobBean extends QuartzJobBean {
 		jobLog.setTriggerTime(new Date());
 		jobLog.setTriggerStatus(HttpUtil.FAIL);
 		jobLog.setTriggerMsg("[responseMsg]:"+responseMsg+"<br>[exceptionMsg]:"+exceptionMsg);
-		if (StringUtils.isNotBlank(responseMsg)) {
-			@SuppressWarnings("unchecked")
+		if (StringUtils.isNotBlank(responseMsg) && responseMsg.indexOf("{")>-1 ) {
 			Map<String, String> responseMap = JacksonUtil.readValue(responseMsg, Map.class);
 			if (responseMap!=null && StringUtils.isNotBlank(responseMap.get(HttpUtil.status))) {
 				jobLog.setTriggerStatus(responseMap.get(HttpUtil.status));

+ 1 - 1
xxl-job-admin/src/main/resources/applicationcontext-database.xml

@@ -31,7 +31,7 @@
 	
 	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
 		<property name="dataSource" ref="dataSource" />
-		<property name="mapperLocations" value="classpath*:com/xxl/job/core/model/mapper/*.xml"/>
+		<property name="mapperLocations" value="classpath*:mybatis-mapper/*.xml"/>
 	</bean>
     
     <!-- scope must be "prototype" when junit -->

+ 102 - 0
xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobInfoMapper.xml

@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 
+	"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="XxlJobInfoMapper">
+	
+	<resultMap id="XxlJobInfo" type="com.xxl.job.core.model.XxlJobInfo" >
+		<result column="id" property="id" />
+	
+	    <result column="job_name" property="jobName" />
+	    <result column="job_cron" property="jobCron" />
+	    <result column="job_class" property="jobClass" />
+	    <result column="job_data" property="jobData" />
+	    
+	    <result column="add_time" property="addTime" />
+	    <result column="update_time" property="updateTime" />
+	</resultMap>
+
+	<sql id="Base_Column_List">
+		t.id,
+		t.job_name,
+		t.job_cron,
+		t.job_class,
+		t.job_data,
+		t.add_time,
+		t.update_time
+	</sql>
+	
+	<select id="pageList" parameterType="java.util.HashMap" resultMap="XxlJobInfo">
+		SELECT <include refid="Base_Column_List" />
+		FROM xxl_job_qrtz_trigger_info AS t
+		<trim prefix="WHERE" prefixOverrides="AND | OR" >
+			<if test="jobName != null and jobName!=''">
+				AND t.job_name = #{jobName}
+			</if>
+			<if test="addTimeStart != null">
+				AND t.add_time <![CDATA[ > ]]> #{addTimeStart}
+			</if>
+			<if test="addTimeEnd != null">
+				AND t.add_time <![CDATA[ < ]]> #{addTimeEnd}
+			</if>
+		</trim>
+		ORDER BY id DESC
+		LIMIT #{offset}, #{pagesize}
+	</select>
+	
+	<select id="pageListCount" parameterType="java.util.HashMap" resultType="int">
+		SELECT count(1)
+		FROM xxl_job_qrtz_trigger_info AS t
+		<trim prefix="WHERE" prefixOverrides="AND | OR" >
+			<if test="jobName != null and jobName!=''">
+				AND t.job_name = #{jobName}
+			</if>
+			<if test="addTimeStart != null">
+				AND t.add_time <![CDATA[ > ]]> #{addTimeStart}
+			</if>
+			<if test="addTimeEnd != null">
+				AND t.add_time <![CDATA[ < ]]> #{addTimeEnd}
+			</if>
+		</trim>
+	</select>
+	
+	<insert id="save" parameterType="com.xxl.job.core.model.XxlJobInfo" useGeneratedKeys="true" keyProperty="id" >
+		INSERT INTO `xxl_job_qrtz_trigger_info` (
+			`job_name`, 
+			`job_cron`, 
+			`job_class`, 
+			`job_data`,
+			`add_time`,
+			`update_time`
+		) VALUES (
+			#{jobName}, 
+			#{jobCron}, 
+			#{jobClass}, 
+			#{jobData},
+			NOW(),
+			NOW()
+		);
+		<selectKey resultType="java.lang.Integer" order="AFTER" keyProperty="id"> 
+			SELECT LAST_INSERT_ID() 
+		</selectKey> 
+	</insert>
+	
+	<select id="load" parameterType="java.lang.String" resultMap="XxlJobInfo">
+		SELECT <include refid="Base_Column_List" />
+		FROM xxl_job_qrtz_trigger_info AS t
+		WHERE t.job_name = #{jobName}
+	</select>
+	
+	<update id="update">
+		UPDATE `xxl_job_qrtz_trigger_info` 
+		SET `job_cron`= #{jobCron}, 
+			`job_data`= #{jobData},
+			`update_time`= NOW()
+		WHERE `id`= #{id}
+	</update>
+	
+	<delete id="delete" parameterType="java.lang.String">
+		delete from xxl_job_qrtz_trigger_info
+		where job_name = #{jobName}
+	</delete>
+	
+</mapper>

xxl-job-admin/src/main/java/com/xxl/job/core/model/mapper/XxlJobLogMapper.xml → xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobLogMapper.xml


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

@@ -68,8 +68,6 @@
 	
 	<!-- footer -->
 	<@netCommon.commonFooter />
-	<!-- control -->
-	<@netCommon.commonControl />
 </div>
 <@netCommon.commonScript />
 </body>

+ 53 - 26
xxl-job-admin/src/main/webapp/WEB-INF/template/job/index.ftl

@@ -28,32 +28,53 @@
 		
 		<!-- Main content -->
 	    <section class="content">
+	    
+	    	<div class="row">
+	            <div class="col-xs-4">
+	              	<div class="input-group">
+	                	<span class="input-group-addon">
+	                  		jobName
+	                	</span>
+	                	<input type="text" class="form-control" id="jobName" value="${jobName}" autocomplete="on" >
+	              	</div>
+	            </div>
+	            <div class="col-xs-2">
+	            	<button class="btn btn-block btn-info" id="searchBtn">搜索</button>
+	            </div>
+	            <div class="col-xs-2">
+	            	<button class="btn btn-block btn-success add" type="button">+新增任务</button>
+	            </div>
+          	</div>
+	    	
 			<div class="row">
 				<div class="col-xs-12">
 					<div class="box">
 			            <div class="box-header">
 			            	<h3 class="box-title">调度列表</h3>
-			            	<button class="btn btn-info btn-xs add" type="button">+新增任务</button>
 			            </div>
 			            <div class="box-body">
 			              	<table id="job_list" class="table table-bordered table-striped">
 				                <thead>
 					            	<tr>
-					                	<th>调度key</th>
-					                  	<th>cron</th>
-					                  	<!--<th>类路径</th>-->
+					            		<th>id</th>
+					                	<th>任务Key</th>
+					                  	<th>任务Cron</th>
+					                  	<th>任务Class</th>
+					                  	<th>状态Status</th>
 					                  	<th>参数</th>
-					                  	<th>状态</th>
+					                  	<th>addTime</th>
+					                  	<th>updateTime</th>
 					                  	<th>操作</th>
 					                </tr>
 				                </thead>
 				                <tbody>
+				                	<#--
 			                		<#if jobList?exists && jobList?size gt 0>
 									<#list jobList as item>
 									<tr>
 					            		<td>${item['TriggerKey'].name}</td>
 					                  	<td>${item['Trigger'].cronExpression}</td>
-					                  	<!--<td>${item['JobDetail'].jobClass}</td>-->
+					                  	<td>${item['JobDetail'].jobClass}</td>
 					                  	<td>
 					                  		<#assign jobDataMap = item['JobDetail'].jobDataMap />
 					                  		<#if jobDataMap?exists && jobDataMap?keys?size gt 0>
@@ -89,17 +110,10 @@
 					                </tr>
 									</#list>
 									</#if>
+									
+									-->
 				                </tbody>
-				                <tfoot>
-					            	<tr>
-					                  	<th>调度key</th>
-					                  	<th>cron</th>
-					                  	<!--<th>类路径</th>-->
-					                  	<th>参数</th>
-					                  	<th>状态</th>
-					                  	<th>操作</th>
-					                </tr>
-				                </tfoot>
+				                <tfoot></tfoot>
 							</table>
 						</div>
 					</div>
@@ -110,8 +124,6 @@
 	
 	<!-- footer -->
 	<@netCommon.commonFooter />
-	<!-- control -->
-	<@netCommon.commonControl />
 </div>
 
 <!-- job新增.模态框 -->
@@ -129,19 +141,19 @@
 					</div>
 					<div class="form-group">
 						<label for="lastname" class="col-sm-3 control-label">任务Corn</label>
-						<div class="col-sm-9"><input type="text" class="form-control" name="cronExpression" placeholder="请输入任务Corn[允许修改]" maxlength="100" ></div>
+						<div class="col-sm-9"><input type="text" class="form-control" name="cronExpression" placeholder="请输入任务Corn" maxlength="100" ></div>
 					</div>
 					<div class="form-group">
 						<label for="lastname" class="col-sm-3 control-label">任务描述</label>
-						<div class="col-sm-9"><input type="text" class="form-control" name="job_desc" placeholder="请输入任务描述[不支持修改]" maxlength="200" ></div>
+						<div class="col-sm-9"><input type="text" class="form-control" name="job_desc" placeholder="请输入任务描述" maxlength="200" ></div>
 					</div>
 					<div class="form-group">
 						<label for="lastname" class="col-sm-3 control-label">任务URL</label>
-						<div class="col-sm-9"><input type="text" class="form-control" name="job_url" placeholder="请输入任务URL[不支持修改]" maxlength="200" ></div>
+						<div class="col-sm-9"><input type="text" class="form-control" name="job_url" placeholder="请输入任务URL" maxlength="200" ></div>
 					</div>
 					<div class="form-group">
 						<label for="lastname" class="col-sm-3 control-label">任务handler</label>
-						<div class="col-sm-9"><input type="text" class="form-control" name="handleName" placeholder="请输入任务handler[不支持修改]" maxlength="200" ></div>
+						<div class="col-sm-9"><input type="text" class="form-control" name="handleName" placeholder="请输入任务handler" maxlength="200" ></div>
 					</div>
 					<div class="form-group">
 						<div class="col-sm-offset-3 col-sm-9">
@@ -166,12 +178,24 @@
          	<div class="modal-body">
 				<form class="form-horizontal form" role="form" >
 					<div class="form-group">
-						<label for="firstname" class="col-sm-2 control-label">任务Key</label>
-						<div class="col-sm-10"><input type="text" class="form-control" name="triggerKeyName" placeholder="请输入任务Key" minlength="4" maxlength="100" readonly ></div>
+						<label for="firstname" class="col-sm-3 control-label">任务Key</label>
+						<div class="col-sm-9"><input type="text" class="form-control" name="triggerKeyName" placeholder="请输入任务Key" minlength="4" maxlength="100" readonly ></div>
+					</div>
+					<div class="form-group">
+						<label for="lastname" class="col-sm-3 control-label">任务Corn</label>
+						<div class="col-sm-9"><input type="text" class="form-control" name="cronExpression" placeholder="请输入任务Corn" maxlength="100" ></div>
 					</div>
 					<div class="form-group">
-						<label for="lastname" class="col-sm-2 control-label">任务Corn</label>
-						<div class="col-sm-10"><input type="text" class="form-control" name="cronExpression" placeholder="请输入任务Corn" maxlength="100" ></div>
+						<label for="lastname" class="col-sm-3 control-label">任务描述</label>
+						<div class="col-sm-9"><input type="text" class="form-control" name="job_desc" placeholder="请输入任务描述" maxlength="200" ></div>
+					</div>
+					<div class="form-group">
+						<label for="lastname" class="col-sm-3 control-label">任务URL</label>
+						<div class="col-sm-9"><input type="text" class="form-control" name="job_url" placeholder="请输入任务URL" maxlength="200" ></div>
+					</div>
+					<div class="form-group">
+						<label for="lastname" class="col-sm-3 control-label">任务handler</label>
+						<div class="col-sm-9"><input type="text" class="form-control" name="handleName" placeholder="请输入任务handler" maxlength="200" ></div>
 					</div>
 					<div class="form-group">
 						<div class="col-sm-offset-2 col-sm-10">
@@ -191,6 +215,9 @@
 <script src="${request.contextPath}/static/adminlte/plugins/datatables/jquery.dataTables.min.js"></script>
 <script src="${request.contextPath}/static/adminlte/plugins/datatables/dataTables.bootstrap.min.js"></script>
 <script src="${request.contextPath}/static/plugins/jquery/jquery.validate.min.js"></script>
+<!-- daterangepicker -->
+<script src="${request.contextPath}/static/adminlte/plugins/daterangepicker/moment.min.js"></script>
+<script src="${request.contextPath}/static/adminlte/plugins/daterangepicker/daterangepicker.js"></script>
 <script>var base_url = '${request.contextPath}';</script>
 <script src="${request.contextPath}/static/js/job.index.1.js"></script>
 </body>

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

@@ -84,8 +84,6 @@
 	
 	<!-- footer -->
 	<@netCommon.commonFooter />
-	<!-- control -->
-	<@netCommon.commonControl />
 </div>
 
 <@netCommon.commonScript />

+ 109 - 9
xxl-job-admin/src/main/webapp/static/js/job.index.1.js

@@ -1,6 +1,75 @@
 $(function() {
 	// init date tables
-	$("#job_list").DataTable({
+	var jobTable = $("#job_list").dataTable({
+		"deferRender": true,
+		"processing" : true, 
+	    "serverSide": true,
+		"ajax": {
+			url: base_url + "/job/pageList",
+	        data : function ( d ) {
+                d.jobName = $('#jobName').val()
+            }
+	    },
+	    //"scrollX": true,	// X轴滚动条,取消自适应
+	    "columns": [
+	                { "data": 'id', "bSortable": false, "visible" : false},
+	                { "data": 'jobName', "bSortable": false},
+	                { "data": 'jobCron', "bSortable": false, "visible" : true},
+	                { "data": 'jobClass', "bSortable": false, "visible" : false},
+	                { "data": 'jobStatus', "bSortable": false, "visible" : true},
+	                { "data": 'jobData', "bSortable": false, "visible" : true},
+	                { 
+	                	"data": 'addTime', 
+	                	"bSortable": false, 
+	                	"render": function ( data, type, row ) {
+	                		return data?moment(new Date(data)).format("YYYY-MM-DD HH:mm:ss"):"";
+	                	}
+	                },
+	                { 
+	                	"data": 'updateTime', 
+	                	"bSortable": false, 
+	                	"render": function ( data, type, row ) {
+	                		return data?moment(new Date(data)).format("YYYY-MM-DD HH:mm:ss"):"";
+	                	}
+	                },
+	                { "data": '操作' , "bSortable": false,
+	                	"render": function ( data, type, row ) {
+	                		return function(){
+	                			// status
+	                			var pause_resume = "";
+	                			if ('NORMAL' == row.jobStatus) {
+	                				pause_resume = '<button class="btn btn-info btn-xs job_operate" type="job_pause" type="button">暂停</button>  ';
+								} else if ('PAUSED' == row.jobStatus){
+									pause_resume = '<button class="btn btn-info btn-xs job_operate" type="job_resume" type="button">恢复</button>  ';
+								}
+	                			// log url
+	                			var logUrl = base_url +'/joblog?jobName='+ row.jobName;
+	                			
+	                			// job data
+	                			var jobDataMap = eval('(' + row.jobData + ')');
+	                			
+	                			var html = '<p jobName="'+ row.jobName +'" '+
+	                							' cronExpression="'+ row.jobCron +'" '+
+	                							' job_desc="'+jobDataMap.job_desc +'" '+
+	                							' job_url="'+ jobDataMap.job_url +'" '+
+	                							' handleName="'+ jobDataMap.handleName +'" '+
+	                							'>'+
+	                					pause_resume +
+										'<button class="btn btn-info btn-xs job_operate" type="job_trigger" type="button">执行</button>  '+
+										'<button class="btn btn-info btn-xs update" type="button">更新corn</button>  '+
+									  	'<button class="btn btn-danger btn-xs job_operate" type="job_del" type="button">删除</button>  '+
+									  	'<button class="btn btn-warning btn-xs" type="job_del" type="button" '+
+									  		'onclick="javascript:window.open(\'' + logUrl + '\')" >查看日志</button>'+
+									'</p>';
+									
+	                			
+	                			return html;
+	                		};
+	                	}
+	                }
+	            ],
+	    "searching": false,
+	    "ordering": true,
 		"language" : {
 			"sProcessing" : "处理中...",
 			"sLengthMenu" : "每页 _MENU_ 条记录",
@@ -27,8 +96,13 @@ $(function() {
 		}
 	});
 	
+	// 搜索按钮
+	$('#searchBtn').on('click', function(){
+		jobTable.fnDraw();
+	});
+	
 	// job operate
-	$(".job_operate").click(function() {
+	$("#job_list").on('click', '.job_operate',function() {
 		var typeName;
 		var url;
 		var type = $(this).attr("type");
@@ -48,22 +122,21 @@ $(function() {
 			return;
 		}
 		
-		var name = $(this).parent('p').attr("name");
-		var group = $(this).parent('p').attr("group");
+		var name = $(this).parent('p').attr("jobName");
 		
 		ComConfirm.show("确认" + typeName + "?", function(){
 			$.ajax({
 				type : 'POST',
 				url : url,
 				data : {
-					"triggerKeyName" :	name,
-					"group"			 :	group
+					"triggerKeyName" :	name
 				},
 				dataType : "json",
 				success : function(data){
 					if (data.code == 200) {
 						ComAlert.show(1, typeName + "成功", function(){
-							window.location.reload();
+							//window.location.reload();
+							jobTable.fnDraw();
 						});
 					} else {
 						ComAlert.show(1, typeName + "失败");
@@ -215,9 +288,12 @@ $(function() {
 	});
 	
 	// 更新
-	$(".update").click(function(){
-		$("#updateModal .form input[name='triggerKeyName']").val($(this).parent('p').attr("name"));
+	$("#job_list").on('click', '.update',function() {
+		$("#updateModal .form input[name='triggerKeyName']").val($(this).parent('p').attr("jobName"));
 		$("#updateModal .form input[name='cronExpression']").val($(this).parent('p').attr("cronExpression"));
+		$("#updateModal .form input[name='job_desc']").val($(this).parent('p').attr("job_desc"));
+		$("#updateModal .form input[name='job_url']").val($(this).parent('p').attr("job_url"));
+		$("#updateModal .form input[name='handleName']").val($(this).parent('p').attr("handleName"));
 		$('#updateModal').modal({backdrop: false, keyboard: false}).modal('show');
 	});
 	var updateModalValidate = $("#updateModal .form").validate({
@@ -233,6 +309,18 @@ $(function() {
             cronExpression : {  
             	required : true ,
                 maxlength: 100
+            },  
+            job_desc : {  
+            	required : true ,
+                maxlength: 200
+            },
+            job_url : {
+            	required : true ,
+                maxlength: 200
+            },
+            handleName : {
+            	required : true ,
+                maxlength: 200
             }
         }, 
         messages : {  
@@ -244,6 +332,18 @@ $(function() {
             cronExpression : {
             	required :"请输入“任务Corn”."  ,
                 maxlength:"“任务Corn”不应超过100位"
+            },  
+            job_desc : {
+            	required :"请输入“任务描述”."  ,
+                maxlength:"“任务描述”长度不应超过200位"
+            },  
+            job_url : {
+            	required :"请输入“任务URL”."  ,
+                maxlength:"“任务URL”长度不应超过200位"
+            },
+            handleName : {
+            	required : "请输入“任务handler”."  ,
+                maxlength: "“任务handler”长度不应超过200位"
             }
         }, 
 		highlight : function(element) {  

+ 4 - 4
xxl-job-admin/src/main/webapp/static/js/joblog.index.1.js

@@ -29,7 +29,7 @@ $(function() {
 	                { "data": 'triggerStatus', "bSortable": false},
 	                { "data": 'triggerMsg',"bSortable": false,
 	                	"render": function ( data, type, row ) {
-	                		return data?'<a class="logTips" title="'+ data +'">调度日志</a>':"无";
+	                		return data?'<a class="logTips" href="javascript:;" >调度日志<span style="display:none;">'+ data +'</span></a>':"无";
 	                	}
 	                },
 	                { 
@@ -42,7 +42,7 @@ $(function() {
 	                { "data": 'handleStatus',"bSortable": false},
 	                { "data": 'handleMsg' , "bSortable": false,
 	                	"render": function ( data, type, row ) {
-	                		return data?'<a class="logTips" title="'+ data +'">执行日志</a>':"无";
+	                		return data?'<a class="logTips" href="javascript:;" >执行日志<span style="display:none;">'+ data +'</span></a>':"无";
 	                	}
 	                }
 	            ],
@@ -76,8 +76,8 @@ $(function() {
 	
 	// 日志弹框提示
 	$('#joblog_list').on('click', '.logTips', function(){
-		var title = $(this).attr('title');
-		ComAlertTec.show(title);
+		var msg = $(this).find('span').html();
+		ComAlertTec.show(msg);
 	});
 	
 	// 过滤时间

+ 55 - 0
xxl-job-admin/src/test/java/com/xxl/job/dao/impl/XxlJobInfoTest.java

@@ -0,0 +1,55 @@
+package com.xxl.job.dao.impl;
+
+import java.util.List;
+
+import javax.annotation.Resource;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import com.xxl.job.core.model.XxlJobInfo;
+import com.xxl.job.dao.IXxlJobInfoDao;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(locations = "classpath*:applicationcontext-*.xml")
+public class XxlJobInfoTest {
+	
+	@Resource
+	private IXxlJobInfoDao xxlJobInfoDao;
+	
+	@Test
+	public void pageList(){
+		List<XxlJobInfo> list = xxlJobInfoDao.pageList(0, 20, null, null, null);
+		int list_count = xxlJobInfoDao.pageListCount(0, 20, null, null, null);
+		
+		System.out.println(list);
+		System.out.println(list_count);
+	}
+	
+	@Test
+	public void save_load(){
+		XxlJobInfo info = new XxlJobInfo();
+		info.setJobName("job_name");
+		info.setJobCron("jobCron");
+		info.setJobClass("jobClass");
+		info.setJobData("jobData");
+		int count = xxlJobInfoDao.save(info);
+		System.out.println(count);
+		System.out.println(info.getId());
+		
+		XxlJobInfo item = xxlJobInfoDao.load(info.getId());
+		System.out.println(item);
+	}
+	
+	@Test
+	public void update(){
+		XxlJobInfo item = xxlJobInfoDao.load(2);
+		
+		item.setJobCron("jobCron2");
+		item.setJobData("jobData2");
+		xxlJobInfoDao.update(item);
+	}
+	
+}

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

@@ -64,6 +64,9 @@ public class HttpUtil {
 				responseMsg = EntityUtils.toString(entity, "UTF-8");
 				EntityUtils.consume(entity);
 			}
+			if (response.getStatusLine().getStatusCode() != 200) {
+				exceptionMsg = "response.getStatusLine().getStatusCode() = " + response.getStatusLine().getStatusCode();
+			}
 		} catch (Exception e) {
 			e.printStackTrace();
 			StringWriter out = new StringWriter();