/*
* Copyright (c) 2004, Rob Gordon.
*/
package org.oddjob.jobs.structural;
import java.util.Iterator;
import org.oddjob.Stateful;
import org.oddjob.Stoppable;
import org.oddjob.arooa.deploy.annotations.ArooaComponent;
import org.oddjob.arooa.deploy.annotations.ArooaElement;
import org.oddjob.framework.StructuralJob;
import org.oddjob.jobs.job.ResetActions;
import org.oddjob.state.AnyActiveStateOp;
import org.oddjob.state.IsStoppable;
import org.oddjob.state.State;
import org.oddjob.state.StateOperator;
import org.oddjob.structural.OddjobChildException;
import org.oddjob.values.types.SequenceIterable;
/**
* @oddjob.description This job will repeatedly run its child job. The repeat
* can be either for:
* <ul>
* <li>Each value of a collection.</li>
* <li>Or a given number times.</li>
* <li>Or until the until property is true.</li>
* </ul>
* <p>
* Without either a until or a times or values the job will loop indefinitely.
*
* @oddjob.example
*
* Repeat a job 3 times.
*
* {@oddjob.xml.resource org/oddjob/jobs/structural/RepeatExample.xml}
*
* @author Rob Gordon.
*
*/
public class RepeatJob extends StructuralJob<Runnable>
implements Stoppable {
private static final long serialVersionUID = 20120121;
/**
* @oddjob.property
* @oddjob.description Repeat will repeat until the value of
* this property is true.
* @oddjob.required No.
*/
private volatile boolean until;
/**
* @oddjob.property
* @oddjob.description The count of repeats.
* @oddjob.required Read Only.
*/
private volatile int count;
/**
* @oddjob.property
* @oddjob.description The number of times to repeat.
* @oddjob.required No.
*/
private volatile int times;
private transient volatile Iterable<?> values;
private transient volatile Iterator<?> iterator;
private transient volatile Object current;
@Override
protected StateOperator getInitialStateOp() {
return new AnyActiveStateOp();
}
/**
* @oddjob.property job
* @oddjob.description The job who's execution
* to schedule.
* @oddjob.required Yes.
*/
@ArooaComponent
public void setJob(Runnable child) {
if (child == null) {
childHelper.removeChildAt(0);
}
else {
if (childHelper.size() > 0) {
throw new IllegalArgumentException("Child Job already set.");
}
childHelper.insertChild(0, child);
}
}
/*
* (non-Javadoc)
* @see org.oddjob.jobs.AbstractJob#execute()
*/
protected void execute() {
Runnable job = childHelper.getChild();
if (job == null) {
return;
}
if (iterator == null) {
if (times > 0) {
iterator = new SequenceIterable(1, times, 1).iterator();
}
else {
if (values == null) {
iterator = null;
}
else {
iterator = values.iterator();
}
}
}
while (!stop && !until && (iterator == null || iterator.hasNext())) {
++count;
if (iterator != null) {
current = iterator.next();
}
ResetActions.AUTO.doWith(job);
try {
job.run();
}
finally {
}
State state = null;
Throwable throwable = null;
if (job instanceof Stateful) {
state = ((Stateful) job).lastStateEvent().getState();
throwable = ((Stateful) job).lastStateEvent().getException();
}
if (state == null) {
continue;
}
if (state.isException()) {
logger().debug("Job [" + job + "] Exception");
throw new OddjobChildException(throwable, job.toString());
}
else if (new IsStoppable().test(state)) {
logger().debug("Job state for [" + job +
"] is: " + state + ", Will not repeat.");
break;
}
} // end while
stop = false;
}
@Override
protected void onHardReset() {
iterator = null;
count = 0;
until = false;
}
public void setValues(Iterable<?> values) {
this.values = values;
}
public Iterable<?> getValues() {
return values;
}
public boolean isUntil() {
return until;
}
@ArooaElement
public void setUntil(boolean until) {
this.until = until;
}
public int getTimes() {
return times;
}
public void setTimes(int times) {
this.times = times;
}
public int getCount() {
return count;
}
/**
* @oddjob.property index
* @oddjob.description The same as count. Provided so configurations
* can be swapped between this and {@link ForEachJob} job.
*
* @return The index.
*/
public int getIndex() {
return count;
}
public Object getCurrent() {
return current;
}
}