package org.oddjob.framework;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Watches the execution of jobs and executes an action when all jobs
* have been executed.
* <p>
* When this watcher is stopped it will check to see if all jobs had
* started executing and if they had it will perform the action. I can't
* remember why this this is required because the action is generally to
* start reflecting child state - and why don't we want to always want to
* do that on stop?
*
* @author rob
*
*/
public class ExecutionWatcher {
/** The action to run. */
private final Runnable allJobsExecutedAction;
/** The number to count to. */
private final AtomicInteger jobsWatchingCount = new AtomicInteger();
/** The number executing. */
private final AtomicInteger jobsExecutingCount = new AtomicInteger();
/** The number executed. */
private final AtomicInteger jobsExecutedCount = new AtomicInteger();
/** Started. */
private volatile boolean started;
/**
* Constructor.
*
* @param action The action to run.
*/
public ExecutionWatcher(Runnable action) {
this.allJobsExecutedAction = action;
}
/**
* Add a job.
*
* @param job
*
* @return The new job to execute.
*/
public Runnable addJob(final Runnable job) {
jobsWatchingCount.incrementAndGet();
return new Runnable() {
@Override
public void run() {
jobsExecutingCount.incrementAndGet();
job.run();
boolean perform;
synchronized (ExecutionWatcher.this) {
jobsExecutingCount.decrementAndGet();
jobsExecutedCount.incrementAndGet();
perform = checkIfAllJobsExecuted();
}
if (perform) {
allJobsExecutedAction.run();
}
}
@Override
public String toString() {
return ExecutionWatcher.class.getSimpleName() +
" for " + job;
}
};
}
/**
* Starts the check.
*/
public void start() {
boolean perform;
synchronized (this) {
started = true;
perform = checkIfAllJobsExecuted();
}
if (perform) {
allJobsExecutedAction.run();
}
}
/**
* Stops the check.
*/
public void stop() {
boolean performAllJobsExecutedAction;
synchronized (this) {
jobsWatchingCount.set(jobsExecutingCount.get() + jobsExecutedCount.get());
performAllJobsExecutedAction = checkIfAllJobsExecuted();
}
if (performAllJobsExecutedAction) {
allJobsExecutedAction.run();
}
}
public void reset() {
synchronized (this) {
jobsWatchingCount.set(0);
jobsExecutingCount.set(0);
jobsExecutedCount.set(0);
started = false;
}
}
/**
* Checks if all jobs have executed.
*
* @return
*/
private boolean checkIfAllJobsExecuted() {
if (started && jobsWatchingCount.get() == jobsExecutedCount.get()) {
return true;
}
else {
return false;
}
}
}