提交 30ea6867 authored 作者: xueli.xue's avatar xueli.xue

JobBean交互完善

上级 23423855
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src/main/webapp"/>
<classpathentry excluding="**/*.min.js|**/node_modules/*|**/bower_components/*" kind="src" path="target/m2e-wtp/web-resources"/>
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.JRE_CONTAINER"/>
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.WebProject">
<attributes>
<attribute name="hide" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.baseBrowserLibrary"/>
<classpathentry kind="output" path=""/>
</classpath>
activeProfiles=
eclipse.preferences.version=1
resolveWorkspaceProjects=true
version=1
org.eclipse.wst.jsdt.launching.baseBrowserLibrary
\ No newline at end of file
disabled=06target
eclipse.preferences.version=1
......@@ -130,6 +130,13 @@
<version>2.2.1</version>
</dependency>
<!-- httpclient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.3.6</version>
</dependency>
</dependencies>
<build>
......
package com.xxl.controller;
package com.xxl.job.controller;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang.StringUtils;
import org.quartz.CronExpression;
......@@ -13,27 +18,66 @@ import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.xxl.quartz.DynamicSchedulerUtil;
import com.xxl.quartz.ReturnT;
import com.xxl.job.core.model.ReturnT;
import com.xxl.job.core.util.DynamicSchedulerUtil;
import com.xxl.job.service.job.DemoJobBean;
import com.xxl.job.service.job.DemoJobBeanB;
/**
* index controller
* @author xuxueli 2015-12-19 16:13:16
*/
@Controller
public class IndexController {
// local job bean list
public static Map<String, String> jobBeanMap = new HashMap<String, String>();
@RequestMapping("")
public String index(Model model) {
jobBeanMap.put(DemoJobBean.class.getName(), "测试任务");
jobBeanMap.put(DemoJobBeanB.class.getName(), "测试任务B");
model.addAttribute("jobBeanMap", jobBeanMap);
// job list
List<Map<String, Object>> jobList = DynamicSchedulerUtil.getJobList();
model.addAttribute("jobList", jobList);
return "job/index";
}
@RequestMapping("/add")
@RequestMapping("/help")
public String help(Model model) {
return "job/help";
}
@SuppressWarnings("unchecked")
@RequestMapping("/job/add")
@ResponseBody
public ReturnT<String> add(String triggerKeyName, String cronExpression, String jobClassName, String jobDesc) {
public ReturnT<String> add(HttpServletRequest request) {
String triggerKeyName = null;
String cronExpression = null;
Map<String, Object> jobData = new HashMap<String, Object>();
try {
request.setCharacterEncoding("utf-8");
} catch (UnsupportedEncodingException e1) {
e1.printStackTrace();
}
Set<Map.Entry<String, String[]>> paramSet = request.getParameterMap().entrySet();
for (Entry<String, String[]> param : paramSet) {
if (param.getKey().equals("triggerKeyName")) {
triggerKeyName = param.getValue()[0];
} else if (param.getKey().equals("cronExpression")) {
cronExpression = param.getValue()[0];
} else {
jobData.put(param.getKey(), param.getValue().length>0?param.getValue()[0]:param.getValue());
}
}
// triggerKeyName
if (StringUtils.isBlank(triggerKeyName)) {
return new ReturnT<String>(500, "请输入“任务key”");
}
// cronExpression
if (StringUtils.isBlank(cronExpression)) {
return new ReturnT<String>(500, "请输入“任务corn”");
......@@ -41,29 +85,37 @@ public class IndexController {
if (!CronExpression.isValidExpression(cronExpression)) {
return new ReturnT<String>(500, "“任务corn”不合法");
}
// jobClassName
Class<?> clazz = null;
// jobData
if (jobData.get(DynamicSchedulerUtil.job_desc)==null || jobData.get(DynamicSchedulerUtil.job_desc).toString().trim().length()==0) {
return new ReturnT<String>(500, "请输入“任务描述”");
}
// job_bean
String job_bean = (String) jobData.get(DynamicSchedulerUtil.job_bean);
if (StringUtils.isBlank(job_bean)) {
return new ReturnT<String>(500, "JobBean不可为空");
}
// jobClass
Class<? extends Job> jobClass = null;
try {
clazz = Class.forName(jobClassName);
Class<?> clazz = Class.forName(job_bean);
if (clazz!=null) {
jobClass = (Class<? extends Job>) clazz;
}
} catch (ClassNotFoundException e1) {
e1.printStackTrace();
}
if (clazz == null) {
return new ReturnT<String>(500, "“任务Impl”不合法");
}
if (!Job.class.isAssignableFrom(clazz)) {
return new ReturnT<String>(500, "“任务Impl”类必须继承Job接口");
}
@SuppressWarnings("unchecked")
Class<? extends Job> jobClass = (Class<? extends Job>)clazz;
// jobDesc
if (StringUtils.isBlank(jobDesc)) {
return new ReturnT<String>(500, "请输入“任务描述”");
if (jobClass == null) {
return new ReturnT<String>(500, "JobBean未知");
}
try {
Map<String, Object> jobData = new HashMap<String, Object>();
jobData.put(DynamicSchedulerUtil.job_desc, jobDesc);
DynamicSchedulerUtil.addJob(triggerKeyName, cronExpression, jobClass, jobData);
boolean result = DynamicSchedulerUtil.addJob(triggerKeyName, cronExpression, jobClass, jobData);
if (!result) {
return new ReturnT<String>(500, "任务ID重复,请更换确认");
}
return ReturnT.SUCCESS;
} catch (SchedulerException e) {
e.printStackTrace();
......@@ -71,7 +123,7 @@ public class IndexController {
return ReturnT.FAIL;
}
@RequestMapping("/reschedule")
@RequestMapping("/job/reschedule")
@ResponseBody
public ReturnT<String> reschedule(String triggerKeyName, String cronExpression) {
// triggerKeyName
......@@ -94,7 +146,7 @@ public class IndexController {
return ReturnT.FAIL;
}
@RequestMapping("/remove")
@RequestMapping("/job/remove")
@ResponseBody
public ReturnT<String> remove(String triggerKeyName) {
try {
......@@ -106,7 +158,7 @@ public class IndexController {
}
}
@RequestMapping("/pause")
@RequestMapping("/job/pause")
@ResponseBody
public ReturnT<String> pause(String triggerKeyName) {
try {
......@@ -118,7 +170,7 @@ public class IndexController {
}
}
@RequestMapping("/resume")
@RequestMapping("/job/resume")
@ResponseBody
public ReturnT<String> resume(String triggerKeyName) {
try {
......@@ -130,9 +182,16 @@ public class IndexController {
}
}
@RequestMapping("/help")
public String help(Model model) {
return "job/help";
@RequestMapping("/job/trigger")
@ResponseBody
public ReturnT<String> triggerJob(String triggerKeyName) {
try {
DynamicSchedulerUtil.triggerJob(triggerKeyName);
return ReturnT.SUCCESS;
} catch (SchedulerException e) {
e.printStackTrace();
return ReturnT.FAIL;
}
}
}
package com.xxl.job.core.model;
import java.util.Date;
/**
* xxl-job log, used to track trigger process
* @author xuxueli 2015-12-19 23:19:09
*/
public class XxlJobLog {
private String jobTriggerUuid;
private String jobHandleName;
// trigger info
private Date triggerTime;
private String triggerStatus;
private String triggerDetailLog;
// handle info
private Date handleTime;
private String handleStatus;
private String handleDetailLog;
public String getJobTriggerUuid() {
return jobTriggerUuid;
}
public void setJobTriggerUuid(String jobTriggerUuid) {
this.jobTriggerUuid = jobTriggerUuid;
}
public String getJobHandleName() {
return jobHandleName;
}
public void setJobHandleName(String jobHandleName) {
this.jobHandleName = jobHandleName;
}
public Date getTriggerTime() {
return triggerTime;
}
public void setTriggerTime(Date triggerTime) {
this.triggerTime = triggerTime;
}
public String getTriggerStatus() {
return triggerStatus;
}
public void setTriggerStatus(String triggerStatus) {
this.triggerStatus = triggerStatus;
}
public String getTriggerDetailLog() {
return triggerDetailLog;
}
public void setTriggerDetailLog(String triggerDetailLog) {
this.triggerDetailLog = triggerDetailLog;
}
public Date getHandleTime() {
return handleTime;
}
public void setHandleTime(Date handleTime) {
this.handleTime = handleTime;
}
public String getHandleStatus() {
return handleStatus;
}
public void setHandleStatus(String handleStatus) {
this.handleStatus = handleStatus;
}
public String getHandleDetailLog() {
return handleDetailLog;
}
public void setHandleDetailLog(String handleDetailLog) {
this.handleDetailLog = handleDetailLog;
}
@Override
public String toString() {
return "XxlJobLog [jobTriggerUuid=" + jobTriggerUuid + ", jobHandleName=" + jobHandleName
+ ", triggerTime=" + triggerTime + ", triggerStatus=" + triggerStatus + ", triggerDetailLog="
+ triggerDetailLog + ", handleTime=" + handleTime + ", handleStatus=" + handleStatus
+ ", handleDetailLog=" + handleDetailLog + "]";
}
}
package com.xxl.quartz;
package com.xxl.job.core.util;
import java.util.ArrayList;
import java.util.Date;
......@@ -26,6 +26,10 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
/**
* base quartz scheduler util
* @author xuxueli 2015-12-19 16:13:53
*/
public final class DynamicSchedulerUtil implements InitializingBean {
private static final Logger logger = LoggerFactory.getLogger(DynamicSchedulerUtil.class);
......@@ -74,6 +78,8 @@ public final class DynamicSchedulerUtil implements InitializingBean {
}
public static final String job_desc = "job_desc";
public static final String job_bean = "job_bean";
// addJob 新增
public static boolean addJob(String triggerKeyName, String cronExpression, Class<? extends Job> jobClass, Map<String, Object> jobData) throws SchedulerException {
// TriggerKey : name + group
......@@ -174,4 +180,21 @@ public final class DynamicSchedulerUtil implements InitializingBean {
return result;
}
// run 执行一次
public static boolean triggerJob(String triggerKeyName) throws SchedulerException {
// TriggerKey : name + group
String group = Scheduler.DEFAULT_GROUP;
JobKey jobKey = JobKey.jobKey(triggerKeyName, group);
boolean result = false;
if (scheduler.checkExists(jobKey)) {
scheduler.triggerJob(jobKey);
result = true;
logger.info(">>>>>>>>>>> runJob success, jobKey:{}", jobKey);
} else {
logger.info(">>>>>>>>>>> runJob fail, jobKey:{}", jobKey);
}
return result;
}
}
\ No newline at end of file
package com.xxl.service;
package com.xxl.job.service;
/**
* local trigger, only exists in local jvm
* @author xuxueli 2015-12-17 17:29:23
*/
public interface ITriggerService {
public void beat();
......
package com.xxl.service.impl;
package com.xxl.job.service.impl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import com.xxl.service.ITriggerService;
import com.xxl.job.service.ITriggerService;
/**
* local trigger, only exists in local jvm
* @author xuxueli 2015-12-17 17:31:24
*/
@Service("triggerService")
public class TriggerServiceImpl implements ITriggerService {
private static transient Logger logger = LoggerFactory.getLogger(TriggerServiceImpl.class);
public void beat() {
logger.info(">>>>>>>>>> xxl-job : local quartz beat success.");
logger.info(">>>>>>>>>>> xxl-job beat success.");
}
}
package com.xxl.job.service.job;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.quartz.QuartzJobBean;
/**
* http job bean
* @author xuxueli 2015-12-17 18:20:34
*/
public class DemoJobBean extends QuartzJobBean {
private static Logger logger = LoggerFactory.getLogger(DemoJobBean.class);
@Override
protected void executeInternal(JobExecutionContext context)
throws JobExecutionException {
String triggerKey = context.getTrigger().getKey().getName();
String triggerGroup = context.getTrigger().getKey().getGroup();
Map<String, Object> jobDataMap = context.getMergedJobDataMap().getWrappedMap();
// jobDataMap 2 params
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()));
}
}
logger.info(">>>>>>>>>>> xxl-job run :jobId:{}, group:{}, jobDataMap:{}",
new Object[]{triggerKey, triggerGroup, jobDataMap});
}
}
\ No newline at end of file
package com.xxl.job.service.job;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.quartz.QuartzJobBean;
/**
* http job bean
* @author xuxueli 2015-12-17 18:20:34
*/
public class DemoJobBeanB extends QuartzJobBean {
private static Logger logger = LoggerFactory.getLogger(DemoJobBeanB.class);
@Override
protected void executeInternal(JobExecutionContext context)
throws JobExecutionException {
String triggerKey = context.getTrigger().getKey().getName();
String triggerGroup = context.getTrigger().getKey().getGroup();
Map<String, Object> jobDataMap = context.getMergedJobDataMap().getWrappedMap();
// jobDataMap 2 params
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()));
}
}
logger.info(">>>>>>>>>>> xxl-job run :jobId:{}, group:{}, jobDataMap:{}",
new Object[]{triggerKey, triggerGroup, jobDataMap});
}
}
\ No newline at end of file
package com.xxl.service.job;
import java.util.Date;
import org.apache.commons.lang.time.FastDateFormat;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.quartz.QuartzJobBean;
public class JobDetailDemo extends QuartzJobBean {
private static Logger logger = LoggerFactory.getLogger(JobDetailDemo.class);
@Override
protected void executeInternal(JobExecutionContext context)
throws JobExecutionException {
logger.info("全站静态化[DB] run at :{}", FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
}
package com.xxl.service.job;
import java.util.concurrent.TimeUnit;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.xxl.quartz.DynamicSchedulerUtil;
public class TestDynamicJob implements Job {
private static Logger logger = LoggerFactory.getLogger(TestDynamicJob.class);
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
logger.info("xxl-job run: name:{}, group:{}, job_desc:{}",
new Object[]{context.getTrigger().getKey().getName(), context.getTrigger().getKey().getGroup(),
context.getMergedJobDataMap().get(DynamicSchedulerUtil.job_desc)});
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
\ No newline at end of file
......@@ -10,7 +10,7 @@
http://www.springframework.org/schema/util/spring-util.xsd">
<context:annotation-config />
<context:component-scan base-package="com.xxl.service.impl, com.xxl.dao.impl" />
<context:component-scan base-package="com.xxl.job.service" />
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/WEB-INF/template/" />
......@@ -19,11 +19,6 @@
<property name="location" value="classpath:freemarker.properties" />
</bean>
</property>
<property name="freemarkerVariables">
<bean class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="location" value="classpath:freemarker.variables.properties" />
</bean>
</property>
</bean>
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
......
......@@ -15,7 +15,6 @@
<context:annotation-config />
<context:component-scan base-package="com.xxl.service.impl, com.xxl.dao.impl" />
<!-- c3p0:Main数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="${c3p0.driverClass}" />
<property name="jdbcUrl" value="${c3p0.url}" />
......@@ -35,7 +34,7 @@
<property name="mapperLocations" value="classpath*:com/xxl/core/model/mapper/*.xml"/>
</bean>
<!-- Template在Junit测试的时候,必须使用scope="prototype",原因未知 -->
<!-- scope must be "prototype" when junit -->
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate" scope="prototype">
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>
......
......@@ -9,37 +9,16 @@
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<!-- Job trigger -->
<bean id="job02Trigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" >
<bean class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="com.xxl.service.job.JobDetailDemo"/>
<property name="jobDataAsMap">
<map>
<!-- <entry key="xxService" value-ref="xxService" /> -->
</map>
</property>
<property name="durability" value="true" />
</bean>
</property>
<property name="cronExpression" value="0/3 * * * * ? *" />
</bean>
<!-- Job信息会被上报并持久化到mysql中,多个集群节点中竞争Job锁,只会有一台执行 -->
<bean id="quartzScheduler" lazy-init="false" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="autoStartup" value="true" />
<property name="applicationContextSchedulerContextKey" value="applicationContextKey" />
<property name="configLocation" value="classpath:quartz.properties"/>
<property name="triggers">
<list>
<!-- <ref bean="job02Trigger" /> -->
</list>
</property>
</bean>
<!-- 调度器 -->
<bean id="dynamicSchedulerUtil" class="com.xxl.quartz.DynamicSchedulerUtil">
<!-- 协同-调度器 -->
<bean id="dynamicSchedulerUtil" class="com.xxl.job.core.util.DynamicSchedulerUtil">
<!-- (轻易不要变更“调度器名称”, 任务创建时会绑定该“调度器名称”) -->
<property name="scheduler" ref="quartzScheduler"/>
</bean>
......
......@@ -20,11 +20,11 @@
<property name="cronExpression" value="0/10 * * * * ? *" />
</bean>
<!-- Job被加载到内存中,每个集群节点相互独立,都会执行该任务 -->
<bean name="startQuertz" lazy-init="false" autowire="no" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<!-- 进程-调度器 -->
<bean name="jvmQuertz" lazy-init="false" autowire="no" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="beatTrigger" />
<!-- <ref bean="beatTrigger" /> -->
</list>
</property>
</bean>
......
......@@ -12,15 +12,12 @@
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<!-- 事务管理器(声明式事务) -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 启动事务注解(方式1:注解方式) -->
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
<!-- 事务通知(方式2:XML事务管理) -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="detail*" propagation="SUPPORTS" />
......@@ -33,11 +30,8 @@
</tx:attributes>
</tx:advice>
<!-- AOP配置 -->
<aop:config>
<!-- 定义一个切入点 -->
<aop:pointcut id="txoperation" expression="execution(* com.xxl.service.imp.*.*(..))" />
<!-- 切入点事务通知 -->
<aop:advisor pointcut-ref="txoperation" advice-ref="txAdvice" />
</aop:config>
......
# 静态文件地址
static_url=https://www.baidu.com/
\ No newline at end of file
log4j.rootLogger=info,console
log4j.rootLogger=info,console,logFile
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d - xxl-job-demo - %p [%c] - <%m>%n
log4j.appender.console.layout.ConversionPattern=%d - xxl-job-admin - %p [%c] - <%m>%n
log4j.appender.logFile=org.apache.log4j.DailyRollingFileAppender
log4j.appender.logFile.File=${catalina.base}/logs/xxl-job-demo.log
log4j.appender.logFile.File=${catalina.base}/logs/xxl-job-admin.log
log4j.appender.logFile.layout=org.apache.log4j.PatternLayout
log4j.appender.logFile.layout.ConversionPattern=%d - xxl-job-demo - %p [%c] - <%m>%n
log4j.appender.logFile.layout.ConversionPattern=%d - xxl-job-admin - %p [%c] - <%m>%n
......@@ -18,6 +18,7 @@ org.quartz.jobStore.misfireThreshold: 60000
#org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore
# for cluster
#org.quartz.jobStore.tablePrefix = WED_qrtz_
org.quartz.scheduler.instanceId: AUTO
org.quartz.jobStore.class: org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.isClustered: true
......
......@@ -18,34 +18,35 @@
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<description>Spring-web MVC配置</description>
<mvc:annotation-driven />
<context:component-scan base-package="com.xxl.controller" />
<context:component-scan base-package="com.xxl.job.controller" />
<mvc:resources mapping="/favicon.ico" location="/favicon.ico" />
<mvc:resources mapping="/static/**" location="/static/" /> <!-- 不拦截static目录下 -->
<mvc:resources mapping="/**/*.html" location="/" /> <!-- 不拦截.html后缀 -->
<mvc:resources mapping="/static/**" location="/static/" />
<mvc:resources mapping="/**/*.html" location="/" />
<bean id="viewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.freemarker.FreeMarkerView" /> <!-- 解析视图模板类 -->
<property name="prefix" value="" /> <!-- 模板前后缀,指定html页面为模板 -->
<property name="viewClass" value="org.springframework.web.servlet.view.freemarker.FreeMarkerView" />
<property name="prefix" value="" />
<property name="suffix" value=".ftl" />
<property name="contentType" value="text/html;charset=UTF-8" /> <!-- 模板输出内容编码 (此处应与defaultEncoding保持一致) -->
<property name="exposeSpringMacroHelpers" value="true" /> <!-- 访问Request/Session宏助手 -->
<property name="exposeRequestAttributes" value="true" /> <!-- 允许访问Request属性,默认为false -->
<property name="exposeSessionAttributes" value="true" /> <!-- 允许访问Session属性,默认为false -->
<property name="requestContextAttribute" value="request" /> <!-- 将HttpServletRequest的属性存放到request这个变量中 -->
<property name="contentType" value="text/html;charset=UTF-8" />
<property name="exposeSpringMacroHelpers" value="true" />
<property name="exposeRequestAttributes" value="true" />
<property name="exposeSessionAttributes" value="true" />
<property name="requestContextAttribute" value="request" />
<property name="cache" value="true" />
<property name="order" value="0" />
</bean>
<!-- <mvc:interceptors>
<!--
// 自定义拦截器,支持SSO登陆拦截
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.xxl.controller.interceptor.PermissionInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
<bean id="exceptionResolver" class="com.xxl.controller.resolver.WebExceptionResolver" /> -->
<bean id="exceptionResolver" class="com.xxl.controller.resolver.WebExceptionResolver" />
-->
</beans>
\ No newline at end of file
......@@ -37,8 +37,6 @@
<script src="${request.contextPath}/static/adminlte/plugins/fastclick/fastclick.js"></script>
<!-- AdminLTE App -->
<script src="${request.contextPath}/static/adminlte/dist/js/app.min.js"></script>
<!-- SlimScroll 1.3.0 -->
<script src="${request.contextPath}/static/adminlte/plugins/slimScroll/jquery.slimscroll.min.js"></script>
<!-- scrollup -->
<script src="${request.contextPath}/static/plugins/scrollup/jquery.scrollUp.min.js"></script>
......@@ -47,71 +45,15 @@
<#macro commonHeader>
<header class="main-header">
<!-- Logo -->
<a href="${request.contextPath}/" class="logo"> <!-- mini logo for sidebar mini 50x50 pixels -->
<span class="logo-mini"><b>X</b>XL</span> <!-- logo for regular state and mobile devices -->
<span class="logo-lg"><b>调度</b>中心</span>
<a href="${request.contextPath}/" class="logo">
<span class="logo-mini"><b>X</b>XL</span>
<span class="logo-lg"><b>任务调度</b>中心</span>
</a>
<!-- Header Navbar: style can be found in header.less -->
<nav class="navbar navbar-static-top" role="navigation">
<!-- Sidebar toggle button-->
<a href="#" class="sidebar-toggle" data-toggle="offcanvas" role="button"><span class="sr-only">切换导航</span></a>
<#--
<div class="navbar-custom-menu">
<ul class="nav navbar-nav">
<li class="dropdown notifications-menu">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-bell-o"></i> <span class="label label-warning">5</span></a>
<ul class="dropdown-menu">
<li class="header">您有5条通知</li>
<li>
<ul class="menu">
<li><a href="#"> <i class="fa fa-warning text-yellow"></i>
这是一个不错的应用
</a></li>
<li><a href="#"> <i class="fa fa-warning text-yellow"></i>
这是一个很不错的应用
</a></li>
<li><a href="#"> <i class="fa fa-warning text-yellow"></i>
这是一个非常不错的应用
</a></li>
<li><a href="#"> <i class="fa fa-warning text-yellow"></i>
这是一个非常非常不错的应用
</a></li>
<li><a href="#"> <i class="fa fa-warning text-yellow"></i>
这是一个非常非常非常不错的应用
</a></li>
</ul>
</li>
<li class="footer"><a href="#">查看全部</a></li>
</ul>
</li>
<li class="dropdown user user-menu">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<i class="fa fa-user text-aqua"></i><span class="hidden-xs">${loginIdentity.userName}</span>
</a>
<ul class="dropdown-menu">
<li class="user-header">
<p>
${loginIdentity.realName} - 前端研发
<small>注册于 2012-11-20</small>
</p>
</li>
<li class="user-footer">
<div class="pull-left"><a href="#" class="btn btn-default btn-flat">资料</a></div>
<div class="pull-right"><a href="javascript:;" class="btn btn-default btn-flat" id="logoutBtn" >注销</a></div>
</li>
</ul></li>
<li><a href="#" data-toggle="control-sidebar"><i class="fa fa-gears"></i></a></li>
</ul>
</div>
-->
<div class="navbar-custom-menu"></div>
</nav>
</header>
</#macro>
<#macro commonLeft>
......@@ -121,22 +63,9 @@
<section class="sidebar">
<!-- sidebar menu: : style can be found in sidebar.less -->
<ul class="sidebar-menu">
<li class="header">常用模块</li>
<li class="nav-click" ><a href="${request.contextPath}//"><i class="fa fa-circle-o text-red"></i> <span>调度管理</span></a></li>
<li class="nav-click" ><a href="${request.contextPath}//"><i class="fa fa-circle-o text-red"></i> <span>调度中心</span></a></li>
<li class="nav-click" ><a href="${request.contextPath}/help"><i class="fa fa-circle-o text-yellow"></i><span>使用教程</span></a></li>
<#--
<li class="header">全部模块</li>
<li class="treeview nav-click">
<a href="#"><i class="fa fa-dashboard"></i> <span>调度中心</span><i class="fa fa-angle-left pull-right"></i></a>
<ul class="treeview-menu">
<li class="nav-click" ><a href="${request.contextPath}/job/index"><i class="fa fa-circle-o"></i>调度管理</a></li>
<li class="nav-click" ><a href="${request.contextPath}/job/help"><i class="fa fa-circle-o"></i>使用教程</a></li>
</ul>
</li>
-->
</ul>
</section>
<!-- /.sidebar -->
......@@ -226,8 +155,8 @@
<b>Version</b> 1.0
</div>
<strong>Copyright &copy; 2015-2015 &nbsp;
<a href="https://github.com/xuxueli/" target="_blank" >github</a>&nbsp;
<a href="http://www.cnblogs.com/xuxueli/" target="_blank" >cnblog</a>.
<a href="https://github.com/xuxueli/xxl-job" target="_blank" >github</a>&nbsp;
<a href="http://www.cnblogs.com/xuxueli/p/5021979.html" target="_blank" >cnblog</a>.
</strong> All rights reserved.
</footer>
</#macro>
......
<!DOCTYPE html>
<html>
<head>
<title>AdminLTE 2 | Dashboard</title>
<title>调度中心</title>
<#import "/common/common.macro.ftl" as netCommon>
<@netCommon.commonStyle />
</head>
......@@ -26,18 +26,41 @@
<!-- Main content -->
<section class="content">
<div class="callout callout-info">
<h4>简介:xxl-job</h4>
<p>调度管理平台:基于quartz封装实现的的集群任务调度管理平台.</p>
<h4>简介:XXL_JOB</h4>
<p>基于quartz封装实现的的集群任务调度管理平台.</p>
<p></p>
</div>
<div class="callout callout-info">
<div class="callout callout-default">
<h4>特点:</h4>
<p>1、简单:支持通过Web页面对任务进行CRUD操作,操作简单,一分钟上手.</p>
<p>2、动态:支持动态修改任务状态,动态暂停/恢复任务,即时生效.</p>
<p>3、集群:任务信息持久化到mysql中,支持Job服务器集群(高可用),一个任务只会在其中一台服务器上执行.</p>
</div>
<div class="callout callout-default">
<h4>分层模型:</h4>
<p>1、基础:基于quartz封装底层调度层,通过CORN自定义任务执行周期,最终执行自定义JobBean的execute方法,如需多个任务,需要开发多个JobBean实现.</p>
<p>2、分层:上述基础调度模型存在一定局限,调度层和任务层耦合,当新任务上线势必影响任务的正常调度,因此规划将调度系统分层为:调度层 + 任务层 + 通讯层.</p>
<p>
<div class="row">
<div class="col-xs-offset-1 col-xs-11">
<p>》调度模块:维护任务的调度信息,负责定时/周期性的发出调度请求.</p>
<p>》任务模块:具体的任务逻辑,负责接收调度模块的调度请求,执行任务逻辑.</p>
<p>》通讯模块:负责调度模块和任务模块之间的通讯.</p>
<p>(总而言之,一条完整任务由 “调度信息” 和 “任务信息” 组成.)</p>
</div>
</div>
</p>
</div>
<div class="callout callout-default">
<h4>调度属性解析 : 发出HTTP调度请求</h4>
<p>1、调度Key【必填】:调度信息的全局唯一标识.</p>
<p>2、调度Corn【必填】:调度执行的时间表达式.</p>
<p>3、调度描述【必填】:调度的简述.</p>
<p>4、调度URL【必填】:调度执行时发出HTTP请求的目标URL地址.</p>
<p>5、+args【选填】:调度执行时发出HTTP请求的附带的POST参数.</p>
</div>
</section>
<!-- /.content -->
</div>
......
......@@ -19,10 +19,10 @@
<div class="content-wrapper">
<!-- Content Header (Page header) -->
<section class="content-header">
<h1>使用教程<small>调度管理平台</small></h1>
<h1>调度中心<small>调度管理</small></h1>
<ol class="breadcrumb">
<li><a><i class="fa fa-dashboard"></i>调度中心</a></li>
<li class="active">使用教程</li>
<li class="active">调度管理</li>
</ol>
</section>
......@@ -32,17 +32,17 @@
<div class="col-xs-12">
<div class="box">
<div class="box-header">
<h3 class="box-title">任务列表</h3>
<button class="btn btn-primary btn-xs add" type="button">新增</button>
<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>任务ID</th>
<th>调度key</th>
<th>cron</th>
<th>Job类路径</th>
<th>简介</th>
<!--<th>类路径</th>-->
<th>参数</th>
<th>状态</th>
<th>操作</th>
</tr>
......@@ -53,11 +53,13 @@
<tr>
<td>${item['TriggerKey'].name}</td>
<td>${item['Trigger'].cronExpression}</td>
<td>${item['JobDetail'].jobClass}</td>
<!--<td>${item['JobDetail'].jobClass}</td>-->
<td>
<#if item['JobDetail'].jobDataMap?exists>
<#assign job_desc=item['JobDetail'].jobDataMap['job_desc'] />
${job_desc}
<#assign jobDataMap = item['JobDetail'].jobDataMap />
<#if jobDataMap?exists && jobDataMap?keys?size gt 0>
<#list jobDataMap?keys as key>
${key} = ${jobDataMap[key]} <br>
</#list>
</#if>
</td>
<td state="${item['TriggerState']}" >
......@@ -77,8 +79,9 @@
<#elseif item['TriggerState'] == 'PAUSED'>
<button class="btn btn-info btn-xs job_operate" type="job_resume" type="button">恢复</button>
</#if>
<button class="btn btn-info btn-xs update" type="button">修改</button>
<button class="btn btn-info btn-xs job_operate" type="job_trigger" type="button">执行一次</button>
<button class="btn btn-danger btn-xs job_operate" type="job_del" type="button">删除</button>
<button class="btn btn-info btn-xs update" type="button">更新corn</button>
</p>
</td>
</tr>
......@@ -87,10 +90,10 @@
</tbody>
<tfoot>
<tr>
<th>任务ID</th>
<th>调度key</th>
<th>cron</th>
<th>Job类路径</th>
<th>简介</th>
<!--<th>类路径</th>-->
<th>参数</th>
<th>状态</th>
<th>操作</th>
</tr>
......@@ -114,30 +117,39 @@
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" >新增调度任务</h4>
<h4 class="modal-title" >新增调度信息</h4>
</div>
<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" ></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" ></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">任务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">任务Impl</label>
<div class="col-sm-10"><input type="text" class="form-control" name="jobClassName" placeholder="请输入任务Impl[不支持修改]" 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-2 control-label">任务描述</label>
<div class="col-sm-10"><input type="text" class="form-control" name="jobDesc" placeholder="请输入任务描述[不支持修改]" maxlength="200" ></div>
<label for="lastname" class="col-sm-3 control-label">任务JobBean</label>
<div class="col-sm-9">
<select name="job_bean" >
<#if jobBeanMap?exists && jobBeanMap?size gt 0>
<#list jobBeanMap?keys as key>
<option value ="${key}" <#if key_index == 0>selected="selected"</#if> >${jobBeanMap[key]}[${key}]</option>
</#list>
</#if>
</select>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<div class="col-sm-offset-3 col-sm-9">
<button type="submit" class="btn btn-primary" >保存</button>
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-info pull-right addParam">+ arg</button>
</div>
</div>
</form>
......@@ -151,7 +163,7 @@
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" >更新配置</h4>
<h4 class="modal-title" >更新corn</h4>
</div>
<div class="modal-body">
<form class="form-horizontal form" role="form" >
......@@ -163,14 +175,6 @@
<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>
</div>
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label">任务Impl</label>
<div class="col-sm-10"><input type="text" class="form-control" name="jobClassName" placeholder="请输入任务Impl" maxlength="100" readonly ></div>
</div>
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label">任务描述</label>
<div class="col-sm-10"><input type="text" class="form-control" name="jobDesc" placeholder="请输入任务描述" maxlength="200" readonly ></div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary" >保存</button>
......
......@@ -4,6 +4,10 @@
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>xxl-job-admin</display-name>
<context-param>
<param-name>webAppRootKey</param-name>
<param-value>xxl-job-admin</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
......
*.DS_Store
*.log
*.zip
.idea
/nbproject/
/nbproject/private/
/node_modules/
ad.js
TODO
test.html
{
"asi" : true,
"browser" : true,
"eqeqeq" : false,
"eqnull" : true,
"es3" : true,
"expr" : true,
"jquery" : true,
"latedef" : "nofunc",
"laxbreak" : true,
"nonbsp" : true,
"strict" : true,
"undef" : true,
"unused" : true,
// External variabls and plugins
"predef": [ "AdminLTEOptions", "FastClick", "moment", "Morris", "Chart" ]
}
# AutoFill
AutoFill gives an Excel like option to a DataTable to click and drag over multiple cells, filling in information over the selected cells and incrementing numbers as needed. Key features include:
* Click and drag cell content insertion
* Automatic incrementing of numeric information
* Enable and disable on any column
* Detailed callback functions for customisation
* Support for both DataTables and browser window scrolling
# Installation
To use AutoFill, first download DataTables ( http://datatables.net/download ) and place the unzipped AutoFill package into a `extensions` directory in the DataTables package. This will allow the pages in the examples to operate correctly. To see the examples running, open the `examples` directory in your web-browser.
# Basic usage
AutoFill is initialised using the `$.fn.dataTable.AutoFill` constructor. For example:
```js
$(document).ready( function () {
var table = $('#example').dataTable();
new $.fn.dataTable.AutoFill( table );
} );
```
# Documentation / support
* Documentation: http://datatables.net/extensions/autofill/
* DataTables support forums: http://datatables.net/forums
# GitHub
If you fancy getting involved with the development of AutoFill and help make it better, please refer to its GitHub repo: https://github.com/DataTables/AutoFill
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* AutoFill styles
*/
div.AutoFill_filler {
display: none;
position: absolute;
height: 14px;
width: 14px;
background: url(../images/filler.png) no-repeat center center;
z-index: 1002;
}
div.AutoFill_border {
display: none;
position: absolute;
background-color: #0063dc;
z-index: 1001;
box-shadow: 0px 0px 5px #76b4ff;
-moz-box-shadow: 0px 0px 5px #76b4ff;
-webkit-box-shadow: 0px 0px 5px #76b4ff;
}
div.AutoFill_filler{display:none;position:absolute;height:14px;width:14px;background:url(../images/filler.png) no-repeat center center;z-index:1002}div.AutoFill_border{display:none;position:absolute;background-color:#0063dc;z-index:1001;box-shadow:0px 0px 5px #76b4ff;-moz-box-shadow:0px 0px 5px #76b4ff;-webkit-box-shadow:0px 0px 5px #76b4ff}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="shortcut icon" type="image/ico" href="http://www.datatables.net/favicon.ico">
<meta name="viewport" content="initial-scale=1.0, maximum-scale=2.0">
<link rel="stylesheet" type="text/css" href="../../../examples/resources/syntax/shCore.css">
<link rel="stylesheet" type="text/css" href="../../../examples/resources/demo.css">
<script type="text/javascript" language="javascript" src="../../../media/js/jquery.js"></script>
<script type="text/javascript" language="javascript" src="../../../examples/resources/syntax/shCore.js"></script>
<script type="text/javascript" language="javascript" src="../../../examples/resources/demo.js"></script>
<title>AutoFill examples - AutoFill examples</title>
</head>
<body class="dt-example">
<div class="container">
<section>
<h1>AutoFill example <span>AutoFill examples</span></h1>
<div class="info">
<p>AutoFill gives an Excel like option to a DataTable to click and drag over multiple cells, filling in
information over the selected cells and incrementing numbers as needed.</p>
<p>Thanks to <a href="http://www.phoniax.no/">Phoniax AS</a> for their sponsorship of this plug-in for
DataTables.</p>
</div>
</section>
</div>
<section>
<div class="footer">
<div class="gradient"></div>
<div class="liner">
<div class="toc">
<div class="toc-group">
<h3><a href="./index.html">Examples</a></h3>
<ul class="toc">
<li><a href="./simple.html">Basic initialisation</a></li>
<li><a href="./columns.html">Column options</a></li>
<li><a href="./scrolling.html">Scrolling DataTable</a></li>
<li><a href="./fill-both.html">Horizontal and vertical fill</a></li>
<li><a href="./fill-horizontal.html">Horizontal fill</a></li>
<li><a href="./complete-callback.html">Complete callback</a></li>
<li><a href="./step-callback.html">Step callback</a></li>
</ul>
</div>
</div>
<div class="epilogue">
<p>Please refer to the <a href="http://www.datatables.net">DataTables documentation</a> for full
information about its API properties and methods.<br>
Additionally, there are a wide range of <a href="http://www.datatables.net/extras">extras</a> and
<a href="http://www.datatables.net/plug-ins">plug-ins</a> which extend the capabilities of
DataTables.</p>
<p class="copyright">DataTables designed and created by <a href=
"http://www.sprymedia.co.uk">SpryMedia Ltd</a> &#169; 2007-2014<br>
DataTables is licensed under the <a href="http://www.datatables.net/mit">MIT license</a>.</p>
</div>
</div>
</div>
</section>
</body>
</html>
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论