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 {