package org.oddjob.scheduling;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import junit.framework.TestCase;
import org.apache.log4j.Logger;
import org.oddjob.FailedToStopException;
import org.oddjob.Stateful;
import org.oddjob.jobs.WaitJob;
import org.oddjob.state.FlagState;
import org.oddjob.state.JobState;
import org.oddjob.state.StateEvent;
import org.oddjob.state.StateListener;
import org.oddjob.tools.StateSteps;
public class ExecutorServiceThrottleTest extends TestCase {
private static final Logger logger = Logger.getLogger(ExecutorServiceThrottle.class);
@Override
protected void setUp() throws Exception {
super.setUp();
logger.info("--------------- " + getName() + " -----------------");
}
private class ExecutingCounter implements StateListener {
private final List<Stateful[]> exceptions = new ArrayList<Stateful[]>();
private final List<Stateful> executing = new ArrayList<Stateful>();
@Override
public void jobStateChange(StateEvent event) {
synchronized(executing) {
switch ((JobState) event.getState()) {
case COMPLETE:
executing.remove(event.getSource());
break;
case READY:
break;
case EXECUTING:
executing.add(event.getSource());
if (executing.size() > 3) {
exceptions.add(executing.toArray(
new Stateful[executing.size()]));
}
break;
default:
exceptions.add(new Stateful[] { event.getSource() });
}
}
}
}
public void testQuickJobs() throws InterruptedException, ExecutionException {
ExecutorService executorService = Executors.newFixedThreadPool(5);
ExecutingCounter counter = new ExecutingCounter();
ExecutorServiceThrottle throttle = new ExecutorServiceThrottle(
executorService, 3);
FlagState[] jobs = new FlagState[100];
Future<?>[] futures = new Future<?>[100];
for (int i = 0; i < 100; ++i) {
jobs[i] = new FlagState();
jobs[i].setName("Job " + i);
jobs[i].addStateListener(counter);
futures[i] = throttle.submit(jobs[i]);
}
for (int i = 0; i < 100; ++i) {
futures[i].get();
assertEquals(JobState.COMPLETE,
jobs[i].lastStateEvent().getState());
}
assertEquals(0, counter.exceptions.size());
executorService.shutdown();
}
public void testSlowJobs() throws InterruptedException, ExecutionException, FailedToStopException {
ExecutorService executorService = Executors.newFixedThreadPool(5);
ExecutorServiceThrottle throttle = new ExecutorServiceThrottle(
executorService, 2);
WaitJob w1 = new WaitJob();
WaitJob w2 = new WaitJob();
WaitJob w3 = new WaitJob();
WaitJob w4 = new WaitJob();
WaitJob w5 = new WaitJob();
w1.setName("Wait 1");
w2.setName("Wait 2");
w3.setName("Wait 3");
w4.setName("Wait 4");
w5.setName("Wait 5");
StateSteps s1 = new StateSteps(w1);
StateSteps s2 = new StateSteps(w2);
StateSteps s3 = new StateSteps(w3);
StateSteps s4 = new StateSteps(w4);
StateSteps s5 = new StateSteps(w5);
s1.startCheck(JobState.READY, JobState.EXECUTING);
s2.startCheck(JobState.READY, JobState.EXECUTING);
s3.startCheck(JobState.READY, JobState.EXECUTING);
s4.startCheck(JobState.READY, JobState.EXECUTING);
s5.startCheck(JobState.READY, JobState.EXECUTING);
throttle.submit(w1);
throttle.submit(w2);
throttle.submit(w3);
throttle.submit(w4);
throttle.submit(w5);
logger.info("Waiting for w1 and w2 to start.");
s1.checkWait();
s2.checkWait();
assertEquals(JobState.READY, w3.lastStateEvent().getState());
assertEquals(JobState.READY, w4.lastStateEvent().getState());
assertEquals(JobState.READY, w5.lastStateEvent().getState());
w1.stop();
logger.info("Waiting for w3 to start.");
s3.checkWait();
assertEquals(JobState.READY, w4.lastStateEvent().getState());
assertEquals(JobState.READY, w5.lastStateEvent().getState());
w3.stop();
logger.info("Waiting for w4 to start.");
s4.checkWait();
assertEquals(JobState.READY, w5.lastStateEvent().getState());
w4.stop();
logger.info("Waiting for w5 to start.");
s5.checkWait();
w2.stop();
w5.stop();
executorService.shutdown();
}
public void testPendingJobsWhenShutdown() throws InterruptedException, ExecutionException, FailedToStopException {
ExecutorService executorService = Executors.newFixedThreadPool(5);
ExecutorServiceThrottle throttle = new ExecutorServiceThrottle(
executorService, 1);
WaitJob wait1 = new WaitJob();
WaitJob wait2 = new WaitJob();
wait1.setName("Wait 1");
wait2.setName("Wait 2");
StateSteps stateCheck1 = new StateSteps(wait1);
StateSteps stateCheck2 = new StateSteps(wait2);
stateCheck1.startCheck(JobState.READY, JobState.EXECUTING);
stateCheck2.startCheck(JobState.READY, JobState.EXECUTING);
Future<?> future1 = throttle.submit(wait1);
throttle.submit(wait2);
logger.info("Waiting for w1 to start.");
stateCheck1.checkWait();
executorService.shutdown();
stateCheck1.startCheck(JobState.EXECUTING, JobState.COMPLETE);
wait1.stop();
stateCheck1.checkWait();
future1.get();
executorService.awaitTermination(1, TimeUnit.HOURS);
assertTrue(executorService.isTerminated());
assertEquals(JobState.READY, wait2.lastStateEvent().getState());
}
public void testCancelledWork() throws InterruptedException, ExecutionException, FailedToStopException {
ExecutorService executorService = Executors.newFixedThreadPool(5);
ExecutorServiceThrottle throttle = new ExecutorServiceThrottle(
executorService, 1);
WaitJob w1 = new WaitJob();
WaitJob w2 = new WaitJob();
w1.setName("Wait 1");
w2.setName("Wait 2");
StateSteps s1 = new StateSteps(w1);
StateSteps s2 = new StateSteps(w2);
s1.startCheck(JobState.READY, JobState.EXECUTING);
s2.startCheck(JobState.READY, JobState.EXECUTING);
Future<?> f1 = throttle.submit(w1);
Future<?> f2 = throttle.submit(w2);
logger.info("Waiting for w1 to start.");
s1.checkWait();
f2.cancel(false);
s1.startCheck(JobState.EXECUTING, JobState.COMPLETE);
w1.stop();
s1.checkWait();
f1.get();
assertEquals(JobState.READY, w2.lastStateEvent().getState());
executorService.shutdown();
}
}