/*
* Copyright (c) 2015 EMC Corporation
* All Rights Reserved
*/
package com.emc.sa.engine;
import java.util.List;
import com.emc.storageos.db.client.model.uimodels.ExecutionTaskLog;
import com.emc.vipr.client.Task;
import com.emc.vipr.client.Tasks;
import com.emc.vipr.client.exceptions.TimeoutException;
/**
* Wrapper around a ViPR Tasks that allow for monitoring progress and updating the execution log on success/failure.
*
* @param <T>
* the result type of the ViPR Tasks
*/
public class ViPRTasksMonitor<T> {
private final ExecutionContext context;
private final ExecutionTaskLog log;
private final Tasks<T> tasks;
/** Whether all tasks have completed. */
private boolean complete;
/** The results of the tasks. */
private List<T> values;
/** The error if the tasks failed. */
private ExecutionException error;
public ViPRTasksMonitor(ExecutionContext context, ExecutionTaskLog log, Tasks<T> tasks) {
this.context = context;
this.log = log;
this.tasks = tasks;
}
/**
* Waits until the tasks complete. This is equivalent to {@code waitFor(-1)}.
*/
public void waitFor() {
waitFor(-1);
}
/**
* Waits until the tasks complete or the specified timeout (millis), whichever is first.
*
* @param timeout
* the maximum time to wait.
* @return whether the tasks completed or not.
*/
public boolean waitFor(long timeout) {
if (!complete) {
try {
values = tasks.get(timeout);
complete = true;
context.updateTaskLog(log, elapsedTime());
} catch (TimeoutException e) {
// Ignore
} catch (Exception e) {
error = new ExecutionException(e);
complete = true;
context.updateTaskLog(log, elapsedTime(), e);
}
}
return complete;
}
/**
* Checks the tasks for complete. If all tasks have already completed this will return right away, otherwise the
* tasks are refreshed and checked for complete.
*
* @return whether the tasks are completed.
*/
public boolean check() {
if (!complete) {
try {
refreshTasks();
complete = isTasksComplete();
if (complete) {
values = tasks.get();
}
context.updateTaskLog(log, elapsedTime());
} catch (TimeoutException e) {
// Ignore
} catch (Exception e) {
error = new ExecutionException(e);
complete = true;
context.updateTaskLog(log, elapsedTime(), e);
}
}
return complete;
}
/**
* Refreshes all tasks.
*/
private void refreshTasks() {
for (Task<T> task : tasks.getTasks()) {
task.refresh();
}
}
/**
* Whether all tasks are complete.
*
* @return true if all tasks are complete.
*/
private boolean isTasksComplete() {
for (Task<T> task : tasks.getTasks()) {
if (!task.isComplete()) {
return false;
}
}
return true;
}
/**
* Determines if the task is complete (success or failure).
*
* @return whether the task is complete.
*/
public boolean isComplete() {
return complete;
}
/**
* Determines if the tasks completed successfully.
*
* @return whether the tasks completed successfully.
*/
public boolean isSuccess() {
return complete && (error == null);
}
/**
* Determines if the tasks completed with an error.
*
* @return whether the tasks completed with an error.
*/
public boolean isError() {
return error != null;
}
/**
* Gets the underlying ViPR tasks.
*
* @return the tasks.
*/
public Tasks<T> getTasks() {
return tasks;
}
/**
* Gets the error wrapping the thrown error (if the task completed in error).
*
* @return the error.
*/
public ExecutionException getError() {
return error;
}
/**
* Waits for the tasks to complete and returns the results of the tasks. If the tasks failed the error is thrown.
*
* @return the results of the tasks.
*
* @throws ExecutionException
* if the tasks completed with an error.
*/
public List<T> getValues() {
waitFor();
if (error != null) {
throw error;
}
return values;
}
/**
* Gets the elapsed time of the tasks.
*
* @return the elapsed time.
*/
public long elapsedTime() {
return endTime() - startTime();
}
/**
* Gets the start time of the first task.
*
* @return the start time of the first task.
*/
private long startTime() {
long start = log.getDate().getTime();
for (Task<?> task : tasks.getTasks()) {
if (task.getStartTime() != null) {
start = Math.min(start, task.getStartTime().getTimeInMillis());
}
}
return start;
}
/**
* Gets the end time of the latest finished task or the current time if no tasks are yet finished.
*
* @return the end time of the latest finished task.
*/
private long endTime() {
long endTime = System.currentTimeMillis();
Task<T> finishedTask = tasks.latestFinishedTask();
if (finishedTask != null && finishedTask.getEndTime() != null) {
endTime = finishedTask.getEndTime().getTimeInMillis();
}
return endTime;
}
}