/*
* Copyright (c) 2015 EMC Corporation
* All Rights Reserved
*/
package com.emc.vipr.client;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.model.TaskResourceRep;
import com.emc.vipr.client.core.impl.TaskUtil;
import com.emc.vipr.client.exceptions.ViPRException;
import com.emc.vipr.client.impl.RestClient;
/**
* Representation of multiple asynchronous tasks returned from an operation.
*
* @param <R> Type of the underlying resource running the operation.
*/
public class Tasks<R> {
private final static Logger log = LoggerFactory.getLogger(Tasks.class);
private final int tasksExecutionTerminationSeconds;
private ExecutorService taskExecutor;
private List<Task<R>> tasks;
public Tasks(RestClient client, List<TaskResourceRep> tasks, Class<? extends R> resourceClass) {
this.tasks = new ArrayList<>();
if (tasks != null) {
for (TaskResourceRep task : tasks) {
this.tasks.add(new Task<>(client, task, resourceClass));
}
}
taskExecutor = Executors.newFixedThreadPool(client.getConfig().getMaxConcurrentTaskRequests());
tasksExecutionTerminationSeconds = client.getConfig().getTasksExecutionTimeoutSeconds();
}
/**
* Retrieves all tasks associated with the operation.
*
* @return List of tasks.
*/
public List<Task<R>> getTasks() {
return tasks;
}
/**
* Retrieves the first task in the list.
*
* @return The first task or null if there are no tasks in the list.
*/
public Task<R> firstTask() {
if (!tasks.isEmpty()) {
return tasks.get(0);
}
return null;
}
/**
* Retrieves the latest task in the list that has finished processing.
*
* @return The latest task or null if there are no tasks in the list.
*/
public Task<R> latestFinishedTask() {
if (!tasks.isEmpty()) {
Collections.sort(tasks, new LatestFinishedTaskComparator());
Task<R> latestTask = tasks.get(0);
if (latestTask.getEndTime() != null) {
return latestTask;
}
}
return null;
}
/**
* Waits for tasks to complete (go into a pending or error state). If an error occurs
* it will be thrown as an exception.
*
* @throws ViPRException Thrown if any task is in an error state.
* @return This tasks.
*/
public Tasks<R> waitFor() throws ViPRException {
return waitFor(-1);
}
/**
* Waits for tasks to complete (go into a pending or error state). If an error occurs
* it will be thrown as an exception.
*
* @param timeoutMillis Timeout after a number of milliseconds
* @throws com.emc.vipr.client.exceptions.TimeoutException Thrown if a timeout occurs.
* @throws ViPRException Thrown if any task is in an error state.
* @return This tasks.
*/
public Tasks<R> waitFor(final long timeoutMillis) throws ViPRException {
final CountDownLatch countdown = new CountDownLatch(tasks.size());
final List<TaskResourceRep> taskImpls = new ArrayList<>();
for (final Task<R> task : tasks) {
taskExecutor.execute(new Runnable() {
@Override
public void run() {
try {
task.doTaskWait(timeoutMillis);
taskImpls.add(task.getTaskResource());
} finally {
countdown.countDown();
}
}
});
}
try {
countdown.await();
taskExecutor.shutdown();
} catch (InterruptedException e) {
log.error(e.getMessage(), e);
}
TaskUtil.checkForErrors(taskImpls);
return this;
}
/**
* Waits for tasks to complete (go into a pending or error state). If an error occurs
* it will be thrown as an exception. If any task was successful this returns the
* actual object for the underlying resource.
*
* @throws com.emc.vipr.client.exceptions.TimeoutException Thrown if a timeout occurs.
* @throws ViPRException Thrown if any task is in an error state.
* @return List of resource objects.
*/
public List<R> get() {
waitFor();
return doGetResources();
}
/**
* Waits for tasks to complete (go into a pending or error state). If an error occurs
* it will be thrown as an exception. If any task was successful this returns the
* actual object for the underlying resource.
*
* @param timeoutMillis Timeout after a number of milliseconds
* @throws com.emc.vipr.client.exceptions.TimeoutException Thrown if a timeout occurs.
* @throws ViPRException Thrown if any task is in an error state.
* @return List of resource objects.
*/
public List<R> get(long timeoutMillis) {
waitFor(timeoutMillis);
return doGetResources();
}
private List<R> doGetResources() {
List<R> resources = new ArrayList<>();
for (Task<R> task : tasks) {
resources.add(task.doGetResource());
}
return resources;
}
protected static class LatestFinishedTaskComparator implements Comparator<Task> {
public int compare(Task task1, Task task2) {
if (task1.getEndTime() == null && task2.getEndTime() == null) {
return 0;
}
else if (task1.getEndTime() == null) {
return 1;
}
else if (task2.getEndTime() == null) {
return -1;
}
else {
return task2.getEndTime().compareTo(task1.getEndTime());
}
}
}
}