/* * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.scheduling.quartz; import java.util.HashMap; import java.util.Map; import javax.sql.DataSource; import org.junit.Test; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.quartz.Scheduler; import org.quartz.SchedulerContext; import org.quartz.SchedulerFactory; import org.quartz.impl.JobDetailImpl; import org.quartz.impl.SchedulerRepository; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.context.support.StaticApplicationContext; import org.springframework.core.task.TaskExecutor; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.tests.Assume; import org.springframework.tests.TestGroup; import org.springframework.tests.sample.beans.TestBean; import static org.junit.Assert.*; import static org.mockito.BDDMockito.*; /** * @author Juergen Hoeller * @author Alef Arendsen * @author Rob Harrop * @author Dave Syer * @author Mark Fisher * @author Sam Brannen * @since 20.02.2004 */ public class QuartzSupportTests { @Test public void schedulerFactoryBeanWithApplicationContext() throws Exception { TestBean tb = new TestBean("tb", 99); StaticApplicationContext ac = new StaticApplicationContext(); final Scheduler scheduler = mock(Scheduler.class); SchedulerContext schedulerContext = new SchedulerContext(); given(scheduler.getContext()).willReturn(schedulerContext); SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean() { @Override protected Scheduler createScheduler(SchedulerFactory schedulerFactory, String schedulerName) { return scheduler; } }; schedulerFactoryBean.setJobFactory(null); Map<String, Object> schedulerContextMap = new HashMap<>(); schedulerContextMap.put("testBean", tb); schedulerFactoryBean.setSchedulerContextAsMap(schedulerContextMap); schedulerFactoryBean.setApplicationContext(ac); schedulerFactoryBean.setApplicationContextSchedulerContextKey("appCtx"); try { schedulerFactoryBean.afterPropertiesSet(); schedulerFactoryBean.start(); Scheduler returnedScheduler = schedulerFactoryBean.getObject(); assertEquals(tb, returnedScheduler.getContext().get("testBean")); assertEquals(ac, returnedScheduler.getContext().get("appCtx")); } finally { schedulerFactoryBean.destroy(); } verify(scheduler).start(); verify(scheduler).shutdown(false); } @Test public void schedulerWithTaskExecutor() throws Exception { Assume.group(TestGroup.PERFORMANCE); CountingTaskExecutor taskExecutor = new CountingTaskExecutor(); DummyJob.count = 0; JobDetailImpl jobDetail = new JobDetailImpl(); jobDetail.setDurability(true); jobDetail.setJobClass(DummyJob.class); jobDetail.setName("myJob"); SimpleTriggerFactoryBean trigger = new SimpleTriggerFactoryBean(); trigger.setName("myTrigger"); trigger.setJobDetail(jobDetail); trigger.setStartDelay(1); trigger.setRepeatInterval(500); trigger.setRepeatCount(1); trigger.afterPropertiesSet(); SchedulerFactoryBean bean = new SchedulerFactoryBean(); bean.setTaskExecutor(taskExecutor); bean.setTriggers(trigger.getObject()); bean.setJobDetails(jobDetail); bean.afterPropertiesSet(); bean.start(); Thread.sleep(500); assertTrue("DummyJob should have been executed at least once.", DummyJob.count > 0); assertEquals(DummyJob.count, taskExecutor.count); bean.destroy(); } @Test(expected = IllegalArgumentException.class) @SuppressWarnings({ "unchecked", "rawtypes" }) public void jobDetailWithRunnableInsteadOfJob() { JobDetailImpl jobDetail = new JobDetailImpl(); jobDetail.setJobClass((Class) DummyRunnable.class); } @Test public void schedulerWithQuartzJobBean() throws Exception { Assume.group(TestGroup.PERFORMANCE); DummyJob.param = 0; DummyJob.count = 0; JobDetailImpl jobDetail = new JobDetailImpl(); jobDetail.setDurability(true); jobDetail.setJobClass(DummyJobBean.class); jobDetail.setName("myJob"); jobDetail.getJobDataMap().put("param", "10"); SimpleTriggerFactoryBean trigger = new SimpleTriggerFactoryBean(); trigger.setName("myTrigger"); trigger.setJobDetail(jobDetail); trigger.setStartDelay(1); trigger.setRepeatInterval(500); trigger.setRepeatCount(1); trigger.afterPropertiesSet(); SchedulerFactoryBean bean = new SchedulerFactoryBean(); bean.setTriggers(trigger.getObject()); bean.setJobDetails(jobDetail); bean.afterPropertiesSet(); bean.start(); Thread.sleep(500); assertEquals(10, DummyJobBean.param); assertTrue(DummyJobBean.count > 0); bean.destroy(); } @Test public void schedulerWithSpringBeanJobFactory() throws Exception { Assume.group(TestGroup.PERFORMANCE); DummyJob.param = 0; DummyJob.count = 0; JobDetailImpl jobDetail = new JobDetailImpl(); jobDetail.setDurability(true); jobDetail.setJobClass(DummyJob.class); jobDetail.setName("myJob"); jobDetail.getJobDataMap().put("param", "10"); jobDetail.getJobDataMap().put("ignoredParam", "10"); SimpleTriggerFactoryBean trigger = new SimpleTriggerFactoryBean(); trigger.setName("myTrigger"); trigger.setJobDetail(jobDetail); trigger.setStartDelay(1); trigger.setRepeatInterval(500); trigger.setRepeatCount(1); trigger.afterPropertiesSet(); SchedulerFactoryBean bean = new SchedulerFactoryBean(); bean.setJobFactory(new SpringBeanJobFactory()); bean.setTriggers(trigger.getObject()); bean.setJobDetails(jobDetail); bean.afterPropertiesSet(); bean.start(); Thread.sleep(500); assertEquals(10, DummyJob.param); assertTrue("DummyJob should have been executed at least once.", DummyJob.count > 0); bean.destroy(); } @Test public void schedulerWithSpringBeanJobFactoryAndParamMismatchNotIgnored() throws Exception { Assume.group(TestGroup.PERFORMANCE); DummyJob.param = 0; DummyJob.count = 0; JobDetailImpl jobDetail = new JobDetailImpl(); jobDetail.setDurability(true); jobDetail.setJobClass(DummyJob.class); jobDetail.setName("myJob"); jobDetail.getJobDataMap().put("para", "10"); jobDetail.getJobDataMap().put("ignoredParam", "10"); SimpleTriggerFactoryBean trigger = new SimpleTriggerFactoryBean(); trigger.setName("myTrigger"); trigger.setJobDetail(jobDetail); trigger.setStartDelay(1); trigger.setRepeatInterval(500); trigger.setRepeatCount(1); trigger.afterPropertiesSet(); SchedulerFactoryBean bean = new SchedulerFactoryBean(); SpringBeanJobFactory jobFactory = new SpringBeanJobFactory(); jobFactory.setIgnoredUnknownProperties("ignoredParam"); bean.setJobFactory(jobFactory); bean.setTriggers(trigger.getObject()); bean.setJobDetails(jobDetail); bean.afterPropertiesSet(); Thread.sleep(500); assertEquals(0, DummyJob.param); assertTrue(DummyJob.count == 0); bean.destroy(); } @Test public void schedulerWithSpringBeanJobFactoryAndQuartzJobBean() throws Exception { Assume.group(TestGroup.PERFORMANCE); DummyJobBean.param = 0; DummyJobBean.count = 0; JobDetailImpl jobDetail = new JobDetailImpl(); jobDetail.setDurability(true); jobDetail.setJobClass(DummyJobBean.class); jobDetail.setName("myJob"); jobDetail.getJobDataMap().put("param", "10"); SimpleTriggerFactoryBean trigger = new SimpleTriggerFactoryBean(); trigger.setName("myTrigger"); trigger.setJobDetail(jobDetail); trigger.setStartDelay(1); trigger.setRepeatInterval(500); trigger.setRepeatCount(1); trigger.afterPropertiesSet(); SchedulerFactoryBean bean = new SchedulerFactoryBean(); bean.setJobFactory(new SpringBeanJobFactory()); bean.setTriggers(trigger.getObject()); bean.setJobDetails(jobDetail); bean.afterPropertiesSet(); bean.start(); Thread.sleep(500); assertEquals(10, DummyJobBean.param); assertTrue(DummyJobBean.count > 0); bean.destroy(); } @Test public void schedulerWithSpringBeanJobFactoryAndJobSchedulingData() throws Exception { Assume.group(TestGroup.PERFORMANCE); DummyJob.param = 0; DummyJob.count = 0; SchedulerFactoryBean bean = new SchedulerFactoryBean(); bean.setJobFactory(new SpringBeanJobFactory()); bean.setJobSchedulingDataLocation("org/springframework/scheduling/quartz/job-scheduling-data.xml"); bean.afterPropertiesSet(); bean.start(); Thread.sleep(500); assertEquals(10, DummyJob.param); assertTrue("DummyJob should have been executed at least once.", DummyJob.count > 0); bean.destroy(); } /** * Tests the creation of multiple schedulers (SPR-772) */ @Test public void multipleSchedulers() throws Exception { ClassPathXmlApplicationContext ctx = context("multipleSchedulers.xml"); try { Scheduler scheduler1 = (Scheduler) ctx.getBean("scheduler1"); Scheduler scheduler2 = (Scheduler) ctx.getBean("scheduler2"); assertNotSame(scheduler1, scheduler2); assertEquals("quartz1", scheduler1.getSchedulerName()); assertEquals("quartz2", scheduler2.getSchedulerName()); } finally { ctx.close(); } } @Test public void twoAnonymousMethodInvokingJobDetailFactoryBeans() throws Exception { Assume.group(TestGroup.PERFORMANCE); ClassPathXmlApplicationContext ctx = context("multipleAnonymousMethodInvokingJobDetailFB.xml"); Thread.sleep(3000); try { QuartzTestBean exportService = (QuartzTestBean) ctx.getBean("exportService"); QuartzTestBean importService = (QuartzTestBean) ctx.getBean("importService"); assertEquals("doImport called exportService", 0, exportService.getImportCount()); assertEquals("doExport not called on exportService", 2, exportService.getExportCount()); assertEquals("doImport not called on importService", 2, importService.getImportCount()); assertEquals("doExport called on importService", 0, importService.getExportCount()); } finally { ctx.close(); } } @Test public void schedulerAccessorBean() throws Exception { Assume.group(TestGroup.PERFORMANCE); ClassPathXmlApplicationContext ctx = context("schedulerAccessorBean.xml"); Thread.sleep(3000); try { QuartzTestBean exportService = (QuartzTestBean) ctx.getBean("exportService"); QuartzTestBean importService = (QuartzTestBean) ctx.getBean("importService"); assertEquals("doImport called exportService", 0, exportService.getImportCount()); assertEquals("doExport not called on exportService", 2, exportService.getExportCount()); assertEquals("doImport not called on importService", 2, importService.getImportCount()); assertEquals("doExport called on importService", 0, importService.getExportCount()); } finally { ctx.close(); } } @Test @SuppressWarnings("resource") public void schedulerAutoStartsOnContextRefreshedEventByDefault() throws Exception { StaticApplicationContext context = new StaticApplicationContext(); context.registerBeanDefinition("scheduler", new RootBeanDefinition(SchedulerFactoryBean.class)); Scheduler bean = context.getBean("scheduler", Scheduler.class); assertFalse(bean.isStarted()); context.refresh(); assertTrue(bean.isStarted()); } @Test @SuppressWarnings("resource") public void schedulerAutoStartupFalse() throws Exception { StaticApplicationContext context = new StaticApplicationContext(); BeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition( SchedulerFactoryBean.class).addPropertyValue("autoStartup", false).getBeanDefinition(); context.registerBeanDefinition("scheduler", beanDefinition); Scheduler bean = context.getBean("scheduler", Scheduler.class); assertFalse(bean.isStarted()); context.refresh(); assertFalse(bean.isStarted()); } @Test public void schedulerRepositoryExposure() throws Exception { ClassPathXmlApplicationContext ctx = context("schedulerRepositoryExposure.xml"); assertSame(SchedulerRepository.getInstance().lookup("myScheduler"), ctx.getBean("scheduler")); ctx.close(); } /** * SPR-6038: detect HSQL and stop illegal locks being taken. * TODO: Against Quartz 2.2, this test's job doesn't actually execute anymore... */ @Test public void schedulerWithHsqlDataSource() throws Exception { // Assume.group(TestGroup.PERFORMANCE); DummyJob.param = 0; DummyJob.count = 0; ClassPathXmlApplicationContext ctx = context("databasePersistence.xml"); JdbcTemplate jdbcTemplate = new JdbcTemplate(ctx.getBean(DataSource.class)); assertFalse("No triggers were persisted", jdbcTemplate.queryForList("SELECT * FROM qrtz_triggers").isEmpty()); /* Thread.sleep(3000); try { assertTrue("DummyJob should have been executed at least once.", DummyJob.count > 0); } finally { ctx.close(); } */ } private ClassPathXmlApplicationContext context(String path) { return new ClassPathXmlApplicationContext(path, getClass()); } public static class CountingTaskExecutor implements TaskExecutor { private int count; @Override public void execute(Runnable task) { this.count++; task.run(); } } public static class DummyJob implements Job { private static int param; private static int count; public void setParam(int value) { if (param > 0) { throw new IllegalStateException("Param already set"); } param = value; } @Override public synchronized void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { count++; } } public static class DummyJobBean extends QuartzJobBean { private static int param; private static int count; public void setParam(int value) { if (param > 0) { throw new IllegalStateException("Param already set"); } param = value; } @Override protected synchronized void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException { count++; } } public static class DummyRunnable implements Runnable { @Override public void run() { /* no-op */ } } }