package net.joelinn.quartz;
import com.google.common.base.Strings;
import net.jodah.concurrentunit.Waiter;
import net.joelinn.quartz.jobstore.RedisJobStore;
import org.junit.After;
import org.junit.Before;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.simpl.PropertySettingJobFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.util.Pool;
import redis.embedded.RedisServer;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicInteger;
import static net.joelinn.quartz.TestUtils.getPort;
/**
* @author Joe Linn
* 12/4/2016
*/
public abstract class BaseIntegrationTest {
private static final Logger log = LoggerFactory.getLogger(BaseIntegrationTest.class);
protected RedisServer redisServer;
protected Scheduler scheduler;
protected Pool<Jedis> jedisPool;
protected int port;
protected static final String HOST = "localhost";
@Before
public void setUp() throws Exception {
port = getPort();
redisServer = RedisServer.builder()
.port(port)
.build();
redisServer.start();
jedisPool = new JedisPool(HOST, port);
scheduler = new StdSchedulerFactory(schedulerConfig(HOST, port)).getScheduler();
scheduler.start();
}
protected Properties schedulerConfig(String host, int port) {
Properties config = new Properties();
config.setProperty(StdSchedulerFactory.PROP_JOB_STORE_CLASS, RedisJobStore.class.getName());
config.setProperty("org.quartz.jobStore.host", host);
config.setProperty("org.quartz.jobStore.port", String.valueOf(port));
config.setProperty("org.quartz.threadPool.threadCount", "1");
config.setProperty("org.quartz.jobStore.misfireThreshold", "500");
return config;
}
@After
public void tearDown() throws Exception {
scheduler.shutdown();
if (jedisPool != null) {
jedisPool.close();
}
redisServer.stop();
}
public static class DataJob implements Job {
protected Pool<Jedis> jedisPool;
public void setJedisPool(Pool<Jedis> jedisPool) {
this.jedisPool = jedisPool;
}
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
String foo = context.getTrigger().getJobDataMap().getString("foo");
if (!Strings.isNullOrEmpty(foo)) {
try (Jedis jedis = jedisPool.getResource()) {
jedis.set("foo", foo);
}
} else {
log.error("Null or empty string retrieved from Redis.");
}
}
}
public static class SleepJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
throw new JobExecutionException("Interrupted while sleeping.", e);
}
}
}
@DisallowConcurrentExecution
public static class SingletonSleepJob extends SleepJob {
public static final AtomicInteger currentlyExecuting = new AtomicInteger(0);
public static final AtomicInteger concurrentExecutions = new AtomicInteger(0);
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
log.info("Starting job: " + context.getJobDetail().getKey() + " due to trigger " + context.getTrigger().getKey());
if (currentlyExecuting.incrementAndGet() > 1) {
log.error("Concurrent execution detected!!");
concurrentExecutions.incrementAndGet();
throw new JobExecutionException("Concurrent execution not allowed!");
}
try {
Thread.sleep(1000); // add some extra sleep time to ensure that concurrent execution will be attempted
} catch (InterruptedException e) {
throw new JobExecutionException("Interrupted while sleeping.", e);
}
super.execute(context);
currentlyExecuting.decrementAndGet();
}
}
protected class CompleteListener implements TriggerListener {
private final Waiter waiter;
protected CompleteListener(Waiter waiter) {
this.waiter = waiter;
}
@Override
public String getName() {
return "Inigo Montoya";
}
@Override
public void triggerFired(Trigger trigger, JobExecutionContext context) {
}
@Override
public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) {
return false;
}
@Override
public void triggerMisfired(Trigger trigger) {
}
@Override
public void triggerComplete(Trigger trigger, JobExecutionContext context, Trigger.CompletedExecutionInstruction triggerInstructionCode) {
waiter.resume();
}
}
protected class MisfireListener implements TriggerListener {
private final Waiter waiter;
protected MisfireListener(Waiter waiter) {
this.waiter = waiter;
}
@Override
public String getName() {
return "Rugen";
}
@Override
public void triggerFired(Trigger trigger, JobExecutionContext context) {
}
@Override
public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) {
return false;
}
@Override
public void triggerMisfired(Trigger trigger) {
waiter.resume();
}
@Override
public void triggerComplete(Trigger trigger, JobExecutionContext context, Trigger.CompletedExecutionInstruction triggerInstructionCode) {
}
}
protected class RedisJobFactory extends PropertySettingJobFactory {
@Override
protected void setBeanProps(Object obj, JobDataMap data) throws SchedulerException {
data.put("jedisPool", jedisPool);
super.setBeanProps(obj, data);
}
}
}