package com.level3.meanwhile.concurrent;
import com.level3.meanwhile.TaskQueue;
import com.level3.meanwhile.Chain;
import com.level3.meanwhile.state.ClaimCheck;
import com.level3.meanwhile.Stage;
import com.level3.meanwhile.Task;
import com.level3.meanwhile.state.TaskStatus;
import java.util.ArrayList;
import java.util.List;
/**
* MeanwhileRunner wraps a Task and manages the Task's execution throughout its life cycle.
*
*
*
* @author Jonathan Griggs <Jonathan.Griggs@Level3.com>
* @since 0.2
*/
public final class MeanwhileRunner<T extends Task> implements Runnable {
/*
* The wrapped Task
*/
private T task;
private ClaimCheck claimCheck;
private TaskQueue manager;
private boolean cancelledInFlight = false;
private List<MeanwhileFuture<T>> futures = new ArrayList<MeanwhileFuture<T>>();
/**
* The default status of a Task is DEQUEUED
* @see TaskStatus
*/
private TaskStatus status = TaskStatus.DEQUEUED;
public MeanwhileRunner(final T task, final TaskQueue manager) {
this.manager = manager;
claimCheck = new ClaimCheck(task.getUUID());
init(task);
}
public MeanwhileRunner(final T task, final TaskQueue manager, final ClaimCheck parentCheck) {
this.manager = manager;
claimCheck = new ClaimCheck(task.getUUID(),parentCheck);
init(task);
}
private void init(T task) {
this.task = task;
status = TaskStatus.QUEUED;
task.onSubmit();
}
public List<MeanwhileFuture<T>> submitChainedTasks() {
List<MeanwhileFuture<T>> chainFutures = new ArrayList<MeanwhileFuture<T>>();
if(task instanceof Chain) {
for(Task nextTask : ((Chain)task).getChainedTasks()) {
chainFutures.add(manager.submit(new MeanwhileRunner(nextTask,manager,claimCheck)));
}
}
return chainFutures;
}
public List<MeanwhileFuture> getStageFutures() {
List<MeanwhileFuture> futures = new ArrayList<MeanwhileFuture>();
if(task instanceof Stage) {
for(Task nextTask : ((Stage)task).getStagedTasks()) {
futures.add(manager.submit(new MeanwhileRunner(nextTask,manager,claimCheck)));
}
}
return futures;
}
/**
* The {@code run()} implementation required to satisfy the Runnable interface.
*
* <p>Calls {@code Task.execute()} and performs logic for determining the success or failure of the Task
*
* @see Runnable
* @since 0.1
*/
public final void run() {
try {
boolean success = true;
do {
if(TaskStatus.RETRY.equals(status) == true) {
task.onRetry();
}
status = TaskStatus.WORKING;
task.onStart();
if(task instanceof Stage) {
for(MeanwhileFuture<Task> future : getStageFutures()) {
future.get();
if(!future.isSuccess()) {
success = false;
}
}
} else {
success = task.execute();
}
if(!success && task.retryOnFail()) {
status = TaskStatus.RETRY;
}
} while (!success && TaskStatus.RETRY.equals(status));
if(success) {
status = TaskStatus.SUCCESS;
task.onSuccess();
} else {
status = TaskStatus.FAILED;
task.onFailure();
}
} catch(Throwable e) {
status = TaskStatus.FAILED;
task.onFailure();
} finally {
task.onComplete();
}
if(TaskStatus.SUCCESS.equals(this.getStatus())&&!cancelledInFlight) {
futures = submitChainedTasks();
} else if(TaskStatus.SUCCESS.equals(this.getStatus())&&cancelledInFlight) {
if(task instanceof Chain) {
for(Task nextTask : ((Chain)task).getChainedTasks()) {
nextTask.onCancel();
}
}
}
}
public boolean cancel() {
boolean canceled = false;
if(TaskStatus.QUEUED.equals(this.getStatus())) {
task.onCancel();
canceled = true;
} else if (TaskStatus.WORKING.equals(this.getStatus())) {
cancelledInFlight = true;
if(task instanceof Chain) {
if(((Chain)task).getChainedTasks().size()>0) {
canceled = true;
}
}
}
return canceled;
}
/**
* @return the task
*/
public Task getTask() {
return task;
}
/**
* @return the status
*/
public TaskStatus getStatus() {
return status;
}
public boolean isSuccess() {
if(!TaskStatus.SUCCESS.equals(status)) {
return false;
} else if (!futures.isEmpty()) {
for(MeanwhileFuture future : futures) {
if(!future.isSuccess()) {
return false;
}
}
}
return true;
}
/**
*
* Gets the ClaimCheck of the wrapped task
*
* @return ClaimCheck representing this Task
* @since 0.1
*/
public ClaimCheck getClaimCheck() {
return claimCheck;
}
/**
* Returns the hashCode of the Task
*
* @see ClaimCheck#hashCode
* @since 0.1
*/
@Override
public int hashCode() {
return claimCheck.hashCode();
}
/**
* Equals depends on the Task's ClaimCheck - in fact, you can pass a ClaimCheck (or UUID obj or UUID String) and it can equal the Task object
*
* @see ClaimCheck#equals
* @since 0.1
*/
@Override
public boolean equals(final Object obj) {
if (this == obj)
return true;
return claimCheck.equals(obj);
}
/**
* @return the futures
*/
public List<MeanwhileFuture<T>> getChainedFutures() {
return futures;
}
}