/* * Copyright 2001-2011 Terracotta, Inc. * * 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.quartz; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Properties; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.junit.Assert; import org.junit.Test; import org.quartz.impl.StdSchedulerFactory; import org.quartz.listeners.JobListenerSupport; import static org.hamcrest.number.OrderingComparison.greaterThanOrEqualTo; import static org.hamcrest.collection.IsCollectionWithSize.hasSize; /** * Integration test for using DisallowConcurrentExecution annot. * * @author Zemian Deng <saltnlight5@gmail.com> */ public class DisallowConcurrentExecutionJobTest { private static final long JOB_BLOCK_TIME = 300L; private static final String BARRIER = "BARRIER"; private static final String DATE_STAMPS = "DATE_STAMPS"; @DisallowConcurrentExecution public static class TestJob implements Job { public void execute(JobExecutionContext context) throws JobExecutionException { try { @SuppressWarnings("unchecked") List<Date> jobExecDates = (List<Date>)context.getScheduler().getContext().get(DATE_STAMPS); long firedAt = System.currentTimeMillis(); jobExecDates.add(new Date(firedAt)); long sleepTill = firedAt + JOB_BLOCK_TIME; for (long sleepFor = sleepTill - System.currentTimeMillis(); sleepFor > 0; sleepFor = sleepTill - System.currentTimeMillis()) { Thread.sleep(sleepFor); } } catch (InterruptedException e) { throw new JobExecutionException("Failed to pause job for testing."); } catch (SchedulerException e) { throw new JobExecutionException("Failed to lookup datestamp collection."); } } } public static class TestJobListener extends JobListenerSupport { private final AtomicInteger jobExCount = new AtomicInteger(0); private final int jobExecutionCountToSyncAfter; public TestJobListener(int jobExecutionCountToSyncAfter) { this.jobExecutionCountToSyncAfter = jobExecutionCountToSyncAfter; } public String getName() { return "TestJobListener"; } @Override public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) { if(jobExCount.incrementAndGet() == jobExecutionCountToSyncAfter) { try { CyclicBarrier barrier = (CyclicBarrier)context.getScheduler().getContext().get(BARRIER); barrier.await(125, TimeUnit.SECONDS); } catch (Throwable e) { e.printStackTrace(); throw new AssertionError("Await on barrier was interrupted: " + e.toString()); } } } } @Test public void testNoConcurrentExecOnSameJob() throws Exception { List<Date> jobExecDates = Collections.synchronizedList(new ArrayList<Date>()); CyclicBarrier barrier = new CyclicBarrier(2); Date startTime = new Date(System.currentTimeMillis() + 100); // make the triggers fire at the same time. JobDetail job1 = JobBuilder.newJob(TestJob.class).withIdentity("job1").build(); Trigger trigger1 = TriggerBuilder.newTrigger().withSchedule(SimpleScheduleBuilder.simpleSchedule()) .startAt(startTime).build(); Trigger trigger2 = TriggerBuilder.newTrigger().withSchedule(SimpleScheduleBuilder.simpleSchedule()) .startAt(startTime).forJob(job1.getKey()).build(); Properties props = new Properties(); props.setProperty("org.quartz.scheduler.idleWaitTime", "1500"); props.setProperty("org.quartz.threadPool.threadCount", "2"); Scheduler scheduler = new StdSchedulerFactory(props).getScheduler(); scheduler.getContext().put(BARRIER, barrier); scheduler.getContext().put(DATE_STAMPS, jobExecDates); scheduler.getListenerManager().addJobListener(new TestJobListener(2)); scheduler.scheduleJob(job1, trigger1); scheduler.scheduleJob(trigger2); scheduler.start(); barrier.await(125, TimeUnit.SECONDS); scheduler.shutdown(true); Assert.assertThat(jobExecDates, hasSize(2)); long fireTimeTrigger1 = jobExecDates.get(0).getTime(); long fireTimeTrigger2 = jobExecDates.get(1).getTime(); Assert.assertThat(fireTimeTrigger2 - fireTimeTrigger1, greaterThanOrEqualTo(JOB_BLOCK_TIME)); } /** QTZ-202 */ @Test public void testNoConcurrentExecOnSameJobWithBatching() throws Exception { List<Date> jobExecDates = Collections.synchronizedList(new ArrayList<Date>()); CyclicBarrier barrier = new CyclicBarrier(2); Date startTime = new Date(System.currentTimeMillis() + 100); // make the triggers fire at the same time. JobDetail job1 = JobBuilder.newJob(TestJob.class).withIdentity("job1").build(); Trigger trigger1 = TriggerBuilder.newTrigger().withSchedule(SimpleScheduleBuilder.simpleSchedule()) .startAt(startTime).build(); Trigger trigger2 = TriggerBuilder.newTrigger().withSchedule(SimpleScheduleBuilder.simpleSchedule()) .startAt(startTime).forJob(job1.getKey()).build(); Properties props = new Properties(); props.setProperty("org.quartz.scheduler.idleWaitTime", "1500"); props.setProperty("org.quartz.scheduler.batchTriggerAcquisitionMaxCount", "2"); props.setProperty("org.quartz.threadPool.threadCount", "2"); Scheduler scheduler = new StdSchedulerFactory(props).getScheduler(); scheduler.getContext().put(BARRIER, barrier); scheduler.getContext().put(DATE_STAMPS, jobExecDates); scheduler.getListenerManager().addJobListener(new TestJobListener(2)); scheduler.scheduleJob(job1, trigger1); scheduler.scheduleJob(trigger2); scheduler.start(); barrier.await(125, TimeUnit.SECONDS); scheduler.shutdown(true); Assert.assertThat(jobExecDates, hasSize(2)); long fireTimeTrigger1 = jobExecDates.get(0).getTime(); long fireTimeTrigger2 = jobExecDates.get(1).getTime(); Assert.assertThat(fireTimeTrigger2 - fireTimeTrigger1, greaterThanOrEqualTo(JOB_BLOCK_TIME)); } }