提交 02dc2a4c authored 作者: xuxueli's avatar xuxueli

迁移 springboot

上级 3568340c
...@@ -29,9 +29,8 @@ ...@@ -29,9 +29,8 @@
<javax.servlet-api.version>3.1.0</javax.servlet-api.version> <javax.servlet-api.version>3.1.0</javax.servlet-api.version>
<javax.servlet.jsp-api.version>2.3.3</javax.servlet.jsp-api.version> <javax.servlet.jsp-api.version>2.3.3</javax.servlet.jsp-api.version>
<spring.version>4.3.19.RELEASE</spring.version> <spring.version>4.3.20.RELEASE</spring.version>
<jackson.version>2.9.6</jackson.version> <jackson.version>2.9.7</jackson.version>
<aspectjweaver.version>1.8.13</aspectjweaver.version>
<slf4j-api.version>1.7.25</slf4j-api.version> <slf4j-api.version>1.7.25</slf4j-api.version>
<freemarker.version>2.3.28</freemarker.version> <freemarker.version>2.3.28</freemarker.version>
<junit.version>4.12</junit.version> <junit.version>4.12</junit.version>
...@@ -53,7 +52,10 @@ ...@@ -53,7 +52,10 @@
<groovy-all.version>2.4.15</groovy-all.version> <groovy-all.version>2.4.15</groovy-all.version>
<quartz.version>2.3.0</quartz.version> <quartz.version>2.3.0</quartz.version>
<spring-boot.version>1.5.16.RELEASE</spring-boot.version> <spring-boot.version>1.5.17.RELEASE</spring-boot.version>
<mybatis-spring-boot-starter.version>1.3.2</mybatis-spring-boot-starter.version>
</properties> </properties>
<build> <build>
......
FROM tomcat:8.0-jre8-slim FROM openjdk:7-jre-slim
MAINTAINER xuxueli MAINTAINER xuxueli
ADD target/xxl-job-admin*.war /usr/local/tomcat/webapps/xxl-job-admin.war ENV PARAMS=""
CMD ["catalina.sh", "run"] ADD target/xxl-conf-admin-*.jar /app.jar
\ No newline at end of file
ENTRYPOINT ["sh","-c","java -jar /app.jar $PARAMS"]
\ No newline at end of file
...@@ -7,77 +7,56 @@ ...@@ -7,77 +7,56 @@
<version>2.0.0-SNAPSHOT</version> <version>2.0.0-SNAPSHOT</version>
</parent> </parent>
<artifactId>xxl-job-admin</artifactId> <artifactId>xxl-job-admin</artifactId>
<packaging>war</packaging> <packaging>jar</packaging>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies> <dependencies>
<!-- springframe start -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- springframe end -->
<!-- aspectjweaver (support spring aop) -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectjweaver.version}</version>
</dependency>
<!-- jackson (support spring json) -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- servlet --> <!-- starter-web:spring-webmvc + autoconfigure + logback + yaml + tomcat -->
<dependency> <dependency>
<groupId>javax.servlet</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>javax.servlet-api</artifactId> <artifactId>spring-boot-starter-web</artifactId>
<version>${javax.servlet-api.version}</version>
</dependency> </dependency>
<!-- starter-test:junit + spring-test + mockito -->
<dependency> <dependency>
<groupId>javax.servlet.jsp</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>javax.servlet.jsp-api</artifactId> <artifactId>spring-boot-starter-test</artifactId>
<version>${javax.servlet.jsp-api.version}</version> <scope>test</scope>
</dependency> </dependency>
<!-- freemarker --> <!-- freemarker-starter -->
<dependency> <dependency>
<groupId>org.freemarker</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>freemarker</artifactId> <artifactId>spring-boot-starter-freemarker</artifactId>
<version>${freemarker.version}</version>
</dependency> </dependency>
<!-- slf4j -->
<!-- mybatis-starter:mybatis + mybatis-spring + tomcat-jdbc(default) -->
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.mybatis.spring.boot</groupId>
<artifactId>slf4j-log4j12</artifactId> <artifactId>mybatis-spring-boot-starter</artifactId>
<version>${slf4j-api.version}</version> <version>${mybatis-spring-boot-starter.version}</version>
</dependency> </dependency>
<!-- junit -->
<!-- mysql -->
<dependency> <dependency>
<groupId>junit</groupId> <groupId>mysql</groupId>
<artifactId>junit</artifactId> <artifactId>mysql-connector-java</artifactId>
<version>${junit.version}</version> <version>${mysql-connector-java.version}</version>
<scope>test</scope>
</dependency> </dependency>
<!-- commons-collections4 --> <!-- commons-collections4 -->
<dependency> <dependency>
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>
...@@ -97,36 +76,6 @@ ...@@ -97,36 +76,6 @@
<version>${commons-email.version}</version> <version>${commons-email.version}</version>
</dependency> </dependency>
<!-- c3p0 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>${c3p0.version}</version>
</dependency>
<!-- mysql-connector -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-connector-java.version}</version>
</dependency>
<!-- mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>${mybatis-spring.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<!-- httpclient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${httpclient.version}</version>
</dependency>
<!-- quartz :quartz-2.2.3/c3p0-0.9.1.1/slf4j-api-1.6.6 --> <!-- quartz :quartz-2.2.3/c3p0-0.9.1.1/slf4j-api-1.6.6 -->
<dependency> <dependency>
...@@ -135,6 +84,7 @@ ...@@ -135,6 +84,7 @@
<version>${quartz.version}</version> <version>${quartz.version}</version>
</dependency> </dependency>
<!-- xxl-job-core --> <!-- xxl-job-core -->
<dependency> <dependency>
<groupId>com.xuxueli</groupId> <groupId>com.xuxueli</groupId>
......
package com.xxl.job.admin;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author xuxueli 2018-10-28 00:38:13
*/
@SpringBootApplication
public class XxlJobAdminApplication {
public static void main(String[] args) {
SpringApplication.run(XxlJobAdminApplication.class, args);
}
}
\ No newline at end of file
package com.xxl.job.admin.controller; package com.xxl.job.admin.controller;
import com.xxl.job.admin.core.conf.XxlJobAdminConfig;
import com.xxl.job.admin.core.model.XxlJobGroup; import com.xxl.job.admin.core.model.XxlJobGroup;
import com.xxl.job.admin.core.model.XxlJobRegistry; import com.xxl.job.admin.core.model.XxlJobRegistry;
import com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler; import com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler;
...@@ -115,7 +116,7 @@ public class JobGroupController { ...@@ -115,7 +116,7 @@ public class JobGroupController {
private List<String> findRegistryByAppName(String appNameParam){ private List<String> findRegistryByAppName(String appNameParam){
HashMap<String, List<String>> appAddressMap = new HashMap<String, List<String>>(); HashMap<String, List<String>> appAddressMap = new HashMap<String, List<String>>();
List<XxlJobRegistry> list = XxlJobDynamicScheduler.xxlJobRegistryDao.findAll(RegistryConfig.DEAD_TIMEOUT); List<XxlJobRegistry> list = XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().findAll(RegistryConfig.DEAD_TIMEOUT);
if (list != null) { if (list != null) {
for (XxlJobRegistry item: list) { for (XxlJobRegistry item: list) {
if (RegistryConfig.RegistType.EXECUTOR.name().equals(item.getRegistryGroup())) { if (RegistryConfig.RegistType.EXECUTOR.name().equals(item.getRegistryGroup())) {
......
...@@ -3,6 +3,7 @@ package com.xxl.job.admin.controller.interceptor; ...@@ -3,6 +3,7 @@ package com.xxl.job.admin.controller.interceptor;
import com.xxl.job.admin.core.util.FtlUtil; import com.xxl.job.admin.core.util.FtlUtil;
import com.xxl.job.admin.core.util.I18nUtil; import com.xxl.job.admin.core.util.I18nUtil;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
...@@ -16,6 +17,7 @@ import java.util.HashMap; ...@@ -16,6 +17,7 @@ import java.util.HashMap;
* *
* @author xuxueli 2015-12-12 18:09:04 * @author xuxueli 2015-12-12 18:09:04
*/ */
@Component
public class CookieInterceptor extends HandlerInterceptorAdapter { public class CookieInterceptor extends HandlerInterceptorAdapter {
@Override @Override
......
...@@ -4,6 +4,7 @@ import com.xxl.job.admin.controller.annotation.PermessionLimit; ...@@ -4,6 +4,7 @@ import com.xxl.job.admin.controller.annotation.PermessionLimit;
import com.xxl.job.admin.core.conf.XxlJobAdminConfig; import com.xxl.job.admin.core.conf.XxlJobAdminConfig;
import com.xxl.job.admin.core.util.CookieUtil; import com.xxl.job.admin.core.util.CookieUtil;
import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod; import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
...@@ -16,23 +17,25 @@ import java.math.BigInteger; ...@@ -16,23 +17,25 @@ import java.math.BigInteger;
* *
* @author xuxueli 2015-12-12 18:09:04 * @author xuxueli 2015-12-12 18:09:04
*/ */
@Component
public class PermissionInterceptor extends HandlerInterceptorAdapter { public class PermissionInterceptor extends HandlerInterceptorAdapter {
public static final String LOGIN_IDENTITY_KEY = "XXL_JOB_LOGIN_IDENTITY"; public static final String LOGIN_IDENTITY_KEY = "XXL_JOB_LOGIN_IDENTITY";
public static final String LOGIN_IDENTITY_TOKEN; private static String LOGIN_IDENTITY_TOKEN;
static { public static String getLoginIdentityToken() {
String username = XxlJobAdminConfig.getAdminConfig().getLoginUsername(); if (LOGIN_IDENTITY_TOKEN == null) {
String password = XxlJobAdminConfig.getAdminConfig().getLoginPassword(); String username = XxlJobAdminConfig.getAdminConfig().getLoginUsername();
String password = XxlJobAdminConfig.getAdminConfig().getLoginPassword();
// login token
String tokenTmp = DigestUtils.md5Hex(username + "_" + password);
tokenTmp = new BigInteger(1, tokenTmp.getBytes()).toString(16);
LOGIN_IDENTITY_TOKEN = tokenTmp;
}
// login token
String tokenTmp = DigestUtils.md5Hex(username + "_" + password);
tokenTmp = new BigInteger(1, tokenTmp.getBytes()).toString(16);
LOGIN_IDENTITY_TOKEN = tokenTmp;
}
return LOGIN_IDENTITY_TOKEN;
}
public static boolean login(HttpServletResponse response, String username, String password, boolean ifRemember){ public static boolean login(HttpServletResponse response, String username, String password, boolean ifRemember){
...@@ -40,12 +43,12 @@ public class PermissionInterceptor extends HandlerInterceptorAdapter { ...@@ -40,12 +43,12 @@ public class PermissionInterceptor extends HandlerInterceptorAdapter {
String tokenTmp = DigestUtils.md5Hex(username + "_" + password); String tokenTmp = DigestUtils.md5Hex(username + "_" + password);
tokenTmp = new BigInteger(1, tokenTmp.getBytes()).toString(16); tokenTmp = new BigInteger(1, tokenTmp.getBytes()).toString(16);
if (!LOGIN_IDENTITY_TOKEN.equals(tokenTmp)){ if (!getLoginIdentityToken().equals(tokenTmp)){
return false; return false;
} }
// do login // do login
CookieUtil.set(response, LOGIN_IDENTITY_KEY, LOGIN_IDENTITY_TOKEN, ifRemember); CookieUtil.set(response, LOGIN_IDENTITY_KEY, getLoginIdentityToken(), ifRemember);
return true; return true;
} }
public static void logout(HttpServletRequest request, HttpServletResponse response){ public static void logout(HttpServletRequest request, HttpServletResponse response){
...@@ -53,7 +56,7 @@ public class PermissionInterceptor extends HandlerInterceptorAdapter { ...@@ -53,7 +56,7 @@ public class PermissionInterceptor extends HandlerInterceptorAdapter {
} }
public static boolean ifLogin(HttpServletRequest request){ public static boolean ifLogin(HttpServletRequest request){
String indentityInfo = CookieUtil.getValue(request, LOGIN_IDENTITY_KEY); String indentityInfo = CookieUtil.getValue(request, LOGIN_IDENTITY_KEY);
if (indentityInfo==null || !LOGIN_IDENTITY_TOKEN.equals(indentityInfo.trim())) { if (indentityInfo==null || !getLoginIdentityToken().equals(indentityInfo.trim())) {
return false; return false;
} }
return true; return true;
......
package com.xxl.job.admin.controller.interceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import javax.annotation.Resource;
/**
* web mvc config
*
* @author xuxueli 2018-04-02 20:48:20
*/
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
@Resource
private PermissionInterceptor permissionInterceptor;
@Resource
private CookieInterceptor cookieInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(permissionInterceptor).addPathPatterns("/**");
registry.addInterceptor(cookieInterceptor).addPathPatterns("/**");
super.addInterceptors(registry);
}
}
\ No newline at end of file
...@@ -4,6 +4,7 @@ import com.xxl.job.core.biz.model.ReturnT; ...@@ -4,6 +4,7 @@ import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.util.JacksonUtil; import com.xxl.job.core.util.JacksonUtil;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.HandlerMethod; import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.HandlerExceptionResolver;
...@@ -15,8 +16,10 @@ import java.io.IOException; ...@@ -15,8 +16,10 @@ import java.io.IOException;
/** /**
* common exception resolver * common exception resolver
*
* @author xuxueli 2016-1-6 19:22:18 * @author xuxueli 2016-1-6 19:22:18
*/ */
@Component
public class WebExceptionResolver implements HandlerExceptionResolver { public class WebExceptionResolver implements HandlerExceptionResolver {
private static transient Logger logger = LoggerFactory.getLogger(WebExceptionResolver.class); private static transient Logger logger = LoggerFactory.getLogger(WebExceptionResolver.class);
......
package com.xxl.job.admin.core.conf; package com.xxl.job.admin.core.conf;
import com.xxl.job.admin.dao.XxlJobGroupDao;
import com.xxl.job.admin.dao.XxlJobInfoDao;
import com.xxl.job.admin.dao.XxlJobLogDao;
import com.xxl.job.admin.dao.XxlJobRegistryDao;
import com.xxl.job.core.biz.AdminBiz;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;
/** /**
* xxl-job config * xxl-job config
* *
...@@ -21,6 +28,8 @@ public class XxlJobAdminConfig implements InitializingBean{ ...@@ -21,6 +28,8 @@ public class XxlJobAdminConfig implements InitializingBean{
adminConfig = this; adminConfig = this;
} }
// conf
@Value("${xxl.job.mail.host}") @Value("${xxl.job.mail.host}")
private String mailHost; private String mailHost;
...@@ -48,6 +57,22 @@ public class XxlJobAdminConfig implements InitializingBean{ ...@@ -48,6 +57,22 @@ public class XxlJobAdminConfig implements InitializingBean{
@Value("${xxl.job.i18n}") @Value("${xxl.job.i18n}")
private String i18n; private String i18n;
@Value("${xxl.job.accessToken}")
private String accessToken;
// dao, service
@Resource
public XxlJobLogDao xxlJobLogDao;
@Resource
public XxlJobInfoDao xxlJobInfoDao;
@Resource
public XxlJobRegistryDao xxlJobRegistryDao;
@Resource
public XxlJobGroupDao xxlJobGroupDao;
@Resource
public AdminBiz adminBiz;
public String getMailHost() { public String getMailHost() {
return mailHost; return mailHost;
...@@ -85,4 +110,28 @@ public class XxlJobAdminConfig implements InitializingBean{ ...@@ -85,4 +110,28 @@ public class XxlJobAdminConfig implements InitializingBean{
return i18n; return i18n;
} }
public String getAccessToken() {
return accessToken;
}
public XxlJobLogDao getXxlJobLogDao() {
return xxlJobLogDao;
}
public XxlJobInfoDao getXxlJobInfoDao() {
return xxlJobInfoDao;
}
public XxlJobRegistryDao getXxlJobRegistryDao() {
return xxlJobRegistryDao;
}
public XxlJobGroupDao getXxlJobGroupDao() {
return xxlJobGroupDao;
}
public AdminBiz getAdminBiz() {
return adminBiz;
}
} }
package com.xxl.job.admin.core.conf;
import com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler;
import org.quartz.Scheduler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import javax.sql.DataSource;
/**
* @author xuxueli 2018-10-28 00:18:17
*/
@Configuration
public class XxlJobDynamicSchedulerConfig {
@Bean
public SchedulerFactoryBean getSchedulerFactoryBean(DataSource dataSource){
SchedulerFactoryBean schedulerFactory = new SchedulerFactoryBean();
schedulerFactory.setDataSource(dataSource);
schedulerFactory.setAutoStartup(true); // 自动启动
schedulerFactory.setStartupDelay(20); // 延时启动,应用启动成功后在启动
schedulerFactory.setOverwriteExistingJobs(true); // 覆盖DB中JOB:true、以数据库中已经存在的为准:false
schedulerFactory.setApplicationContextSchedulerContextKey("applicationContext");
schedulerFactory.setConfigLocation(new ClassPathResource("quartz.properties"));
return schedulerFactory;
}
@Bean(initMethod = "start", destroyMethod = "destroy")
public XxlJobDynamicScheduler getXxlJobDynamicScheduler(SchedulerFactoryBean schedulerFactory){
Scheduler scheduler = schedulerFactory.getScheduler();
XxlJobDynamicScheduler xxlJobDynamicScheduler = new XxlJobDynamicScheduler();
xxlJobDynamicScheduler.setScheduler(scheduler);
return xxlJobDynamicScheduler;
}
}
package com.xxl.job.admin.core.schedule; package com.xxl.job.admin.core.schedule;
import com.xxl.job.admin.core.conf.XxlJobAdminConfig;
import com.xxl.job.admin.core.jobbean.RemoteHttpJobBean; import com.xxl.job.admin.core.jobbean.RemoteHttpJobBean;
import com.xxl.job.admin.core.model.XxlJobInfo; import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.core.thread.JobFailMonitorHelper; import com.xxl.job.admin.core.thread.JobFailMonitorHelper;
...@@ -21,6 +22,8 @@ import org.quartz.impl.triggers.CronTriggerImpl; ...@@ -21,6 +22,8 @@ import org.quartz.impl.triggers.CronTriggerImpl;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationContextAware;
import org.springframework.util.Assert; import org.springframework.util.Assert;
...@@ -33,7 +36,7 @@ import java.util.concurrent.ConcurrentHashMap; ...@@ -33,7 +36,7 @@ import java.util.concurrent.ConcurrentHashMap;
* base quartz scheduler util * base quartz scheduler util
* @author xuxueli 2015-12-19 16:13:53 * @author xuxueli 2015-12-19 16:13:53
*/ */
public final class XxlJobDynamicScheduler implements ApplicationContextAware { public final class XxlJobDynamicScheduler {
private static final Logger logger = LoggerFactory.getLogger(XxlJobDynamicScheduler.class); private static final Logger logger = LoggerFactory.getLogger(XxlJobDynamicScheduler.class);
// ---------------------- param ---------------------- // ---------------------- param ----------------------
...@@ -44,31 +47,9 @@ public final class XxlJobDynamicScheduler implements ApplicationContextAware { ...@@ -44,31 +47,9 @@ public final class XxlJobDynamicScheduler implements ApplicationContextAware {
XxlJobDynamicScheduler.scheduler = scheduler; XxlJobDynamicScheduler.scheduler = scheduler;
} }
// accessToken
private static String accessToken;
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
// dao
public static XxlJobLogDao xxlJobLogDao;
public static XxlJobInfoDao xxlJobInfoDao;
public static XxlJobRegistryDao xxlJobRegistryDao;
public static XxlJobGroupDao xxlJobGroupDao;
public static AdminBiz adminBiz;
// ---------------------- applicationContext ----------------------
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
XxlJobDynamicScheduler.xxlJobLogDao = applicationContext.getBean(XxlJobLogDao.class);
XxlJobDynamicScheduler.xxlJobInfoDao = applicationContext.getBean(XxlJobInfoDao.class);
XxlJobDynamicScheduler.xxlJobRegistryDao = applicationContext.getBean(XxlJobRegistryDao.class);
XxlJobDynamicScheduler.xxlJobGroupDao = applicationContext.getBean(XxlJobGroupDao.class);
XxlJobDynamicScheduler.adminBiz = applicationContext.getBean(AdminBiz.class);
}
// ---------------------- init + destroy ---------------------- // ---------------------- init + destroy ----------------------
public void init() throws Exception { public void start() throws Exception {
// admin registry monitor run // admin registry monitor run
JobRegistryMonitorHelper.getInstance().start(); JobRegistryMonitorHelper.getInstance().start();
...@@ -76,8 +57,8 @@ public final class XxlJobDynamicScheduler implements ApplicationContextAware { ...@@ -76,8 +57,8 @@ public final class XxlJobDynamicScheduler implements ApplicationContextAware {
JobFailMonitorHelper.getInstance().start(); JobFailMonitorHelper.getInstance().start();
// admin-server(spring-mvc) // admin-server(spring-mvc)
NetComServerFactory.putService(AdminBiz.class, XxlJobDynamicScheduler.adminBiz); NetComServerFactory.putService(AdminBiz.class, XxlJobAdminConfig.getAdminConfig().getAdminBiz());
NetComServerFactory.setAccessToken(accessToken); NetComServerFactory.setAccessToken(XxlJobAdminConfig.getAdminConfig().getAccessToken());
// init i18n // init i18n
initI18n(); initI18n();
...@@ -105,6 +86,7 @@ public final class XxlJobDynamicScheduler implements ApplicationContextAware { ...@@ -105,6 +86,7 @@ public final class XxlJobDynamicScheduler implements ApplicationContextAware {
JobFailMonitorHelper.getInstance().toStop(); JobFailMonitorHelper.getInstance().toStop();
} }
// ---------------------- executor-client ---------------------- // ---------------------- executor-client ----------------------
private static ConcurrentHashMap<String, ExecutorBiz> executorBizRepository = new ConcurrentHashMap<String, ExecutorBiz>(); private static ConcurrentHashMap<String, ExecutorBiz> executorBizRepository = new ConcurrentHashMap<String, ExecutorBiz>();
public static ExecutorBiz getExecutorBiz(String address) throws Exception { public static ExecutorBiz getExecutorBiz(String address) throws Exception {
...@@ -121,7 +103,7 @@ public final class XxlJobDynamicScheduler implements ApplicationContextAware { ...@@ -121,7 +103,7 @@ public final class XxlJobDynamicScheduler implements ApplicationContextAware {
} }
// set-cache // set-cache
executorBiz = (ExecutorBiz) new NetComClientProxy(ExecutorBiz.class, address, accessToken).getObject(); executorBiz = (ExecutorBiz) new NetComClientProxy(ExecutorBiz.class, address, XxlJobAdminConfig.getAdminConfig().getAccessToken()).getObject();
executorBizRepository.put(address, executorBiz); executorBizRepository.put(address, executorBiz);
return executorBiz; return executorBiz;
} }
...@@ -362,6 +344,7 @@ public final class XxlJobDynamicScheduler implements ApplicationContextAware { ...@@ -362,6 +344,7 @@ public final class XxlJobDynamicScheduler implements ApplicationContextAware {
return result; return result;
} }
/** /**
* finaAllJobList * finaAllJobList
* *
......
package com.xxl.job.admin.core.thread; package com.xxl.job.admin.core.thread;
import com.xxl.job.admin.core.conf.XxlJobAdminConfig;
import com.xxl.job.admin.core.model.XxlJobGroup; import com.xxl.job.admin.core.model.XxlJobGroup;
import com.xxl.job.admin.core.model.XxlJobInfo; import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.core.model.XxlJobLog; import com.xxl.job.admin.core.model.XxlJobLog;
...@@ -52,7 +53,7 @@ public class JobFailMonitorHelper { ...@@ -52,7 +53,7 @@ public class JobFailMonitorHelper {
if (jobLogId==null || jobLogId==0) { if (jobLogId==null || jobLogId==0) {
continue; continue;
} }
XxlJobLog log = XxlJobDynamicScheduler.xxlJobLogDao.load(jobLogId); XxlJobLog log = XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().load(jobLogId);
if (log == null) { if (log == null) {
continue; continue;
} }
...@@ -70,13 +71,13 @@ public class JobFailMonitorHelper { ...@@ -70,13 +71,13 @@ public class JobFailMonitorHelper {
// job fail, // job fail,
// 1、fail retry // 1、fail retry
XxlJobInfo info = XxlJobDynamicScheduler.xxlJobInfoDao.loadById(log.getJobId()); XxlJobInfo info = XxlJobAdminConfig.getAdminConfig().getXxlJobInfoDao().loadById(log.getJobId());
if (log.getExecutorFailRetryCount() > 0) { if (log.getExecutorFailRetryCount() > 0) {
JobTriggerPoolHelper.trigger(log.getJobId(), TriggerTypeEnum.RETRY, (log.getExecutorFailRetryCount()-1), log.getExecutorShardingParam(), null); JobTriggerPoolHelper.trigger(log.getJobId(), TriggerTypeEnum.RETRY, (log.getExecutorFailRetryCount()-1), log.getExecutorShardingParam(), null);
String retryMsg = "<br><br><span style=\"color:#F39C12;\" > >>>>>>>>>>>"+ I18nUtil.getString("jobconf_trigger_type_retry") +"<<<<<<<<<<< </span><br>"; String retryMsg = "<br><br><span style=\"color:#F39C12;\" > >>>>>>>>>>>"+ I18nUtil.getString("jobconf_trigger_type_retry") +"<<<<<<<<<<< </span><br>";
log.setTriggerMsg(log.getTriggerMsg() + retryMsg); log.setTriggerMsg(log.getTriggerMsg() + retryMsg);
XxlJobDynamicScheduler.xxlJobLogDao.updateTriggerInfo(log); XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().updateTriggerInfo(log);
} }
// 2、fail alarm // 2、fail alarm
...@@ -101,10 +102,10 @@ public class JobFailMonitorHelper { ...@@ -101,10 +102,10 @@ public class JobFailMonitorHelper {
int drainToNum = getInstance().queue.drainTo(jobLogIdList); int drainToNum = getInstance().queue.drainTo(jobLogIdList);
if (jobLogIdList!=null && jobLogIdList.size()>0) { if (jobLogIdList!=null && jobLogIdList.size()>0) {
for (Integer jobLogId: jobLogIdList) { for (Integer jobLogId: jobLogIdList) {
XxlJobLog log = XxlJobDynamicScheduler.xxlJobLogDao.load(jobLogId); XxlJobLog log = XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().load(jobLogId);
if (ReturnT.FAIL_CODE == log.getTriggerCode()|| ReturnT.FAIL_CODE==log.getHandleCode()) { if (ReturnT.FAIL_CODE == log.getTriggerCode()|| ReturnT.FAIL_CODE==log.getHandleCode()) {
// job fail, // job fail,
XxlJobInfo info = XxlJobDynamicScheduler.xxlJobInfoDao.loadById(log.getJobId()); XxlJobInfo info = XxlJobAdminConfig.getAdminConfig().getXxlJobInfoDao().loadById(log.getJobId());
failAlarm(info, log); failAlarm(info, log);
logger.info(">>>>>>>>>>> job monitor last, job fail, JobLogId:{}", jobLogId); logger.info(">>>>>>>>>>> job monitor last, job fail, JobLogId:{}", jobLogId);
...@@ -180,7 +181,7 @@ public class JobFailMonitorHelper { ...@@ -180,7 +181,7 @@ public class JobFailMonitorHelper {
Set<String> emailSet = new HashSet<String>(Arrays.asList(info.getAlarmEmail().split(","))); Set<String> emailSet = new HashSet<String>(Arrays.asList(info.getAlarmEmail().split(",")));
for (String email: emailSet) { for (String email: emailSet) {
XxlJobGroup group = XxlJobDynamicScheduler.xxlJobGroupDao.load(Integer.valueOf(info.getJobGroup())); XxlJobGroup group = XxlJobAdminConfig.getAdminConfig().getXxlJobGroupDao().load(Integer.valueOf(info.getJobGroup()));
String title = I18nUtil.getString("jobconf_monitor"); String title = I18nUtil.getString("jobconf_monitor");
String content = MessageFormat.format(mailBodyTemplate, String content = MessageFormat.format(mailBodyTemplate,
......
package com.xxl.job.admin.core.thread; package com.xxl.job.admin.core.thread;
import com.xxl.job.admin.core.conf.XxlJobAdminConfig;
import com.xxl.job.admin.core.model.XxlJobGroup; import com.xxl.job.admin.core.model.XxlJobGroup;
import com.xxl.job.admin.core.model.XxlJobRegistry; import com.xxl.job.admin.core.model.XxlJobRegistry;
import com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler; import com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler;
...@@ -36,15 +37,15 @@ public class JobRegistryMonitorHelper { ...@@ -36,15 +37,15 @@ public class JobRegistryMonitorHelper {
while (!toStop) { while (!toStop) {
try { try {
// auto registry group // auto registry group
List<XxlJobGroup> groupList = XxlJobDynamicScheduler.xxlJobGroupDao.findByAddressType(0); List<XxlJobGroup> groupList = XxlJobAdminConfig.getAdminConfig().getXxlJobGroupDao().findByAddressType(0);
if (CollectionUtils.isNotEmpty(groupList)) { if (CollectionUtils.isNotEmpty(groupList)) {
// remove dead address (admin/executor) // remove dead address (admin/executor)
XxlJobDynamicScheduler.xxlJobRegistryDao.removeDead(RegistryConfig.DEAD_TIMEOUT); XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().removeDead(RegistryConfig.DEAD_TIMEOUT);
// fresh online address (admin/executor) // fresh online address (admin/executor)
HashMap<String, List<String>> appAddressMap = new HashMap<String, List<String>>(); HashMap<String, List<String>> appAddressMap = new HashMap<String, List<String>>();
List<XxlJobRegistry> list = XxlJobDynamicScheduler.xxlJobRegistryDao.findAll(RegistryConfig.DEAD_TIMEOUT); List<XxlJobRegistry> list = XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().findAll(RegistryConfig.DEAD_TIMEOUT);
if (list != null) { if (list != null) {
for (XxlJobRegistry item: list) { for (XxlJobRegistry item: list) {
if (RegistryConfig.RegistType.EXECUTOR.name().equals(item.getRegistryGroup())) { if (RegistryConfig.RegistType.EXECUTOR.name().equals(item.getRegistryGroup())) {
...@@ -71,7 +72,7 @@ public class JobRegistryMonitorHelper { ...@@ -71,7 +72,7 @@ public class JobRegistryMonitorHelper {
addressListStr = StringUtils.join(registryList, ","); addressListStr = StringUtils.join(registryList, ",");
} }
group.setAddressList(addressListStr); group.setAddressList(addressListStr);
XxlJobDynamicScheduler.xxlJobGroupDao.update(group); XxlJobAdminConfig.getAdminConfig().getXxlJobGroupDao().update(group);
} }
} }
} catch (Exception e) { } catch (Exception e) {
......
package com.xxl.job.admin.core.trigger; package com.xxl.job.admin.core.trigger;
import com.xxl.job.admin.core.conf.XxlJobAdminConfig;
import com.xxl.job.admin.core.model.XxlJobGroup; import com.xxl.job.admin.core.model.XxlJobGroup;
import com.xxl.job.admin.core.model.XxlJobInfo; import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.core.model.XxlJobLog; import com.xxl.job.admin.core.model.XxlJobLog;
...@@ -41,7 +42,7 @@ public class XxlJobTrigger { ...@@ -41,7 +42,7 @@ public class XxlJobTrigger {
*/ */
public static void trigger(int jobId, TriggerTypeEnum triggerType, int failRetryCount, String executorShardingParam, String executorParam) { public static void trigger(int jobId, TriggerTypeEnum triggerType, int failRetryCount, String executorShardingParam, String executorParam) {
// load data // load data
XxlJobInfo jobInfo = XxlJobDynamicScheduler.xxlJobInfoDao.loadById(jobId); XxlJobInfo jobInfo = XxlJobAdminConfig.getAdminConfig().getXxlJobInfoDao().loadById(jobId);
if (jobInfo == null) { if (jobInfo == null) {
logger.warn(">>>>>>>>>>>> trigger fail, jobId invalid,jobId={}", jobId); logger.warn(">>>>>>>>>>>> trigger fail, jobId invalid,jobId={}", jobId);
return; return;
...@@ -50,7 +51,7 @@ public class XxlJobTrigger { ...@@ -50,7 +51,7 @@ public class XxlJobTrigger {
jobInfo.setExecutorParam(executorParam); jobInfo.setExecutorParam(executorParam);
} }
int finalFailRetryCount = failRetryCount>=0?failRetryCount:jobInfo.getExecutorFailRetryCount(); int finalFailRetryCount = failRetryCount>=0?failRetryCount:jobInfo.getExecutorFailRetryCount();
XxlJobGroup group = XxlJobDynamicScheduler.xxlJobGroupDao.load(jobInfo.getJobGroup()); XxlJobGroup group = XxlJobAdminConfig.getAdminConfig().getXxlJobGroupDao().load(jobInfo.getJobGroup());
// sharding param // sharding param
int[] shardingParam = null; int[] shardingParam = null;
...@@ -96,7 +97,7 @@ public class XxlJobTrigger { ...@@ -96,7 +97,7 @@ public class XxlJobTrigger {
jobLog.setJobGroup(jobInfo.getJobGroup()); jobLog.setJobGroup(jobInfo.getJobGroup());
jobLog.setJobId(jobInfo.getId()); jobLog.setJobId(jobInfo.getId());
jobLog.setTriggerTime(new Date()); jobLog.setTriggerTime(new Date());
XxlJobDynamicScheduler.xxlJobLogDao.save(jobLog); XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().save(jobLog);
logger.debug(">>>>>>>>>>> xxl-job trigger start, jobId:{}", jobLog.getId()); logger.debug(">>>>>>>>>>> xxl-job trigger start, jobId:{}", jobLog.getId());
// 2、init trigger-param // 2、init trigger-param
...@@ -169,7 +170,7 @@ public class XxlJobTrigger { ...@@ -169,7 +170,7 @@ public class XxlJobTrigger {
//jobLog.setTriggerTime(); //jobLog.setTriggerTime();
jobLog.setTriggerCode(triggerResult.getCode()); jobLog.setTriggerCode(triggerResult.getCode());
jobLog.setTriggerMsg(triggerMsgSb.toString()); jobLog.setTriggerMsg(triggerMsgSb.toString());
XxlJobDynamicScheduler.xxlJobLogDao.updateTriggerInfo(jobLog); XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().updateTriggerInfo(jobLog);
// 7、monitor trigger // 7、monitor trigger
JobFailMonitorHelper.monitor(jobLog.getId()); JobFailMonitorHelper.monitor(jobLog.getId());
......
package com.xxl.job.admin.dao; package com.xxl.job.admin.dao;
import com.xxl.job.admin.core.model.XxlJobGroup; import com.xxl.job.admin.core.model.XxlJobGroup;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import java.util.List; import java.util.List;
...@@ -8,6 +9,7 @@ import java.util.List; ...@@ -8,6 +9,7 @@ import java.util.List;
/** /**
* Created by xuxueli on 16/9/30. * Created by xuxueli on 16/9/30.
*/ */
@Mapper
public interface XxlJobGroupDao { public interface XxlJobGroupDao {
public List<XxlJobGroup> findAll(); public List<XxlJobGroup> findAll();
......
package com.xxl.job.admin.dao; package com.xxl.job.admin.dao;
import com.xxl.job.admin.core.model.XxlJobInfo; import com.xxl.job.admin.core.model.XxlJobInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import java.util.List; import java.util.List;
...@@ -10,6 +11,7 @@ import java.util.List; ...@@ -10,6 +11,7 @@ import java.util.List;
* job info * job info
* @author xuxueli 2016-1-12 18:03:45 * @author xuxueli 2016-1-12 18:03:45
*/ */
@Mapper
public interface XxlJobInfoDao { public interface XxlJobInfoDao {
public List<XxlJobInfo> pageList(@Param("offset") int offset, public List<XxlJobInfo> pageList(@Param("offset") int offset,
......
package com.xxl.job.admin.dao; package com.xxl.job.admin.dao;
import com.xxl.job.admin.core.model.XxlJobLog; import com.xxl.job.admin.core.model.XxlJobLog;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import java.util.Date; import java.util.Date;
...@@ -11,6 +12,7 @@ import java.util.Map; ...@@ -11,6 +12,7 @@ import java.util.Map;
* job log * job log
* @author xuxueli 2016-1-12 18:03:06 * @author xuxueli 2016-1-12 18:03:06
*/ */
@Mapper
public interface XxlJobLogDao { public interface XxlJobLogDao {
public List<XxlJobLog> pageList(@Param("offset") int offset, public List<XxlJobLog> pageList(@Param("offset") int offset,
......
package com.xxl.job.admin.dao; package com.xxl.job.admin.dao;
import com.xxl.job.admin.core.model.XxlJobLogGlue; import com.xxl.job.admin.core.model.XxlJobLogGlue;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import java.util.List; import java.util.List;
...@@ -9,6 +10,7 @@ import java.util.List; ...@@ -9,6 +10,7 @@ import java.util.List;
* job log for glue * job log for glue
* @author xuxueli 2016-5-19 18:04:56 * @author xuxueli 2016-5-19 18:04:56
*/ */
@Mapper
public interface XxlJobLogGlueDao { public interface XxlJobLogGlueDao {
public int save(XxlJobLogGlue xxlJobLogGlue); public int save(XxlJobLogGlue xxlJobLogGlue);
......
package com.xxl.job.admin.dao; package com.xxl.job.admin.dao;
import com.xxl.job.admin.core.model.XxlJobRegistry; import com.xxl.job.admin.core.model.XxlJobRegistry;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import java.util.List; import java.util.List;
...@@ -8,6 +9,7 @@ import java.util.List; ...@@ -8,6 +9,7 @@ import java.util.List;
/** /**
* Created by xuxueli on 16/9/30. * Created by xuxueli on 16/9/30.
*/ */
@Mapper
public interface XxlJobRegistryDao { public interface XxlJobRegistryDao {
public int removeDead(@Param("timeout") int timeout); public int removeDead(@Param("timeout") int timeout);
......
### xxl-job db (use &amp; replace & in xml) ### web
xxl.job.db.driverClass=com.mysql.jdbc.Driver server.port=8080
xxl.job.db.url=jdbc:mysql://localhost:3306/xxl-job?useUnicode=true&characterEncoding=UTF-8 server.context-path=/xxl-job-admin
xxl.job.db.user=root
xxl.job.db.password=root_pwd ### resources
spring.mvc.static-path-pattern=/static/**
spring.resources.static-locations=classpath:/static/
### freemarker
spring.freemarker.templateLoaderPath=classpath:/templates/
spring.freemarker.suffix=.ftl
spring.freemarker.charset=UTF-8
spring.freemarker.request-context-attribute=request
### mybatis
mybatis.mapper-locations=classpath:/mybatis-mapper/*Mapper.xml
### xxl-job, datasource
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/xxl-job?Unicode=true&characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=root_pwd
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.type=org.apache.tomcat.jdbc.pool.DataSource
spring.datasource.tomcat.max-wait=10000
spring.datasource.tomcat.max-active=30
spring.datasource.tomcat.test-on-borrow=true
### xxl-job email ### xxl-job email
xxl.job.mail.host=smtp.163.com xxl.job.mail.host=smtp.163.com
...@@ -20,4 +42,4 @@ xxl.job.login.password=123456 ...@@ -20,4 +42,4 @@ xxl.job.login.password=123456
xxl.job.accessToken= xxl.job.accessToken=
### xxl-job, i18n (default empty as chinese, "en" as english) ### xxl-job, i18n (default empty as chinese, "en" as english)
xxl.job.i18n= xxl.job.i18n=
\ No newline at end of file
template_update_delay=0
default_encoding=UTF-8
output_encoding=UTF-8
locale=zh_CN
number_format=0.##########
date_format=yyyy-MM-dd
time_format=HH:mm:ss
datetime_format=yyyy-MM-dd HH:mm:s
classic_compatible=true
template_exception_handler=ignore
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration PUBLIC "-//log4j/log4j Configuration//EN" "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" threshold="null" debug="null">
<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
<param name="Target" value="System.out" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-d{yyyy-MM-dd HH:mm:ss} xxl-job-admin [%c]-[%t]-[%M]-[%L]-[%p] %m%n"/>
</layout>
</appender>
<appender name="FILE" class="org.apache.log4j.DailyRollingFileAppender">
<param name="file" value="/data/applogs/xxl-job/xxl-job-admin.log"/>
<param name="append" value="true"/>
<param name="encoding" value="UTF-8"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-d{yyyy-MM-dd HH:mm:ss} xxl-job-admin [%c]-[%t]-[%M]-[%L]-[%p] %m%n"/>
</layout>
</appender>
<root>
<level value="INFO" />
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
</root>
</log4j:configuration>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false" scan="true" scanPeriod="1 seconds">
<contextName>logback</contextName>
<property name="log.path" value="/data/applogs/xxl-job/xxl-job-admin.log"/>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}.%d{yyyy-MM-dd}.zip</fileNamePattern>
</rollingPolicy>
<encoder>
<pattern>%date %level [%thread] %logger{36} [%file : %line] %msg%n
</pattern>
</encoder>
</appender>
<root level="info">
<appender-ref ref="console"/>
<appender-ref ref="file"/>
</root>
</configuration>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.xxl.job.admin.core.conf, com.xxl.job.admin.service, com.xxl.job.admin.dao" />
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/WEB-INF/template/" />
<property name="freemarkerSettings">
<bean class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="location" value="classpath:freemarker.properties" />
</bean>
</property>
</bean>
</beans>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="fileEncoding" value="utf-8" />
<property name="locations">
<list>
<value>classpath*:xxl-job-admin.properties</value>
</list>
</property>
</bean>
<!-- ********************************* part 1 :for datasource ********************************* -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="${xxl.job.db.driverClass}" />
<property name="jdbcUrl" value="${xxl.job.db.url}" />
<property name="user" value="${xxl.job.db.user}" />
<property name="password" value="${xxl.job.db.password}" />
<property name="initialPoolSize" value="3" />
<property name="minPoolSize" value="2" />
<property name="maxPoolSize" value="10" />
<property name="maxIdleTime" value="60" />
<property name="acquireRetryDelay" value="1000" />
<property name="acquireRetryAttempts" value="10" />
<property name="preferredTestQuery" value="SELECT 1" />
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations" value="classpath:mybatis-mapper/*.xml"/>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<property name="basePackage" value="com.xxl.job.admin.dao" />
</bean>
<!-- ********************************* part 2 :for tx ********************************* -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
<!-- ********************************* part 3 :for xxl-job scheduler ********************************* -->
<bean id="quartzScheduler" lazy-init="false" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="autoStartup" value="true" /> <!--自动启动 -->
<property name="startupDelay" value="20" /> <!--延时启动,应用启动成功后在启动 -->
<property name="overwriteExistingJobs" value="true" /> <!--覆盖DB中JOB:true、以数据库中已经存在的为准:false -->
<property name="applicationContextSchedulerContextKey" value="applicationContextKey" />
<property name="configLocation" value="classpath:quartz.properties"/>
</bean>
<bean id="xxlJobDynamicScheduler" class="com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler" init-method="init" destroy-method="destroy" >
<!-- (轻易不要变更“调度器名称”, 任务创建时会绑定该“调度器名称”) -->
<property name="scheduler" ref="quartzScheduler"/>
<property name="accessToken" value="${xxl.job.accessToken}" />
</bean>
</beans>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<mvc:annotation-driven />
<context:component-scan base-package="com.xxl.job.admin.controller" />
<mvc:resources mapping="/favicon.ico" location="/favicon.ico" />
<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="" />
<property name="suffix" value=".ftl" />
<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>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.xxl.job.admin.controller.interceptor.PermissionInterceptor"/>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.xxl.job.admin.controller.interceptor.CookieInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
<bean id="exceptionResolver" class="com.xxl.job.admin.controller.resolver.WebExceptionResolver" />
</beans>
\ No newline at end of file
$(function(){ $(function(){
// logout // logout
$("#logoutBtn").click(function(){ $("#logoutBtn").click(function(){
layer.confirm( I18n.logout_confirm , { layer.confirm( I18n.logout_confirm , {
icon: 3, icon: 3,
title: I18n.system_tips , title: I18n.system_tips ,
btn: [ I18n.system_ok, I18n.system_cancel ] btn: [ I18n.system_ok, I18n.system_cancel ]
}, function(index){ }, function(index){
layer.close(index); layer.close(index);
$.post(base_url + "/logout", function(data, status) { $.post(base_url + "/logout", function(data, status) {
if (data.code == "200") { if (data.code == "200") {
layer.msg( I18n.logout_success ); layer.msg( I18n.logout_success );
setTimeout(function(){ setTimeout(function(){
window.location.href = base_url + "/"; window.location.href = base_url + "/";
}, 500); }, 500);
} else { } else {
layer.open({ layer.open({
title: I18n.system_tips , title: I18n.system_tips ,
btn: [ I18n.system_ok ], btn: [ I18n.system_ok ],
content: (data.msg || I18n.logout_fail), content: (data.msg || I18n.logout_fail),
icon: '2' icon: '2'
}); });
} }
}); });
}); });
}); });
// slideToTop // slideToTop
var slideToTop = $("<div />"); var slideToTop = $("<div />");
slideToTop.html('<i class="fa fa-chevron-up"></i>'); slideToTop.html('<i class="fa fa-chevron-up"></i>');
slideToTop.css({ slideToTop.css({
position: 'fixed', position: 'fixed',
bottom: '20px', bottom: '20px',
right: '25px', right: '25px',
width: '40px', width: '40px',
height: '40px', height: '40px',
color: '#eee', color: '#eee',
'font-size': '', 'font-size': '',
'line-height': '40px', 'line-height': '40px',
'text-align': 'center', 'text-align': 'center',
'background-color': '#222d32', 'background-color': '#222d32',
cursor: 'pointer', cursor: 'pointer',
'border-radius': '5px', 'border-radius': '5px',
'z-index': '99999', 'z-index': '99999',
opacity: '.7', opacity: '.7',
'display': 'none' 'display': 'none'
}); });
slideToTop.on('mouseenter', function () { slideToTop.on('mouseenter', function () {
$(this).css('opacity', '1'); $(this).css('opacity', '1');
}); });
slideToTop.on('mouseout', function () { slideToTop.on('mouseout', function () {
$(this).css('opacity', '.7'); $(this).css('opacity', '.7');
}); });
$('.wrapper').append(slideToTop); $('.wrapper').append(slideToTop);
$(window).scroll(function () { $(window).scroll(function () {
if ($(window).scrollTop() >= 150) { if ($(window).scrollTop() >= 150) {
if (!$(slideToTop).is(':visible')) { if (!$(slideToTop).is(':visible')) {
$(slideToTop).fadeIn(500); $(slideToTop).fadeIn(500);
} }
} else { } else {
$(slideToTop).fadeOut(500); $(slideToTop).fadeOut(500);
} }
}); });
$(slideToTop).click(function () { $(slideToTop).click(function () {
$("html,body").animate({ // firefox ie not support body, chrome support body. but found that new version chrome not support body too. $("html,body").animate({ // firefox ie not support body, chrome support body. but found that new version chrome not support body too.
scrollTop: 0 scrollTop: 0
}, 100); }, 100);
}); });
// left menu status v: js + server + cookie // left menu status v: js + server + cookie
$('.sidebar-toggle').click(function(){ $('.sidebar-toggle').click(function(){
var xxljob_adminlte_settings = $.cookie('xxljob_adminlte_settings'); // on=open,off=close var xxljob_adminlte_settings = $.cookie('xxljob_adminlte_settings'); // on=open,off=close
if ('off' == xxljob_adminlte_settings) { if ('off' == xxljob_adminlte_settings) {
xxljob_adminlte_settings = 'on'; xxljob_adminlte_settings = 'on';
} else { } else {
xxljob_adminlte_settings = 'off'; xxljob_adminlte_settings = 'off';
} }
$.cookie('xxljob_adminlte_settings', xxljob_adminlte_settings, { expires: 7 }); //$.cookie('the_cookie', '', { expires: -1 }); $.cookie('xxljob_adminlte_settings', xxljob_adminlte_settings, { expires: 7 }); //$.cookie('the_cookie', '', { expires: -1 });
}); });
// left menu status v1: js + cookie // left menu status v1: js + cookie
/* /*
var xxljob_adminlte_settings = $.cookie('xxljob_adminlte_settings'); var xxljob_adminlte_settings = $.cookie('xxljob_adminlte_settings');
if (xxljob_adminlte_settings == 'off') { if (xxljob_adminlte_settings == 'off') {
$('body').addClass('sidebar-collapse'); $('body').addClass('sidebar-collapse');
} }
*/ */
}); });
$(function() { $(function() {
// init code editor // init code editor
var codeEditor; var codeEditor;
function initIde(glueSource) { function initIde(glueSource) {
if (codeEditor == null) { if (codeEditor == null) {
codeEditor = CodeMirror(document.getElementById("ideWindow"), { codeEditor = CodeMirror(document.getElementById("ideWindow"), {
mode : ideMode, mode : ideMode,
lineNumbers : true, lineNumbers : true,
matchBrackets : true, matchBrackets : true,
value: glueSource value: glueSource
}); });
} else { } else {
codeEditor.setValue(glueSource); codeEditor.setValue(glueSource);
} }
} }
initIde($("#version_now").val()); initIde($("#version_now").val());
// code change // code change
$(".source_version").click(function(){ $(".source_version").click(function(){
var sourceId = $(this).attr('version'); var sourceId = $(this).attr('version');
var temp = $( "#" + sourceId ).val(); var temp = $( "#" + sourceId ).val();
//codeEditor.setValue(''); //codeEditor.setValue('');
initIde(temp); initIde(temp);
}); });
// code source save // code source save
$("#save").click(function() { $("#save").click(function() {
$('#saveModal').modal({backdrop: false, keyboard: false}).modal('show'); $('#saveModal').modal({backdrop: false, keyboard: false}).modal('show');
}); });
$("#saveModal .ok").click(function() { $("#saveModal .ok").click(function() {
var glueSource = codeEditor.getValue(); var glueSource = codeEditor.getValue();
var glueRemark = $("#glueRemark").val(); var glueRemark = $("#glueRemark").val();
if (!glueRemark) { if (!glueRemark) {
layer.open({ layer.open({
title: I18n.system_tips, title: I18n.system_tips,
btn: [ I18n.system_ok], btn: [ I18n.system_ok],
content: I18n.system_please_input + I18n.jobinfo_glue_remark , content: I18n.system_please_input + I18n.jobinfo_glue_remark ,
icon: '2' icon: '2'
}); });
return; return;
} }
if (glueRemark.length <4 || glueRemark.length > 100) { if (glueRemark.length <4 || glueRemark.length > 100) {
layer.open({ layer.open({
title: I18n.system_tips , title: I18n.system_tips ,
btn: [ I18n.system_ok ], btn: [ I18n.system_ok ],
content: I18n.jobinfo_glue_remark_limit , content: I18n.jobinfo_glue_remark_limit ,
icon: '2' icon: '2'
}); });
return; return;
} }
$.ajax({ $.ajax({
type : 'POST', type : 'POST',
url : base_url + '/jobcode/save', url : base_url + '/jobcode/save',
data : { data : {
'id' : id, 'id' : id,
'glueSource' : glueSource, 'glueSource' : glueSource,
'glueRemark' : glueRemark 'glueRemark' : glueRemark
}, },
dataType : "json", dataType : "json",
success : function(data){ success : function(data){
if (data.code == 200) { if (data.code == 200) {
layer.open({ layer.open({
title: I18n.system_tips, title: I18n.system_tips,
btn: [ I18n.system_ok ], btn: [ I18n.system_ok ],
content: (I18n.system_save + I18n.system_success) , content: (I18n.system_save + I18n.system_success) ,
icon: '1', icon: '1',
end: function(layero, index){ end: function(layero, index){
//$(window).unbind('beforeunload'); //$(window).unbind('beforeunload');
window.location.reload(); window.location.reload();
} }
}); });
} else { } else {
layer.open({ layer.open({
title: I18n.system_tips, title: I18n.system_tips,
btn: [ I18n.system_ok ], btn: [ I18n.system_ok ],
content: (data.msg || (I18n.system_save + I18n.system_fail) ), content: (data.msg || (I18n.system_save + I18n.system_fail) ),
icon: '2' icon: '2'
}); });
} }
} }
}); });
}); });
// before upload // before upload
/*$(window).bind('beforeunload',function(){ /*$(window).bind('beforeunload',function(){
return 'Glue尚未保存,确定离开Glue编辑器?'; return 'Glue尚未保存,确定离开Glue编辑器?';
});*/ });*/
}); });
$(function(){ $(function(){
// input iCheck // input iCheck
$('input').iCheck({ $('input').iCheck({
checkboxClass: 'icheckbox_square-blue', checkboxClass: 'icheckbox_square-blue',
radioClass: 'iradio_square-blue', radioClass: 'iradio_square-blue',
increaseArea: '20%' // optional increaseArea: '20%' // optional
}); });
// login Form Valid // login Form Valid
var loginFormValid = $("#loginForm").validate({ var loginFormValid = $("#loginForm").validate({
errorElement : 'span', errorElement : 'span',
errorClass : 'help-block', errorClass : 'help-block',
focusInvalid : true, focusInvalid : true,
rules : { rules : {
userName : { userName : {
required : true , required : true ,
minlength: 5, minlength: 5,
maxlength: 18 maxlength: 18
}, },
password : { password : {
required : true , required : true ,
minlength: 5, minlength: 5,
maxlength: 18 maxlength: 18
} }
}, },
messages : { messages : {
userName : { userName : {
required : I18n.login_username_empty, required : I18n.login_username_empty,
minlength : I18n.login_username_lt_5 minlength : I18n.login_username_lt_5
}, },
password : { password : {
required : I18n.login_password_empty , required : I18n.login_password_empty ,
minlength : I18n.login_password_lt_5 minlength : I18n.login_password_lt_5
/*,maxlength:"登录密码不应超过18位"*/ /*,maxlength:"登录密码不应超过18位"*/
} }
}, },
highlight : function(element) { highlight : function(element) {
$(element).closest('.form-group').addClass('has-error'); $(element).closest('.form-group').addClass('has-error');
}, },
success : function(label) { success : function(label) {
label.closest('.form-group').removeClass('has-error'); label.closest('.form-group').removeClass('has-error');
label.remove(); label.remove();
}, },
errorPlacement : function(error, element) { errorPlacement : function(error, element) {
element.parent('div').append(error); element.parent('div').append(error);
}, },
submitHandler : function(form) { submitHandler : function(form) {
$.post(base_url + "/login", $("#loginForm").serialize(), function(data, status) { $.post(base_url + "/login", $("#loginForm").serialize(), function(data, status) {
if (data.code == "200") { if (data.code == "200") {
layer.msg( I18n.login_success ); layer.msg( I18n.login_success );
setTimeout(function(){ setTimeout(function(){
window.location.href = base_url; window.location.href = base_url;
}, 500); }, 500);
} else { } else {
layer.open({ layer.open({
title: I18n.system_tips, title: I18n.system_tips,
btn: [ I18n.system_ok ], btn: [ I18n.system_ok ],
content: (data.msg || I18n.login_fail ), content: (data.msg || I18n.login_fail ),
icon: '2' icon: '2'
}); });
} }
}); });
} }
}); });
}); });
\ No newline at end of file
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>Error</title> <title>Error</title>
<style type="text/css"> <style type="text/css">
body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; } body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
div.dialog { div.dialog {
width: 80%; width: 80%;
padding: 1em 4em; padding: 1em 4em;
margin: 4em auto 0 auto; margin: 4em auto 0 auto;
border: 1px solid #ccc; border: 1px solid #ccc;
border-right-color: #999; border-right-color: #999;
border-bottom-color: #999; border-bottom-color: #999;
} }
h1 { font-size: 100%; color: #f00; line-height: 1.5em; } h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
</style> </style>
</head> </head>
</head> </head>
<body> <body>
<div class="dialog"> <div class="dialog">
<h1>System Error</h1> <h1>System Error</h1>
<p>${exceptionMsg}</p> <p>${exceptionMsg}</p>
<a href="javascript:window.location.href='${request.contextPath}/'">Back</a> <a href="javascript:window.location.href='${request.contextPath}/'">Back</a>
</p> </p>
</div> </div>
</body> </body>
</html> </html>
\ No newline at end of file
<#macro commonStyle> <#macro commonStyle>
<#-- favicon --> <#-- favicon -->
<link rel="icon" href="favicon.ico" /> <link rel="icon" href="${request.contextPath}/static/favicon.ico" />
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- Tell the browser to be responsive to screen width --> <!-- Tell the browser to be responsive to screen width -->
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport"> <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<!-- Bootstrap 3.3.5 --> <!-- Bootstrap 3.3.5 -->
<link rel="stylesheet" href="${request.contextPath}/static/adminlte/bootstrap/css/bootstrap.min.css"> <link rel="stylesheet" href="${request.contextPath}/static/adminlte/bootstrap/css/bootstrap.min.css">
<!-- Font Awesome --> <!-- Font Awesome -->
<!-- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.5.0/css/font-awesome.min.css"> --> <!-- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.5.0/css/font-awesome.min.css"> -->
<link rel="stylesheet" href="${request.contextPath}/static/plugins/font-awesome-4.5.0/css/font-awesome.min.css"> <link rel="stylesheet" href="${request.contextPath}/static/plugins/font-awesome-4.5.0/css/font-awesome.min.css">
<!-- Ionicons --> <!-- Ionicons -->
<!-- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ionicons/2.0.1/css/ionicons.min.css"> --> <!-- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ionicons/2.0.1/css/ionicons.min.css"> -->
<link rel="stylesheet" href="${request.contextPath}/static/plugins/ionicons-2.0.1/css/ionicons.min.css"> <link rel="stylesheet" href="${request.contextPath}/static/plugins/ionicons-2.0.1/css/ionicons.min.css">
<!-- Theme style --> <!-- Theme style -->
<link rel="stylesheet" href="${request.contextPath}/static/adminlte/dist/css/AdminLTE-local.min.css"> <link rel="stylesheet" href="${request.contextPath}/static/adminlte/dist/css/AdminLTE-local.min.css">
<!-- AdminLTE Skins. Choose a skin from the css/skins folder instead of downloading all of them to reduce the load. --> <!-- AdminLTE Skins. Choose a skin from the css/skins folder instead of downloading all of them to reduce the load. -->
<link rel="stylesheet" href="${request.contextPath}/static/adminlte/dist/css/skins/_all-skins.min.css"> <link rel="stylesheet" href="${request.contextPath}/static/adminlte/dist/css/skins/_all-skins.min.css">
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries --> <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// --> <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]> <!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script> <script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script> <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]--> <![endif]-->
<!-- pace --> <!-- pace -->
<link rel="stylesheet" href="${request.contextPath}/static/plugins/pace/themes/pace-theme-flash.css"> <link rel="stylesheet" href="${request.contextPath}/static/plugins/pace/themes/pace-theme-flash.css">
<#-- i18n --> <#-- i18n -->
<#global I18n = I18nUtil.getMultString()?eval /> <#global I18n = I18nUtil.getMultString()?eval />
</#macro> </#macro>
<#macro commonScript> <#macro commonScript>
<!-- jQuery 2.1.4 --> <!-- jQuery 2.1.4 -->
<script src="${request.contextPath}/static/adminlte/plugins/jQuery/jquery-2.2.3.min.js"></script> <script src="${request.contextPath}/static/adminlte/plugins/jQuery/jquery-2.2.3.min.js"></script>
<!-- Bootstrap 3.3.5 --> <!-- Bootstrap 3.3.5 -->
<script src="${request.contextPath}/static/adminlte/bootstrap/js/bootstrap.min.js"></script> <script src="${request.contextPath}/static/adminlte/bootstrap/js/bootstrap.min.js"></script>
<!-- FastClick --> <!-- FastClick -->
<script src="${request.contextPath}/static/adminlte/plugins/fastclick/fastclick.min.js"></script> <script src="${request.contextPath}/static/adminlte/plugins/fastclick/fastclick.min.js"></script>
<!-- AdminLTE App --> <!-- AdminLTE App -->
<script src="${request.contextPath}/static/adminlte/dist/js/app.min.js"></script> <script src="${request.contextPath}/static/adminlte/dist/js/app.min.js"></script>
<#-- jquery.slimscroll --> <#-- jquery.slimscroll -->
<script src="${request.contextPath}/static/adminlte/plugins/slimScroll/jquery.slimscroll.min.js"></script> <script src="${request.contextPath}/static/adminlte/plugins/slimScroll/jquery.slimscroll.min.js"></script>
<!-- pace --> <!-- pace -->
<script src="${request.contextPath}/static/plugins/pace/pace.min.js"></script> <script src="${request.contextPath}/static/plugins/pace/pace.min.js"></script>
<#-- jquery cookie --> <#-- jquery cookie -->
<script src="${request.contextPath}/static/plugins/jquery/jquery.cookie.js"></script> <script src="${request.contextPath}/static/plugins/jquery/jquery.cookie.js"></script>
<#-- layer --> <#-- layer -->
<script src="${request.contextPath}/static/plugins/layer/layer.js"></script> <script src="${request.contextPath}/static/plugins/layer/layer.js"></script>
<#-- common --> <#-- common -->
<script src="${request.contextPath}/static/js/common.1.js"></script> <script src="${request.contextPath}/static/js/common.1.js"></script>
<script> <script>
var base_url = '${request.contextPath}'; var base_url = '${request.contextPath}';
var I18n = ${I18nUtil.getMultString()}; var I18n = ${I18nUtil.getMultString()};
</script> </script>
</#macro> </#macro>
<#macro commonHeader> <#macro commonHeader>
<header class="main-header"> <header class="main-header">
<a href="${request.contextPath}/" class="logo"> <a href="${request.contextPath}/" class="logo">
<span class="logo-mini"><b>XXL</b></span> <span class="logo-mini"><b>XXL</b></span>
<span class="logo-lg"><b>${I18n.admin_name}</b></span> <span class="logo-lg"><b>${I18n.admin_name}</b></span>
</a> </a>
<nav class="navbar navbar-static-top" role="navigation"> <nav class="navbar navbar-static-top" role="navigation">
<a href="#" class="sidebar-toggle" data-toggle="offcanvas" role="button"><span class="sr-only">切换导航</span></a> <a href="#" class="sidebar-toggle" data-toggle="offcanvas" role="button"><span class="sr-only">切换导航</span></a>
<div class="navbar-custom-menu"> <div class="navbar-custom-menu">
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">
<li class="dropdown user user-menu"> <li class="dropdown user user-menu">
<a href=";" id="logoutBtn" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false"> <a href=";" id="logoutBtn" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
<span class="hidden-xs">${I18n.logout_btn}</span> <span class="hidden-xs">${I18n.logout_btn}</span>
</a> </a>
</li> </li>
</ul> </ul>
</div> </div>
</nav> </nav>
</header> </header>
</#macro> </#macro>
<#macro commonLeft pageName > <#macro commonLeft pageName >
<!-- Left side column. contains the logo and sidebar --> <!-- Left side column. contains the logo and sidebar -->
<aside class="main-sidebar"> <aside class="main-sidebar">
<!-- sidebar: style can be found in sidebar.less --> <!-- sidebar: style can be found in sidebar.less -->
<section class="sidebar"> <section class="sidebar">
<!-- sidebar menu: : style can be found in sidebar.less --> <!-- sidebar menu: : style can be found in sidebar.less -->
<ul class="sidebar-menu"> <ul class="sidebar-menu">
<li class="header">${I18n.system_nav}</li> <li class="header">${I18n.system_nav}</li>
<li class="nav-click <#if pageName == "index">active</#if>" ><a href="${request.contextPath}/"><i class="fa fa-circle-o text-aqua"></i><span>${I18n.job_dashboard_name}</span></a></li> <li class="nav-click <#if pageName == "index">active</#if>" ><a href="${request.contextPath}/"><i class="fa fa-circle-o text-aqua"></i><span>${I18n.job_dashboard_name}</span></a></li>
<li class="nav-click <#if pageName == "jobinfo">active</#if>" ><a href="${request.contextPath}/jobinfo"><i class="fa fa-circle-o text-yellow"></i><span>${I18n.jobinfo_name}</span></a></li> <li class="nav-click <#if pageName == "jobinfo">active</#if>" ><a href="${request.contextPath}/jobinfo"><i class="fa fa-circle-o text-yellow"></i><span>${I18n.jobinfo_name}</span></a></li>
<li class="nav-click <#if pageName == "joblog">active</#if>" ><a href="${request.contextPath}/joblog"><i class="fa fa-circle-o text-green"></i><span>${I18n.joblog_name}</span></a></li> <li class="nav-click <#if pageName == "joblog">active</#if>" ><a href="${request.contextPath}/joblog"><i class="fa fa-circle-o text-green"></i><span>${I18n.joblog_name}</span></a></li>
<li class="nav-click <#if pageName == "jobgroup">active</#if>" ><a href="${request.contextPath}/jobgroup"><i class="fa fa-circle-o text-red"></i><span>${I18n.jobgroup_name}</span></a></li> <li class="nav-click <#if pageName == "jobgroup">active</#if>" ><a href="${request.contextPath}/jobgroup"><i class="fa fa-circle-o text-red"></i><span>${I18n.jobgroup_name}</span></a></li>
<li class="nav-click <#if pageName == "help">active</#if>" ><a href="${request.contextPath}/help"><i class="fa fa-circle-o text-gray"></i><span>${I18n.job_help}</span></a></li> <li class="nav-click <#if pageName == "help">active</#if>" ><a href="${request.contextPath}/help"><i class="fa fa-circle-o text-gray"></i><span>${I18n.job_help}</span></a></li>
</ul> </ul>
</section> </section>
<!-- /.sidebar --> <!-- /.sidebar -->
</aside> </aside>
</#macro> </#macro>
<#macro commonControl > <#macro commonControl >
<!-- Control Sidebar --> <!-- Control Sidebar -->
<aside class="control-sidebar control-sidebar-dark"> <aside class="control-sidebar control-sidebar-dark">
<!-- Create the tabs --> <!-- Create the tabs -->
<ul class="nav nav-tabs nav-justified control-sidebar-tabs"> <ul class="nav nav-tabs nav-justified control-sidebar-tabs">
<li class="active"><a href="#control-sidebar-home-tab" data-toggle="tab"><i class="fa fa-home"></i></a></li> <li class="active"><a href="#control-sidebar-home-tab" data-toggle="tab"><i class="fa fa-home"></i></a></li>
<li><a href="#control-sidebar-settings-tab" data-toggle="tab"><i class="fa fa-gears"></i></a></li> <li><a href="#control-sidebar-settings-tab" data-toggle="tab"><i class="fa fa-gears"></i></a></li>
</ul> </ul>
<!-- Tab panes --> <!-- Tab panes -->
<div class="tab-content"> <div class="tab-content">
<!-- Home tab content --> <!-- Home tab content -->
<div class="tab-pane active" id="control-sidebar-home-tab"> <div class="tab-pane active" id="control-sidebar-home-tab">
<h3 class="control-sidebar-heading">近期活动</h3> <h3 class="control-sidebar-heading">近期活动</h3>
<ul class="control-sidebar-menu"> <ul class="control-sidebar-menu">
<li> <li>
<a href="javascript::;"> <a href="javascript::;">
<i class="menu-icon fa fa-birthday-cake bg-red"></i> <i class="menu-icon fa fa-birthday-cake bg-red"></i>
<div class="menu-info"> <div class="menu-info">
<h4 class="control-sidebar-subheading">张三今天过生日</h4> <h4 class="control-sidebar-subheading">张三今天过生日</h4>
<p>2015-09-10</p> <p>2015-09-10</p>
</div> </div>
</a> </a>
</li> </li>
<li> <li>
<a href="javascript::;"> <a href="javascript::;">
<i class="menu-icon fa fa-user bg-yellow"></i> <i class="menu-icon fa fa-user bg-yellow"></i>
<div class="menu-info"> <div class="menu-info">
<h4 class="control-sidebar-subheading">Frodo 更新了资料</h4> <h4 class="control-sidebar-subheading">Frodo 更新了资料</h4>
<p>更新手机号码 +1(800)555-1234</p> <p>更新手机号码 +1(800)555-1234</p>
</div> </div>
</a> </a>
</li> </li>
<li> <li>
<a href="javascript::;"> <a href="javascript::;">
<i class="menu-icon fa fa-envelope-o bg-light-blue"></i> <i class="menu-icon fa fa-envelope-o bg-light-blue"></i>
<div class="menu-info"> <div class="menu-info">
<h4 class="control-sidebar-subheading">Nora 加入邮件列表</h4> <h4 class="control-sidebar-subheading">Nora 加入邮件列表</h4>
<p>nora@example.com</p> <p>nora@example.com</p>
</div> </div>
</a> </a>
</li> </li>
<li> <li>
<a href="javascript::;"> <a href="javascript::;">
<i class="menu-icon fa fa-file-code-o bg-green"></i> <i class="menu-icon fa fa-file-code-o bg-green"></i>
<div class="menu-info"> <div class="menu-info">
<h4 class="control-sidebar-subheading">001号定时作业调度</h4> <h4 class="control-sidebar-subheading">001号定时作业调度</h4>
<p>5秒前执行</p> <p>5秒前执行</p>
</div> </div>
</a> </a>
</li> </li>
</ul> </ul>
<!-- /.control-sidebar-menu --> <!-- /.control-sidebar-menu -->
</div> </div>
<!-- /.tab-pane --> <!-- /.tab-pane -->
<!-- Settings tab content --> <!-- Settings tab content -->
<div class="tab-pane" id="control-sidebar-settings-tab"> <div class="tab-pane" id="control-sidebar-settings-tab">
<form method="post"> <form method="post">
<h3 class="control-sidebar-heading">个人设置</h3> <h3 class="control-sidebar-heading">个人设置</h3>
<div class="form-group"> <div class="form-group">
<label class="control-sidebar-subheading"> 左侧菜单自适应 <label class="control-sidebar-subheading"> 左侧菜单自适应
<input type="checkbox" class="pull-right" checked> <input type="checkbox" class="pull-right" checked>
</label> </label>
<p>左侧菜单栏样式自适应</p> <p>左侧菜单栏样式自适应</p>
</div> </div>
<!-- /.form-group --> <!-- /.form-group -->
</form> </form>
</div> </div>
<!-- /.tab-pane --> <!-- /.tab-pane -->
</div> </div>
</aside> </aside>
<!-- /.control-sidebar --> <!-- /.control-sidebar -->
<!-- Add the sidebar's background. This div must be placed immediately after the control sidebar --> <!-- Add the sidebar's background. This div must be placed immediately after the control sidebar -->
<div class="control-sidebar-bg"></div> <div class="control-sidebar-bg"></div>
</#macro> </#macro>
<#macro commonFooter > <#macro commonFooter >
<footer class="main-footer"> <footer class="main-footer">
Powered by <b>XXL-JOB</b> ${I18n.admin_version} Powered by <b>XXL-JOB</b> ${I18n.admin_version}
<div class="pull-right hidden-xs"> <div class="pull-right hidden-xs">
<strong>Copyright &copy; 2015-${.now?string('yyyy')} &nbsp; <strong>Copyright &copy; 2015-${.now?string('yyyy')} &nbsp;
<a href="http://www.xuxueli.com/" target="_blank" >xuxueli</a> <a href="http://www.xuxueli.com/" target="_blank" >xuxueli</a>
&nbsp; &nbsp;
<a href="https://github.com/xuxueli/xxl-job" target="_blank" >github</a> <a href="https://github.com/xuxueli/xxl-job" target="_blank" >github</a>
</strong><!-- All rights reserved. --> </strong><!-- All rights reserved. -->
</div> </div>
</footer> </footer>
</#macro> </#macro>
\ No newline at end of file
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<#import "/common/common.macro.ftl" as netCommon> <#import "./common/common.macro.ftl" as netCommon>
<@netCommon.commonStyle /> <@netCommon.commonStyle />
<title>${I18n.admin_name}</title> <title>${I18n.admin_name}</title>
</head> </head>
<body class="hold-transition skin-blue sidebar-mini <#if cookieMap?exists && "off" == cookieMap["xxljob_adminlte_settings"].value >sidebar-collapse</#if> "> <body class="hold-transition skin-blue sidebar-mini <#if cookieMap?exists && cookieMap["xxljob_adminlte_settings"]?exists && "off" == cookieMap["xxljob_adminlte_settings"].value >sidebar-collapse</#if> ">
<div class="wrapper"> <div class="wrapper">
<!-- header --> <!-- header -->
<@netCommon.commonHeader /> <@netCommon.commonHeader />
<!-- left --> <!-- left -->
<@netCommon.commonLeft "help" /> <@netCommon.commonLeft "help" />
<!-- Content Wrapper. Contains page content --> <!-- Content Wrapper. Contains page content -->
<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>${I18n.job_help}</h1> <h1>${I18n.job_help}</h1>
</section> </section>
<!-- Main content --> <!-- Main content -->
<section class="content"> <section class="content">
<div class="callout callout-info"> <div class="callout callout-info">
<h4>${I18n.admin_name_full}</h4> <h4>${I18n.admin_name_full}</h4>
<br> <br>
<p> <p>
<a target="_blank" href="https://github.com/xuxueli/xxl-job">Github</a>&nbsp;&nbsp;&nbsp;&nbsp; <a target="_blank" href="https://github.com/xuxueli/xxl-job">Github</a>&nbsp;&nbsp;&nbsp;&nbsp;
<iframe src="https://ghbtns.com/github-btn.html?user=xuxueli&repo=xxl-job&type=star&count=true" frameborder="0" scrolling="0" width="170px" height="20px" style="margin-bottom:-5px;"></iframe> <iframe src="https://ghbtns.com/github-btn.html?user=xuxueli&repo=xxl-job&type=star&count=true" frameborder="0" scrolling="0" width="170px" height="20px" style="margin-bottom:-5px;"></iframe>
<br><br> <br><br>
<a target="_blank" href="http://www.xuxueli.com/xxl-job/">${I18n.job_help_document}</a> <a target="_blank" href="http://www.xuxueli.com/xxl-job/">${I18n.job_help_document}</a>
<br><br> <br><br>
</p> </p>
<p></p> <p></p>
</div> </div>
</section> </section>
<!-- /.content --> <!-- /.content -->
</div> </div>
<!-- /.content-wrapper --> <!-- /.content-wrapper -->
<!-- footer --> <!-- footer -->
<@netCommon.commonFooter /> <@netCommon.commonFooter />
</div> </div>
<@netCommon.commonScript /> <@netCommon.commonScript />
</body> </body>
</html> </html>
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<#import "/common/common.macro.ftl" as netCommon> <#import "./common/common.macro.ftl" as netCommon>
<@netCommon.commonStyle /> <@netCommon.commonStyle />
<!-- daterangepicker --> <!-- daterangepicker -->
<link rel="stylesheet" href="${request.contextPath}/static/adminlte/plugins/daterangepicker/daterangepicker.css"> <link rel="stylesheet" href="${request.contextPath}/static/adminlte/plugins/daterangepicker/daterangepicker.css">
<title>${I18n.admin_name}</title> <title>${I18n.admin_name}</title>
</head> </head>
<body class="hold-transition skin-blue sidebar-mini <#if cookieMap?exists && "off" == cookieMap["xxljob_adminlte_settings"].value >sidebar-collapse</#if> "> <body class="hold-transition skin-blue sidebar-mini <#if cookieMap?exists && cookieMap["xxljob_adminlte_settings"]?exists && "off" == cookieMap["xxljob_adminlte_settings"].value >sidebar-collapse</#if> ">
<div class="wrapper"> <div class="wrapper">
<!-- header --> <!-- header -->
<@netCommon.commonHeader /> <@netCommon.commonHeader />
......
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<#import "/common/common.macro.ftl" as netCommon> <#import "../common/common.macro.ftl" as netCommon>
<@netCommon.commonStyle /> <@netCommon.commonStyle />
<link rel="stylesheet" href="${request.contextPath}/static/plugins/codemirror/lib/codemirror.css"> <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"> <link rel="stylesheet" href="${request.contextPath}/static/plugins/codemirror/addon/hint/show-hint.css">
<title>${I18n.admin_name}</title> <title>${I18n.admin_name}</title>
<style type="text/css"> <style type="text/css">
.CodeMirror { .CodeMirror {
font-size:16px; font-size:16px;
width: 100%; width: 100%;
height: 100%; height: 100%;
/*bottom: 0; /*bottom: 0;
top: 0px;*/ top: 0px;*/
position: absolute; position: absolute;
} }
</style> </style>
</head> </head>
<body class="skin-blue fixed layout-top-nav"> <body class="skin-blue fixed layout-top-nav">
<div class="wrapper"> <div class="wrapper">
<header class="main-header"> <header class="main-header">
<nav class="navbar navbar-static-top"> <nav class="navbar navbar-static-top">
<div class="container"> <div class="container">
<#-- icon --> <#-- icon -->
<div class="navbar-header"> <div class="navbar-header">
<a class="navbar-brand"><b>Web</b>IDE</a> <a class="navbar-brand"><b>Web</b>IDE</a>
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-collapse"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-collapse">
<i class="fa fa-bars"></i> <i class="fa fa-bars"></i>
</button> </button>
</div> </div>
<#-- left nav --> <#-- left nav -->
<div class="collapse navbar-collapse pull-left" id="navbar-collapse"> <div class="collapse navbar-collapse pull-left" id="navbar-collapse">
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">
<li class="active" ><a href="javascript:;"> <li class="active" ><a href="javascript:;">
<span class="sr-only">(current)</span> <span class="sr-only">(current)</span>
<#list GlueTypeEnum as item><#if item == jobInfo.glueType>${item.desc}</#if></#list> <#list GlueTypeEnum as item><#if item == jobInfo.glueType>${item.desc}</#if></#list>
${jobInfo.jobDesc} ${jobInfo.jobDesc}
</a></li> </a></li>
</ul> </ul>
</div> </div>
<#-- right nav --> <#-- right nav -->
<div class="navbar-custom-menu"> <div class="navbar-custom-menu">
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">
<li class="dropdown"> <li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false">${I18n.jobinfo_glue_rollback} <span class="caret"></span></a> <a href="#" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false">${I18n.jobinfo_glue_rollback} <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu"> <ul class="dropdown-menu" role="menu">
<li <#if jobLogGlues?exists && jobLogGlues?size gt 0 >style="display: none;"</#if> > <li <#if jobLogGlues?exists && jobLogGlues?size gt 0 >style="display: none;"</#if> >
<a href="javascript:;" class="source_version" version="version_now" glueType="${jobInfo.glueType}" > <a href="javascript:;" class="source_version" version="version_now" glueType="${jobInfo.glueType}" >
<#list GlueTypeEnum as item><#if item == jobInfo.glueType>${item.desc}</#if></#list>: ${jobInfo.glueRemark} <#list GlueTypeEnum as item><#if item == jobInfo.glueType>${item.desc}</#if></#list>: ${jobInfo.glueRemark}
</a> </a>
</li> </li>
<textarea id="version_now" style="display:none;" >${jobInfo.glueSource}</textarea> <textarea id="version_now" style="display:none;" >${jobInfo.glueSource}</textarea>
<#if jobLogGlues?exists && jobLogGlues?size gt 0 > <#if jobLogGlues?exists && jobLogGlues?size gt 0 >
<#list jobLogGlues as glue> <#list jobLogGlues as glue>
<li> <li>
<a href="javascript:;" class="source_version" version="version_${glue.id}" glueType="${glue.glueType}" > <a href="javascript:;" class="source_version" version="version_${glue.id}" glueType="${glue.glueType}" >
<#list GlueTypeEnum as item><#if item == glue.glueType>${item.desc}</#if></#list>: ${glue.glueRemark} <#list GlueTypeEnum as item><#if item == glue.glueType>${item.desc}</#if></#list>: ${glue.glueRemark}
</a> </a>
</li> </li>
<textarea id="version_${glue.id}" style="display:none;" >${glue.glueSource}</textarea> <textarea id="version_${glue.id}" style="display:none;" >${glue.glueSource}</textarea>
</#list> </#list>
</#if> </#if>
</ul> </ul>
</li> </li>
<li id="save" > <li id="save" >
<a href="javascript:;" > <a href="javascript:;" >
<i class="fa fa-fw fa-save" ></i> <i class="fa fa-fw fa-save" ></i>
${I18n.system_save} ${I18n.system_save}
</a> </a>
</li> </li>
</ul> </ul>
</div> </div>
</div> </div>
</nav> </nav>
</header> </header>
<div class="content-wrapper" id="ideWindow" ></div> <div class="content-wrapper" id="ideWindow" ></div>
<!-- footer --> <!-- footer -->
<#--<@netCommon.commonFooter />--> <#--<@netCommon.commonFooter />-->
</div> </div>
<!-- 保存.模态框 --> <!-- 保存.模态框 -->
<div class="modal fade" id="saveModal" tabindex="-1" role="dialog" aria-hidden="true"> <div class="modal fade" id="saveModal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog "> <div class="modal-dialog ">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h4 class="modal-title" ><i class="fa fa-fw fa-save"></i>${I18n.system_save}</h4> <h4 class="modal-title" ><i class="fa fa-fw fa-save"></i>${I18n.system_save}</h4>
</div> </div>
<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="lastname" class="col-sm-2 control-label">${I18n.jobinfo_glue_remark}<font color="red">*</font></label> <label for="lastname" class="col-sm-2 control-label">${I18n.jobinfo_glue_remark}<font color="red">*</font></label>
<div class="col-sm-10"><input type="text" class="form-control" id="glueRemark" placeholder="${I18n.system_please_input}${I18n.jobinfo_glue_remark}" maxlength="64" ></div> <div class="col-sm-10"><input type="text" class="form-control" id="glueRemark" placeholder="${I18n.system_please_input}${I18n.jobinfo_glue_remark}" maxlength="64" ></div>
</div> </div>
<hr> <hr>
<div class="form-group"> <div class="form-group">
<div class="col-sm-offset-3 col-sm-6"> <div class="col-sm-offset-3 col-sm-6">
<button type="button" class="btn btn-primary ok" >${I18n.system_save}</button> <button type="button" class="btn btn-primary ok" >${I18n.system_save}</button>
<button type="button" class="btn btn-default" data-dismiss="modal">${I18n.system_cancel}</button> <button type="button" class="btn btn-default" data-dismiss="modal">${I18n.system_cancel}</button>
</div> </div>
</div> </div>
</form> </form>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<@netCommon.commonScript /> <@netCommon.commonScript />
<#assign glueTypeModeSrc = "${request.contextPath}/static/plugins/codemirror/mode/clike/clike.js" /> <#assign glueTypeModeSrc = "${request.contextPath}/static/plugins/codemirror/mode/clike/clike.js" />
<#assign glueTypeIdeMode = "text/x-java" /> <#assign glueTypeIdeMode = "text/x-java" />
<#if jobInfo.glueType == "GLUE_GROOVY" > <#if jobInfo.glueType == "GLUE_GROOVY" >
<#assign glueTypeModeSrc = "${request.contextPath}/static/plugins/codemirror/mode/clike/clike.js" /> <#assign glueTypeModeSrc = "${request.contextPath}/static/plugins/codemirror/mode/clike/clike.js" />
<#assign glueTypeIdeMode = "text/x-java" /> <#assign glueTypeIdeMode = "text/x-java" />
<#elseif jobInfo.glueType == "GLUE_SHELL" > <#elseif jobInfo.glueType == "GLUE_SHELL" >
<#assign glueTypeModeSrc = "${request.contextPath}/static/plugins/codemirror/mode/shell/shell.js" /> <#assign glueTypeModeSrc = "${request.contextPath}/static/plugins/codemirror/mode/shell/shell.js" />
<#assign glueTypeIdeMode = "text/x-sh" /> <#assign glueTypeIdeMode = "text/x-sh" />
<#elseif jobInfo.glueType == "GLUE_PYTHON" > <#elseif jobInfo.glueType == "GLUE_PYTHON" >
<#assign glueTypeModeSrc = "${request.contextPath}/static/plugins/codemirror/mode/python/python.js" /> <#assign glueTypeModeSrc = "${request.contextPath}/static/plugins/codemirror/mode/python/python.js" />
<#assign glueTypeIdeMode = "text/x-python" /> <#assign glueTypeIdeMode = "text/x-python" />
<#elseif jobInfo.glueType == "GLUE_PHP" > <#elseif jobInfo.glueType == "GLUE_PHP" >
<#assign glueTypeModeSrc = "${request.contextPath}/static/plugins/codemirror/mode/php/php.js" /> <#assign glueTypeModeSrc = "${request.contextPath}/static/plugins/codemirror/mode/php/php.js" />
<#assign glueTypeIdeMode = "text/x-php" /> <#assign glueTypeIdeMode = "text/x-php" />
<#assign glueTypeModeSrc02 = "${request.contextPath}/static/plugins/codemirror/mode/clike/clike.js" /> <#assign glueTypeModeSrc02 = "${request.contextPath}/static/plugins/codemirror/mode/clike/clike.js" />
<#elseif jobInfo.glueType == "GLUE_NODEJS" > <#elseif jobInfo.glueType == "GLUE_NODEJS" >
<#assign glueTypeModeSrc = "${request.contextPath}/static/plugins/codemirror/mode/javascript/javascript.js" /> <#assign glueTypeModeSrc = "${request.contextPath}/static/plugins/codemirror/mode/javascript/javascript.js" />
<#assign glueTypeIdeMode = "text/javascript" /> <#assign glueTypeIdeMode = "text/javascript" />
<#elseif jobInfo.glueType == "GLUE_POWERSHELL" > <#elseif jobInfo.glueType == "GLUE_POWERSHELL" >
<#assign glueTypeModeSrc = "${request.contextPath}/static/plugins/codemirror/mode/powershell/powershell.js" /> <#assign glueTypeModeSrc = "${request.contextPath}/static/plugins/codemirror/mode/powershell/powershell.js" />
<#assign glueTypeIdeMode = "powershell" /> <#assign glueTypeIdeMode = "powershell" />
</#if> </#if>
<script src="${request.contextPath}/static/plugins/codemirror/lib/codemirror.js"></script> <script src="${request.contextPath}/static/plugins/codemirror/lib/codemirror.js"></script>
<script src="${glueTypeModeSrc}"></script> <script src="${glueTypeModeSrc}"></script>
<#if glueTypeModeSrc02?exists> <#if glueTypeModeSrc02?exists>
<script src="${glueTypeModeSrc02}"></script> <script src="${glueTypeModeSrc02}"></script>
</#if> </#if>
<script src="${request.contextPath}/static/plugins/codemirror/addon/hint/show-hint.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/plugins/codemirror/addon/hint/anyword-hint.js"></script>
<script> <script>
var id = '${jobInfo.id}'; var id = '${jobInfo.id}';
var ideMode = '${glueTypeIdeMode}'; var ideMode = '${glueTypeIdeMode}';
</script> </script>
<script src="${request.contextPath}/static/js/jobcode.index.1.js"></script> <script src="${request.contextPath}/static/js/jobcode.index.1.js"></script>
</body> </body>
</html> </html>
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<#import "/common/common.macro.ftl" as netCommon> <#import "../common/common.macro.ftl" as netCommon>
<@netCommon.commonStyle /> <@netCommon.commonStyle />
<!-- DataTables --> <!-- DataTables -->
<link rel="stylesheet" href="${request.contextPath}/static/adminlte/plugins/datatables/dataTables.bootstrap.css"> <link rel="stylesheet" href="${request.contextPath}/static/adminlte/plugins/datatables/dataTables.bootstrap.css">
<title>${I18n.admin_name}</title> <title>${I18n.admin_name}</title>
</head> </head>
<body class="hold-transition skin-blue sidebar-mini <#if cookieMap?exists && "off" == cookieMap["xxljob_adminlte_settings"].value >sidebar-collapse</#if> "> <body class="hold-transition skin-blue sidebar-mini <#if cookieMap?exists && cookieMap["xxljob_adminlte_settings"]?exists && "off" == cookieMap["xxljob_adminlte_settings"].value >sidebar-collapse</#if> ">
<div class="wrapper"> <div class="wrapper">
<!-- header --> <!-- header -->
<@netCommon.commonHeader /> <@netCommon.commonHeader />
...@@ -74,7 +74,7 @@ ...@@ -74,7 +74,7 @@
title="${group.title}" title="${group.title}"
order="${group.order}" order="${group.order}"
addressType="${group.addressType}" addressType="${group.addressType}"
addressList="${group.addressList}" >${I18n.system_opt_edit}</button> addressList="${group.addressList!}" >${I18n.system_opt_edit}</button>
<button class="btn btn-danger btn-xs remove" id="${group.id}" >${I18n.system_opt_del}</button> <button class="btn btn-danger btn-xs remove" id="${group.id}" >${I18n.system_opt_del}</button>
</td> </td>
</tr> </tr>
......
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<#import "/common/common.macro.ftl" as netCommon> <#import "../common/common.macro.ftl" as netCommon>
<@netCommon.commonStyle /> <@netCommon.commonStyle />
<!-- DataTables --> <!-- DataTables -->
<link rel="stylesheet" href="${request.contextPath}/static/adminlte/plugins/datatables/dataTables.bootstrap.css"> <link rel="stylesheet" href="${request.contextPath}/static/adminlte/plugins/datatables/dataTables.bootstrap.css">
<title>${I18n.admin_name}</title> <title>${I18n.admin_name}</title>
</head> </head>
<body class="hold-transition skin-blue sidebar-mini <#if cookieMap?exists && "off" == cookieMap["xxljob_adminlte_settings"].value >sidebar-collapse</#if>"> <body class="hold-transition skin-blue sidebar-mini <#if cookieMap?exists && cookieMap["xxljob_adminlte_settings"]?exists && "off" == cookieMap["xxljob_adminlte_settings"].value >sidebar-collapse</#if>">
<div class="wrapper"> <div class="wrapper">
<!-- header --> <!-- header -->
<@netCommon.commonHeader /> <@netCommon.commonHeader />
......
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<#import "/common/common.macro.ftl" as netCommon> <#import "../common/common.macro.ftl" as netCommon>
<@netCommon.commonStyle /> <@netCommon.commonStyle />
<title>${I18n.admin_name}</title> <title>${I18n.admin_name}</title>
</head> </head>
...@@ -64,7 +64,7 @@ ...@@ -64,7 +64,7 @@
var triggerCode = '${triggerCode}'; var triggerCode = '${triggerCode}';
var handleCode = '${handleCode}'; var handleCode = '${handleCode}';
var executorAddress = '${executorAddress}'; var executorAddress = '${executorAddress}';
var triggerTime = '${triggerTime}'; var triggerTime = '${triggerTime?c}';
var logId = '${logId}'; var logId = '${logId}';
</script> </script>
<script src="${request.contextPath}/static/js/joblog.detail.1.js"></script> <script src="${request.contextPath}/static/js/joblog.detail.1.js"></script>
......
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<#import "/common/common.macro.ftl" as netCommon> <#import "../common/common.macro.ftl" as netCommon>
<@netCommon.commonStyle /> <@netCommon.commonStyle />
<!-- DataTables --> <!-- DataTables -->
<link rel="stylesheet" href="${request.contextPath}/static/adminlte/plugins/datatables/dataTables.bootstrap.css"> <link rel="stylesheet" href="${request.contextPath}/static/adminlte/plugins/datatables/dataTables.bootstrap.css">
<!-- daterangepicker --> <!-- daterangepicker -->
<link rel="stylesheet" href="${request.contextPath}/static/adminlte/plugins/daterangepicker/daterangepicker.css"> <link rel="stylesheet" href="${request.contextPath}/static/adminlte/plugins/daterangepicker/daterangepicker.css">
<title>${I18n.admin_name}</title> <title>${I18n.admin_name}</title>
</head> </head>
<body class="hold-transition skin-blue sidebar-mini <#if cookieMap?exists && "off" == cookieMap["xxljob_adminlte_settings"].value >sidebar-collapse</#if> "> <body class="hold-transition skin-blue sidebar-mini <#if cookieMap?exists && cookieMap["xxljob_adminlte_settings"]?exists && "off" == cookieMap["xxljob_adminlte_settings"].value >sidebar-collapse</#if> ">
<div class="wrapper"> <div class="wrapper">
<!-- header --> <!-- header -->
<@netCommon.commonHeader /> <@netCommon.commonHeader />
<!-- left --> <!-- left -->
<@netCommon.commonLeft "joblog" /> <@netCommon.commonLeft "joblog" />
<!-- Content Wrapper. Contains page content --> <!-- Content Wrapper. Contains page content -->
<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>${I18n.joblog_name}</h1> <h1>${I18n.joblog_name}</h1>
</section> </section>
<!-- Main content --> <!-- Main content -->
<section class="content"> <section class="content">
<div class="row"> <div class="row">
<div class="col-xs-2"> <div class="col-xs-2">
<div class="input-group"> <div class="input-group">
<span class="input-group-addon">${I18n.jobinfo_field_jobgroup}</span> <span class="input-group-addon">${I18n.jobinfo_field_jobgroup}</span>
<select class="form-control" id="jobGroup" paramVal="<#if jobInfo?exists>${jobInfo.jobGroup}</#if>" > <select class="form-control" id="jobGroup" paramVal="<#if jobInfo?exists>${jobInfo.jobGroup}</#if>" >
<option value="0" >${I18n.system_all}</option> <option value="0" >${I18n.system_all}</option>
<#list JobGroupList as group> <#list JobGroupList as group>
<option value="${group.id}" >${group.title}</option> <option value="${group.id}" >${group.title}</option>
</#list> </#list>
</select> </select>
</div> </div>
</div> </div>
<div class="col-xs-2"> <div class="col-xs-2">
<div class="input-group"> <div class="input-group">
<span class="input-group-addon">${I18n.jobinfo_job}</span> <span class="input-group-addon">${I18n.jobinfo_job}</span>
<select class="form-control" id="jobId" paramVal="<#if jobInfo?exists>${jobInfo.id}</#if>" > <select class="form-control" id="jobId" paramVal="<#if jobInfo?exists>${jobInfo.id}</#if>" >
<option value="0" >${I18n.system_all}</option> <option value="0" >${I18n.system_all}</option>
</select> </select>
</div> </div>
</div> </div>
<div class="col-xs-2"> <div class="col-xs-2">
<div class="input-group"> <div class="input-group">
<span class="input-group-addon">${I18n.joblog_status}</span> <span class="input-group-addon">${I18n.joblog_status}</span>
<select class="form-control" id="logStatus" > <select class="form-control" id="logStatus" >
<option value="-1" >${I18n.joblog_status_all}</option> <option value="-1" >${I18n.joblog_status_all}</option>
<option value="1" >${I18n.joblog_status_suc}</option> <option value="1" >${I18n.joblog_status_suc}</option>
<option value="2" >${I18n.joblog_status_fail}</option> <option value="2" >${I18n.joblog_status_fail}</option>
<option value="3" >${I18n.joblog_status_running}</option> <option value="3" >${I18n.joblog_status_running}</option>
</select> </select>
</div> </div>
</div> </div>
<div class="col-xs-4"> <div class="col-xs-4">
<div class="input-group"> <div class="input-group">
<span class="input-group-addon"> <span class="input-group-addon">
${I18n.joblog_field_triggerTime} ${I18n.joblog_field_triggerTime}
</span> </span>
<input type="text" class="form-control" id="filterTime" readonly > <input type="text" class="form-control" id="filterTime" readonly >
</div> </div>
</div> </div>
<div class="col-xs-1"> <div class="col-xs-1">
<button class="btn btn-block btn-info" id="searchBtn">${I18n.system_search}</button> <button class="btn btn-block btn-info" id="searchBtn">${I18n.system_search}</button>
</div> </div>
<div class="col-xs-1"> <div class="col-xs-1">
<button class="btn btn-block btn-nomal" id="clearLog">${I18n.joblog_clean}</button> <button class="btn btn-block btn-nomal" id="clearLog">${I18n.joblog_clean}</button>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-xs-12"> <div class="col-xs-12">
<div class="box"> <div class="box">
<#--<div class="box-header hide"><h3 class="box-title">调度日志</h3></div>--> <#--<div class="box-header hide"><h3 class="box-title">调度日志</h3></div>-->
<div class="box-body"> <div class="box-body">
<table id="joblog_list" class="table table-bordered table-striped display" width="100%" > <table id="joblog_list" class="table table-bordered table-striped display" width="100%" >
<thead> <thead>
<tr> <tr>
<th name="jobId" >${I18n.jobinfo_field_id}</th> <th name="jobId" >${I18n.jobinfo_field_id}</th>
<th name="jobGroup" >jobGroup</th> <th name="jobGroup" >jobGroup</th>
<#--<th name="executorAddress" >执行器地址</th> <#--<th name="executorAddress" >执行器地址</th>
<th name="glueType" >运行模式</th> <th name="glueType" >运行模式</th>
<th name="executorParam" >任务参数</th>--> <th name="executorParam" >任务参数</th>-->
<th name="triggerTime" >${I18n.joblog_field_triggerTime}</th> <th name="triggerTime" >${I18n.joblog_field_triggerTime}</th>
<th name="triggerCode" >${I18n.joblog_field_triggerCode}</th> <th name="triggerCode" >${I18n.joblog_field_triggerCode}</th>
<th name="triggerMsg" >${I18n.joblog_field_triggerMsg}</th> <th name="triggerMsg" >${I18n.joblog_field_triggerMsg}</th>
<th name="handleTime" >${I18n.joblog_field_handleTime}</th> <th name="handleTime" >${I18n.joblog_field_handleTime}</th>
<th name="handleCode" >${I18n.joblog_field_handleCode}</th> <th name="handleCode" >${I18n.joblog_field_handleCode}</th>
<th name="handleMsg" >${I18n.joblog_field_handleMsg}</th> <th name="handleMsg" >${I18n.joblog_field_handleMsg}</th>
<th name="handleMsg" >${I18n.system_opt}</th> <th name="handleMsg" >${I18n.system_opt}</th>
</tr> </tr>
</thead> </thead>
<tbody></tbody> <tbody></tbody>
</table> </table>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</section> </section>
</div> </div>
<!-- footer --> <!-- footer -->
<@netCommon.commonFooter /> <@netCommon.commonFooter />
</div> </div>
<!-- 日志清理.模态框 --> <!-- 日志清理.模态框 -->
<div class="modal fade" id="clearLogModal" tabindex="-1" role="dialog" aria-hidden="true"> <div class="modal fade" id="clearLogModal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h4 class="modal-title" >${I18n.joblog_clean_log}</h4> <h4 class="modal-title" >${I18n.joblog_clean_log}</h4>
</div> </div>
<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 class="col-sm-3 control-label"">${I18n.jobinfo_field_jobgroup}:</label> <label class="col-sm-3 control-label"">${I18n.jobinfo_field_jobgroup}:</label>
<div class="col-sm-9"> <div class="col-sm-9">
<input type="text" class="form-control jobGroupText" readonly > <input type="text" class="form-control jobGroupText" readonly >
<input type="hidden" name="jobGroup" > <input type="hidden" name="jobGroup" >
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="col-sm-3 control-label"">${I18n.jobinfo_job}:</label> <label class="col-sm-3 control-label"">${I18n.jobinfo_job}:</label>
<div class="col-sm-9"> <div class="col-sm-9">
<input type="text" class="form-control jobIdText" readonly > <input type="text" class="form-control jobIdText" readonly >
<input type="hidden" name="jobId" > <input type="hidden" name="jobId" >
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="col-sm-3 control-label"">${I18n.joblog_clean_type}:</label> <label class="col-sm-3 control-label"">${I18n.joblog_clean_type}:</label>
<div class="col-sm-9"> <div class="col-sm-9">
<select class="form-control" name="type" > <select class="form-control" name="type" >
<option value="1" >${I18n.joblog_clean_type_1}</option> <option value="1" >${I18n.joblog_clean_type_1}</option>
<option value="2" >${I18n.joblog_clean_type_2}</option> <option value="2" >${I18n.joblog_clean_type_2}</option>
<option value="3" >${I18n.joblog_clean_type_3}</option> <option value="3" >${I18n.joblog_clean_type_3}</option>
<option value="4" >${I18n.joblog_clean_type_4}</option> <option value="4" >${I18n.joblog_clean_type_4}</option>
<option value="5" >${I18n.joblog_clean_type_5}</option> <option value="5" >${I18n.joblog_clean_type_5}</option>
<option value="6" >${I18n.joblog_clean_type_6}</option> <option value="6" >${I18n.joblog_clean_type_6}</option>
<option value="7" >${I18n.joblog_clean_type_7}</option> <option value="7" >${I18n.joblog_clean_type_7}</option>
<option value="8" >${I18n.joblog_clean_type_8}</option> <option value="8" >${I18n.joblog_clean_type_8}</option>
<option value="9" >${I18n.joblog_clean_type_9}</option> <option value="9" >${I18n.joblog_clean_type_9}</option>
</select> </select>
</div> </div>
</div> </div>
<hr> <hr>
<div class="form-group"> <div class="form-group">
<div class="col-sm-offset-3 col-sm-6"> <div class="col-sm-offset-3 col-sm-6">
<button type="button" class="btn btn-primary ok" >${I18n.system_ok}</button> <button type="button" class="btn btn-primary ok" >${I18n.system_ok}</button>
<button type="button" class="btn btn-default" data-dismiss="modal">${I18n.system_cancel}</button> <button type="button" class="btn btn-default" data-dismiss="modal">${I18n.system_cancel}</button>
</div> </div>
</div> </div>
</form> </form>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<@netCommon.commonScript /> <@netCommon.commonScript />
<!-- DataTables --> <!-- DataTables -->
<script src="${request.contextPath}/static/adminlte/plugins/datatables/jquery.dataTables.min.js"></script> <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/adminlte/plugins/datatables/dataTables.bootstrap.min.js"></script>
<!-- daterangepicker --> <!-- daterangepicker -->
<script src="${request.contextPath}/static/adminlte/plugins/daterangepicker/moment.min.js"></script> <script src="${request.contextPath}/static/adminlte/plugins/daterangepicker/moment.min.js"></script>
<script src="${request.contextPath}/static/adminlte/plugins/daterangepicker/daterangepicker.js"></script> <script src="${request.contextPath}/static/adminlte/plugins/daterangepicker/daterangepicker.js"></script>
<script src="${request.contextPath}/static/js/joblog.index.1.js"></script> <script src="${request.contextPath}/static/js/joblog.index.1.js"></script>
</body> </body>
</html> </html>
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<#import "/common/common.macro.ftl" as netCommon> <#import "./common/common.macro.ftl" as netCommon>
<@netCommon.commonStyle /> <@netCommon.commonStyle />
<link rel="stylesheet" href="${request.contextPath}/static/adminlte/plugins/iCheck/square/blue.css"> <link rel="stylesheet" href="${request.contextPath}/static/adminlte/plugins/iCheck/square/blue.css">
<title>${I18n.admin_name}</title> <title>${I18n.admin_name}</title>
......
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Error</title>
<style type="text/css">
body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
div.dialog {
width: 80%;
padding: 1em 4em;
margin: 4em auto 0 auto;
border: 1px solid #ccc;
border-right-color: #999;
border-bottom-color: #999;
}
h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
</style>
</head>
</head>
<body>
<div class="dialog">
<h1>System Error</h1>
<p>Oops! Page not found.</p>
<a href="javascript:window.location.href='${request.contextPath}/'">Back</a>
</div>
</body>
</html>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
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>
<param-value>classpath*:spring/applicationcontext-*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring/springmvc-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
<error-page>
<error-code>404</error-code>
<location>/500.html</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/500.html</location>
</error-page>
</web-app>
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论