diff --git a/examples/showcase/src/main/java/org/springside/examples/showcase/modules/schedule/JdkExecutorJob.java b/examples/showcase/src/main/java/org/springside/examples/showcase/modules/schedule/JdkTimerJob.java
similarity index 65%
rename from examples/showcase/src/main/java/org/springside/examples/showcase/modules/schedule/JdkExecutorJob.java
rename to examples/showcase/src/main/java/org/springside/examples/showcase/modules/schedule/JdkTimerJob.java
index 366dfb53cf50a28df10a234c912afa9b02d26251..a743a839d99ca32eed4d408fa9800076f922688b 100644
--- a/examples/showcase/src/main/java/org/springside/examples/showcase/modules/schedule/JdkExecutorJob.java
+++ b/examples/showcase/src/main/java/org/springside/examples/showcase/modules/schedule/JdkTimerJob.java
@@ -8,22 +8,17 @@ import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.apache.commons.lang3.Validate;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.scheduling.support.DelegatingErrorHandlingRunnable;
import org.springframework.scheduling.support.TaskUtils;
-import org.springside.examples.showcase.service.AccountService;
import org.springside.modules.utils.Threads;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
/**
- * 被ScheduledThreadPoolExecutor定时执行的任务类, 并强化了退出控制.
+ * 用JDKScheduledThreadPoolExecutor定时执行的任务。
+ * 相比Spring的NameSpace, 不需要反射調用,并强化了退出控制.
*/
-public class JdkExecutorJob implements Runnable {
-
- private static Logger logger = LoggerFactory.getLogger(JdkExecutorJob.class);
+public class JdkTimerJob implements Runnable {
private int initialDelay = 0;
@@ -33,17 +28,19 @@ public class JdkExecutorJob implements Runnable {
private ScheduledExecutorService scheduledExecutorService;
- private AccountService accountService;
+ @Autowired
+ private UserCountScanner userCountScanner;
@PostConstruct
public void start() throws Exception {
Validate.isTrue(period > 0);
- //任何异常不会中断schedule执行
- Runnable task = new DelegatingErrorHandlingRunnable(this, TaskUtils.LOG_AND_SUPPRESS_ERROR_HANDLER);
+ //任何异常不会中断schedule执行, 由Spring TaskUtils的LOG_AND_SUPPRESS_ERROR_HANDLER進行处理
+ Runnable task = TaskUtils.decorateTaskWithErrorHandler(this, null, true);
+ //创建单线程的SechdulerExecutor,并用guava的ThreadFactoryBuilder设定生成线程的名称
scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryBuilder().setNameFormat(
- "JdkExecutorJob-%1$d").build());
+ "JdkTimerJob-%1$d").build());
//scheduleAtFixedRatefixRate() 固定任务两次启动之间的时间间隔.
//scheduleAtFixedDelay() 固定任务结束后到下一次启动间的时间间隔.
@@ -60,8 +57,7 @@ public class JdkExecutorJob implements Runnable {
*/
@Override
public void run() {
- long userCount = accountService.getUserCount();
- logger.info("There are {} user in database, printed by jdk timer job.", userCount);
+ userCountScanner.executeByJdk();
}
/**
@@ -79,14 +75,9 @@ public class JdkExecutorJob implements Runnable {
}
/**
- * 设置gracefulShutdown的等待时间,单位秒.
+ * 设置normalShutdown的等待时间, 单位秒.
*/
public void setShutdownTimeout(int shutdownTimeout) {
this.shutdownTimeout = shutdownTimeout;
}
-
- @Autowired
- public void setAccountService(AccountService accountService) {
- this.accountService = accountService;
- }
}
diff --git a/examples/showcase/src/main/java/org/springside/examples/showcase/modules/schedule/QuartzJob.java b/examples/showcase/src/main/java/org/springside/examples/showcase/modules/schedule/QuartzJob.java
deleted file mode 100644
index d5c7c58b3ae83f223ac7691136eaaf545babfabc..0000000000000000000000000000000000000000
--- a/examples/showcase/src/main/java/org/springside/examples/showcase/modules/schedule/QuartzJob.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package org.springside.examples.showcase.modules.schedule;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springside.examples.showcase.service.AccountService;
-
-/**
- * 被Spring的Quartz MethodInvokingJobDetailFactoryBean定时执行的普通Spring Bean.
- */
-public class QuartzJob {
-
- private static Logger logger = LoggerFactory.getLogger(QuartzJob.class);
-
- @Autowired
- private AccountService accountService;
-
- /**
- * 定时打印当前用户数到日志.
- */
- public void execute() {
- long userCount = accountService.getUserCount();
- logger.info("There are {} user in database, printed by quartz local job.", userCount);
- }
-}
diff --git a/examples/showcase/src/main/java/org/springside/examples/showcase/modules/schedule/SpringCronJob.java b/examples/showcase/src/main/java/org/springside/examples/showcase/modules/schedule/SpringCronJob.java
index 2dbfa5f9956ca60c4824f5e3259ad36c4f4ebaa1..583a3b221ffeaae9303777b1533e037e1271547b 100644
--- a/examples/showcase/src/main/java/org/springside/examples/showcase/modules/schedule/SpringCronJob.java
+++ b/examples/showcase/src/main/java/org/springside/examples/showcase/modules/schedule/SpringCronJob.java
@@ -7,28 +7,25 @@ import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.apache.commons.lang3.Validate;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
-import org.springside.examples.showcase.service.AccountService;
import org.springside.modules.utils.Threads;
/**
- * 使用Spring的ThreadPoolTaskScheduler执行Cron式任务的类, 并强化了退出控制.
+ * 使用Spring的ThreadPoolTaskScheduler执行Cron式任务的类.
+ * 相比Spring的NameSpace, 不需要反射調用,并强化了退出控制.
*/
public class SpringCronJob implements Runnable {
- private static Logger logger = LoggerFactory.getLogger(SpringCronJob.class);
-
private String cronExpression;
private int shutdownTimeout = Integer.MAX_VALUE;
private ThreadPoolTaskScheduler threadPoolTaskScheduler;
- private AccountService accountService;
+ @Autowired
+ private UserCountScanner userCountScanner;
@PostConstruct
public void start() {
@@ -44,9 +41,7 @@ public class SpringCronJob implements Runnable {
@PreDestroy
public void stop() {
ScheduledExecutorService scheduledExecutorService = threadPoolTaskScheduler.getScheduledExecutor();
-
Threads.normalShutdown(scheduledExecutorService, shutdownTimeout, TimeUnit.SECONDS);
-
}
/**
@@ -54,8 +49,7 @@ public class SpringCronJob implements Runnable {
*/
@Override
public void run() {
- long userCount = accountService.getUserCount();
- logger.info("There are {} user in database, printed by spring cron job.", userCount);
+ userCountScanner.executeBySpringCronByJava();
}
public void setCronExpression(String cronExpression) {
@@ -63,14 +57,9 @@ public class SpringCronJob implements Runnable {
}
/**
- * 设置gracefulShutdown的等待时间,单位秒.
+ * 设置normalShutdown的等待时间,单位秒.
*/
public void setShutdownTimeout(int shutdownTimeout) {
this.shutdownTimeout = shutdownTimeout;
}
-
- @Autowired
- public void setAccountService(AccountService accountService) {
- this.accountService = accountService;
- }
}
diff --git a/examples/showcase/src/main/java/org/springside/examples/showcase/modules/schedule/UserCountScanner.java b/examples/showcase/src/main/java/org/springside/examples/showcase/modules/schedule/UserCountScanner.java
new file mode 100644
index 0000000000000000000000000000000000000000..555a061d30ac4b2725ee7fb307fc033f3c1ef799
--- /dev/null
+++ b/examples/showcase/src/main/java/org/springside/examples/showcase/modules/schedule/UserCountScanner.java
@@ -0,0 +1,52 @@
+package org.springside.examples.showcase.modules.schedule;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springside.examples.showcase.service.AccountService;
+
+/**
+ * 被Spring各种Scheduler反射调用的Service POJO
+ *
+ * @author Calvin
+ */
+@Component
+public class UserCountScanner {
+
+ private static Logger logger = LoggerFactory.getLogger(UserCountScanner.class);
+
+ @Autowired
+ private AccountService accountService;
+
+ public void executeByJdk() {
+ execute("jdk timer job");
+ }
+
+ public void executeBySpringCronByJava() {
+ execute("spring cron job by java");
+ }
+
+ //被Spring的Quartz MethodInvokingJobDetailFactoryBean反射执行
+ public void executeByQuartzLocalJob() {
+ execute("quartz local job");
+ }
+
+ //被Spring的Scheduler namespace 反射构造成ScheduledMethodRunnable
+ public void executeBySpringCronByXml() {
+ execute("spring cron job by xml");
+ }
+
+ //被Spring的Scheduler namespace 反射构造成ScheduledMethodRunnable
+ public void executeBySpringTimerByXml() {
+ execute("spring timer job by xml");
+ }
+
+ /**
+ * 定时打印当前用户数到日志.
+ */
+ private void execute(String by) {
+ long userCount = accountService.getUserCount();
+ logger.info("There are {} user in database, printed by {}.", userCount, by);
+ }
+}
diff --git a/examples/showcase/src/main/resources/applicationContext-showcases.xml b/examples/showcase/src/main/resources/applicationContext-showcases.xml
index 6767c1ad3727858f7cd23077f2389aa3593535b8..624021968d839e80d4d02aa3e10109dafce7d010 100644
--- a/examples/showcase/src/main/resources/applicationContext-showcases.xml
+++ b/examples/showcase/src/main/resources/applicationContext-showcases.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/examples/showcase/src/main/resources/schedule/applicationContext-jdk-timer.xml b/examples/showcase/src/main/resources/schedule/applicationContext-jdk-timer.xml
index d6cbd7a1a93ae614e49920999fdabe0ba42ba4df..588aca9692045ce16ce321b801733c6331edfcba 100644
--- a/examples/showcase/src/main/resources/schedule/applicationContext-jdk-timer.xml
+++ b/examples/showcase/src/main/resources/schedule/applicationContext-jdk-timer.xml
@@ -6,7 +6,7 @@
使用JDK ScheduledExecutorService的定时任务配置
-
+
diff --git a/examples/showcase/src/main/resources/schedule/applicationContext-quartz-cron-local.xml b/examples/showcase/src/main/resources/schedule/applicationContext-quartz-cron-local.xml
index feec548a8ccde2f31703f8420d59f589693010c7..0c756404d16d9571be85ed7ec8af31f6dff7f86c 100644
--- a/examples/showcase/src/main/resources/schedule/applicationContext-quartz-cron-local.xml
+++ b/examples/showcase/src/main/resources/schedule/applicationContext-quartz-cron-local.xml
@@ -13,7 +13,7 @@
-
+
5
@@ -30,14 +30,12 @@
-
+
-
-
+
+
-
-
diff --git a/examples/showcase/src/main/resources/schedule/applicationContext-quartz-timer-cluster.xml b/examples/showcase/src/main/resources/schedule/applicationContext-quartz-timer-cluster.xml
index 40de0b2789f65a2fd18fbf656d1527741cac20b4..ea94ad0eeb48fc7d73540cfd8eb142afcb0fccc3 100644
--- a/examples/showcase/src/main/resources/schedule/applicationContext-quartz-timer-cluster.xml
+++ b/examples/showcase/src/main/resources/schedule/applicationContext-quartz-timer-cluster.xml
@@ -15,7 +15,7 @@
-
+
@@ -32,12 +32,12 @@
-
+
-
+
diff --git a/examples/showcase/src/main/resources/schedule/applicationContext-spring-cron.xml b/examples/showcase/src/main/resources/schedule/applicationContext-spring-cron.xml
deleted file mode 100644
index ba5f4568156364256e352db80fe4805c940113af..0000000000000000000000000000000000000000
--- a/examples/showcase/src/main/resources/schedule/applicationContext-spring-cron.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
- 使用Spring的 Scheduled的定时任务配置
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/examples/showcase/src/main/resources/schedule/applicationContext-spring-scheduler.xml b/examples/showcase/src/main/resources/schedule/applicationContext-spring-scheduler.xml
new file mode 100644
index 0000000000000000000000000000000000000000..3f328835827809a29e25e3b2cab79b468c4210d8
--- /dev/null
+++ b/examples/showcase/src/main/resources/schedule/applicationContext-spring-scheduler.xml
@@ -0,0 +1,25 @@
+
+
+
+ 使用Spring的 Scheduled的定时任务配置
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/showcase/src/main/resources/schedule/quartz-cluster.properties b/examples/showcase/src/main/resources/schedule/quartz-cluster.properties
index af9352b559baa3b2455750e8987b5b66c89681bc..7356e54027ba0e7bdf58fd927b9a4ab321be6dd4 100644
--- a/examples/showcase/src/main/resources/schedule/quartz-cluster.properties
+++ b/examples/showcase/src/main/resources/schedule/quartz-cluster.properties
@@ -21,5 +21,6 @@ org.quartz.jobStore.misfireThreshold = 60000
org.quartz.jobStore.useProperties = false
org.quartz.jobStore.tablePrefix = QRTZ_
+#Cluster setting
org.quartz.jobStore.isClustered = true
org.quartz.jobStore.clusterCheckinInterval = 15000
\ No newline at end of file
diff --git a/examples/showcase/src/test/java/org/springside/examples/showcase/modules/schedule/JdkTimerJobTest.java b/examples/showcase/src/test/java/org/springside/examples/showcase/modules/schedule/JdkTimerJobTest.java
index df2ceb234cc2b5ddcf7ddd5ad5fc52848f92c830..0dc69073096e61766b35884214229b94a5321ab5 100644
--- a/examples/showcase/src/test/java/org/springside/examples/showcase/modules/schedule/JdkTimerJobTest.java
+++ b/examples/showcase/src/test/java/org/springside/examples/showcase/modules/schedule/JdkTimerJobTest.java
@@ -23,7 +23,7 @@ public class JdkTimerJobTest extends SpringTransactionalTestCase {
//加载测试用logger appender
Log4jMockAppender appender = new Log4jMockAppender();
- appender.addToLogger(JdkExecutorJob.class);
+ appender.addToLogger(UserCountScanner.class);
//等待任务启动
Threads.sleep(3000);
@@ -31,5 +31,7 @@ public class JdkTimerJobTest extends SpringTransactionalTestCase {
//验证任务已执行
assertEquals(1, appender.getLogsCount());
assertEquals("There are 6 user in database, printed by jdk timer job.", appender.getFirstMessage());
+
+ appender.removeFromLogger(UserCountScanner.class);
}
}
diff --git a/examples/showcase/src/test/java/org/springside/examples/showcase/modules/schedule/QuartzTimerClusterJobTest.java b/examples/showcase/src/test/java/org/springside/examples/showcase/modules/schedule/QuartzTimerClusterJobTest.java
index edf35068295c86d83827d8f986015045b03f6c5a..eb4d790fbcb0c783ede9a0411fd09492079eba71 100644
--- a/examples/showcase/src/test/java/org/springside/examples/showcase/modules/schedule/QuartzTimerClusterJobTest.java
+++ b/examples/showcase/src/test/java/org/springside/examples/showcase/modules/schedule/QuartzTimerClusterJobTest.java
@@ -38,5 +38,7 @@ public class QuartzTimerClusterJobTest extends SpringTransactionalTestCase {
assertEquals("There are 6 user in database, printed by quartz cluster job on node default.",
appender.getFirstMessage());
+
+ appender.removeFromLogger(QuartzClusterableJob.class);
}
}
diff --git a/examples/showcase/src/test/java/org/springside/examples/showcase/modules/schedule/SpringTimerJobTest.java b/examples/showcase/src/test/java/org/springside/examples/showcase/modules/schedule/SpringTimerJobTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..f96ce5b59f48df68e44c70d3fbb7858f10b9c478
--- /dev/null
+++ b/examples/showcase/src/test/java/org/springside/examples/showcase/modules/schedule/SpringTimerJobTest.java
@@ -0,0 +1,36 @@
+package org.springside.examples.showcase.modules.schedule;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.transaction.TransactionConfiguration;
+import org.springside.modules.test.category.UnStable;
+import org.springside.modules.test.log.Log4jMockAppender;
+import org.springside.modules.test.spring.SpringTransactionalTestCase;
+import org.springside.modules.utils.Threads;
+
+@Category(UnStable.class)
+@DirtiesContext
+@ContextConfiguration(locations = { "/applicationContext.xml", "/schedule/applicationContext-spring-scheduler.xml" })
+@TransactionConfiguration(transactionManager = "defaultTransactionManager")
+public class SpringTimerJobTest extends SpringTransactionalTestCase {
+
+ @Test
+ public void scheduleJob() throws Exception {
+
+ //加载测试用logger appender
+ Log4jMockAppender appender = new Log4jMockAppender();
+ appender.addToLogger(UserCountScanner.class);
+
+ //等待任务启动
+ Threads.sleep(1000);
+
+ //验证任务已执行
+ assertEquals(1, appender.getLogsCount());
+ assertEquals("There are 6 user in database, printed by spring timer job by xml.", appender.getFirstMessage());
+ appender.removeFromLogger(UserCountScanner.class);
+ }
+}
diff --git a/modules/core/src/main/java/org/springside/modules/utils/Reflections.java b/modules/core/src/main/java/org/springside/modules/utils/Reflections.java
index 8718e77458b3c739adb2cafd0f05944ca4781ab4..fb77a40498f8e057b324a208518d11c831439f99 100644
--- a/modules/core/src/main/java/org/springside/modules/utils/Reflections.java
+++ b/modules/core/src/main/java/org/springside/modules/utils/Reflections.java
@@ -199,7 +199,7 @@ public class Reflections {
}
/**
- // * 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
+ * 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
*/
public static void makeAccessible(Field field) {
if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) || Modifier
diff --git a/modules/core/src/main/java/org/springside/modules/utils/Threads.java b/modules/core/src/main/java/org/springside/modules/utils/Threads.java
index 3d8d07bcb810808fd726c8270cd133524d8acf42..89d00c7a4d9d064da0919ff4b41deb3c09ea650b 100644
--- a/modules/core/src/main/java/org/springside/modules/utils/Threads.java
+++ b/modules/core/src/main/java/org/springside/modules/utils/Threads.java
@@ -41,6 +41,7 @@ public class Threads {
* 按照ExecutorService JavaDoc示例代码编写的Graceful Shutdown方法.
* 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务.
* 如果超时, 则调用shutdownNow, 取消在workQueue中Pending的任务,并中断所有阻塞函数.
+ * 如果仍人超時,則強制退出.
* 另对在shutdown时线程本身被调用中断做了处理.
*/
public static void gracefulShutdown(ExecutorService pool, int shutdownTimeout, int shutdownNowTimeout,
@@ -64,7 +65,7 @@ public class Threads {
}
/**
- * 直接调用shutdownNow的方法, 取消在workQueue中Pending的任务,并中断所有阻塞函数.
+ * 直接调用shutdownNow的方法, 有timeout控制.取消在workQueue中Pending的任务,并中断所有阻塞函数.
*/
public static void normalShutdown(ExecutorService pool, int timeout, TimeUnit timeUnit) {
try {