package org.oddjob.jobs;
import java.util.LinkedList;
import org.oddjob.Stateful;
import org.oddjob.Stoppable;
import org.oddjob.arooa.deploy.annotations.ArooaAttribute;
import org.oddjob.framework.SimpleJob;
import org.oddjob.scheduling.ExecutorThrottleType;
import org.oddjob.state.IsAnyState;
import org.oddjob.state.IsStoppable;
import org.oddjob.state.State;
import org.oddjob.state.StateCondition;
import org.oddjob.state.StateEvent;
import org.oddjob.state.StateListener;
/**
* @oddjob.description This Job will either wait a given number of milliseconds
* or will wait for a property or job to become available.
* <p>
* If the for property is provided, then the delay is used as the number of
* milliseconds between checking if the property is available.
*
* @oddjob.example
*
* The {@link ExecutorThrottleType} has a simple example.
*
* @oddjob.example
*
* This example waits for a variable 'text' to be set. The value could be set
* across the network or by a another job running in parallel.
*
* {@oddjob.xml.resource org/oddjob/jobs/WaitForExample.xml}
*
* @author Rob Gordon
*
*/
public class WaitJob extends SimpleJob
implements Stoppable {
private static final long DEFAULT_WAIT_SLEEP = 1000;
/**
* @oddjob.property
* @oddjob.description The wait delay in milliseconds.
* @oddjob.required No if for property is set, otherwise yes.
*/
private volatile long pause;
/**
* @oddjob.property for
* @oddjob.description The property to wait for.
* @oddjob.required No.
*/
private volatile Object forProperty;
private volatile boolean forSet;
/**
* @oddjob.property state
* @oddjob.description A state condition to wait for. When this is
* set this job will wait for the job referenced with the <code>
* for</code> property match the given state condition.
* See the Oddjob User guide for a full list of state conditions.
* @oddjob.required No.
*/
private volatile StateCondition state;
/**
* Set the delay time in milli seconds.
*
* @param delay The delay time.
*/
public void setPause(long delay) {
this.pause = delay;
}
/**
* Get the delay time in milli seconds.
*
* @return The delay time.
*/
public long getPause() {
return pause;
}
/*
* (non-Javadoc)
* @see org.oddjob.jobs.AbstractJob#execute()
*/
public int execute() throws Exception {
if (state != null) {
if (forProperty == null) {
throw new IllegalStateException("'for' property must set.");
}
if (!(forProperty instanceof Stateful)) {
throw new IllegalStateException("'for' property must Stateful.");
}
waitForState();
}
else if (forSet) {
logger().debug("Waiting for property.");
waitFor();
}
else {
simpleWait();
}
return 0;
}
protected void simpleWait() {
sleep(pause);
}
protected void waitFor() {
long sleep = pause;
if (sleep == 0) {
sleep = DEFAULT_WAIT_SLEEP;
}
while (!stop) {
stateHandler().waitToWhen(new IsStoppable(), new Runnable() {
public void run() {
configure();
}
});
if (forProperty != null) {
break;
}
sleep(sleep);
}
}
protected void waitForState() {
final long waitBetweenChecks;
if (pause == 0) {
waitBetweenChecks = DEFAULT_WAIT_SLEEP;
}
else {
waitBetweenChecks = pause;
}
final LinkedList<State> states = new LinkedList<State>();
StateListener listener = new StateListener() {
public void jobStateChange(StateEvent event) {
synchronized (states) {
states.add(event.getState());
stateHandler().waitToWhen(new IsAnyState(), new Runnable() {
public void run() {
stateHandler().wake();
}
});
}
}
};
((Stateful) forProperty).addStateListener(listener);
while (!stop) {
State now = null;
synchronized (states) {
if (!states.isEmpty()) {
now = states.removeFirst();
logger().debug("State received "+ now);
}
}
if (now != null && state.test(now)) {
logger().debug("State matches " + state);
break;
}
logger().debug("Waiting for state to match " + state);
sleep(waitBetweenChecks);
}
((Stateful) forProperty).removeStateListener(listener);
}
public Object getFor() {
return forProperty;
}
@ArooaAttribute
public void setFor(Object forProperty) {
this.forProperty = forProperty;
this.forSet = true;
}
public StateCondition getState() {
return state;
}
@ArooaAttribute
public void setState(StateCondition state) {
this.state = state;
}
}