/* * Copyright (c) 2015 EMC Corporation * All Rights Reserved */ package com.emc.sa.engine; import java.net.URI; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.MissingResourceException; import org.apache.commons.lang.StringUtils; import com.emc.sa.engine.inject.Injector; import com.emc.sa.model.dao.ModelClient; import com.emc.sa.util.Messages; import com.emc.storageos.db.client.model.uimodels.CatalogService; import com.emc.storageos.db.client.model.uimodels.ExecutionState; import com.emc.storageos.db.client.model.uimodels.ExecutionTaskLog; import com.emc.storageos.db.client.model.uimodels.Order; import com.emc.storageos.db.client.model.uimodels.OrderParameter; import com.emc.storageos.db.client.model.uimodels.OrderStatus; import com.emc.storageos.db.client.model.uimodels.ScheduledEvent; import com.emc.vipr.client.ClientConfig; import com.emc.vipr.client.Task; import com.emc.vipr.client.Tasks; import com.emc.vipr.model.catalog.OrderCreateParam; import com.google.common.collect.Maps; public class ExecutionUtils { private static Messages MESSAGES = new Messages(ExecutionUtils.class, "ViPRService"); private static final ThreadLocal<ExecutionContext> CONTEXT_HOLDER = new ThreadLocal<ExecutionContext>() { protected ExecutionContext initialValue() { return new ExecutionContext(); } }; public static void createContext(ModelClient modelClient, Order order) { // Ensure there is no existing context destroyContext(); // Initialize the execution state for this order ExecutionState state = modelClient.executionStates().findById(order.getExecutionStateId()); state.setStartDate(new Date()); ExecutionContext context = currentContext(); context.setOrder(order); context.setModelClient(modelClient); context.setExecutionState(state); URI scheduledEventId = order.getScheduledEventId(); if (scheduledEventId != null) { ScheduledEvent event = modelClient.findById(ScheduledEvent.class, scheduledEventId); context.setScheduledEvent(event); } CatalogService catalogService = modelClient.catalogServices().findById(order.getCatalogServiceId()); context.setServiceName(catalogService.getLabel()); List<OrderParameter> orderParameters = modelClient.orderParameters().findByOrderId(order.getId()); Map<String, Object> params = Maps.newLinkedHashMap(); for (OrderParameter param : orderParameters) { params.put(param.getLabel(), param.getValue()); } context.setParameters(params); } public static void destroyContext() { CONTEXT_HOLDER.remove(); } public static ExecutionContext currentContext() { return CONTEXT_HOLDER.get(); } public static <T> T execute(ExecutionTask<T> task) throws ExecutionException { return execute(task, currentContext()); } protected static <T> T execute(ExecutionTask<T> task, ExecutionContext context) throws ExecutionException { ExecutionTaskLog log = context.logCurrentTask(task); long startTime = System.currentTimeMillis(); try { injectValues(task, context); T result = task.executeTask(); long elapsedTime = System.currentTimeMillis() - startTime; context.updateCurrentTask(log, task, elapsedTime); return result; } catch (Exception e) { long elapsedTime = System.currentTimeMillis() - startTime; context.updateCurrentTask(log, task, elapsedTime, e); throw new ExecutionException(e); } } public static <T> ViPRTaskMonitor<T> startViprTask(ExecutionTask<Task<T>> task) throws ExecutionException { return startViprTask(task, currentContext()); } protected static <T> ViPRTaskMonitor<T> startViprTask(ExecutionTask<Task<T>> task, ExecutionContext context) throws ExecutionException { ExecutionTaskLog log = context.logCurrentTask(task); long startTime = System.currentTimeMillis(); try { injectValues(task, context); Task<T> result = task.executeTask(); return new ViPRTaskMonitor<T>(context, log, result); } catch (Exception e) { long elapsedTime = System.currentTimeMillis() - startTime; context.updateCurrentTask(log, task, elapsedTime, e); throw new ExecutionException(e); } } public static <T> ViPRTasksMonitor<T> startViprTasks(ExecutionTask<Tasks<T>> task) throws ExecutionException { return startViprTasks(task, currentContext()); } protected static <T> ViPRTasksMonitor<T> startViprTasks(ExecutionTask<Tasks<T>> task, ExecutionContext context) throws ExecutionException { ExecutionTaskLog log = context.logCurrentTask(task); long startTime = System.currentTimeMillis(); try { injectValues(task, context); Tasks<T> result = task.executeTask(); return new ViPRTasksMonitor<T>(context, log, result); } catch (Exception e) { long elapsedTime = System.currentTimeMillis() - startTime; context.updateCurrentTask(log, task, elapsedTime, e); throw new ExecutionException(e); } } /** * Injects any values into the execution task. * * @param task * the execution task. * @param context * the execution context. */ public static <T> void injectValues(ExecutionTask<T> task, ExecutionContext context) { Injector.inject(task, context.getInjectedValues()); } public static void addRollback(ExecutionTask<?> rollbackTask) { currentContext().getRollback().add(rollbackTask); } /** * Removes a rollback task of a certain type. * * @param type * the type of the rollback task. */ public static void removeRollback(Class<? extends ExecutionTask<?>> type) { Iterator<ExecutionTask<?>> iter = currentContext().getRollback().iterator(); while (iter.hasNext()) { ExecutionTask<?> task = iter.next(); if (type.equals(task.getClass())) { iter.remove(); } } } /** * Clears all rollback tasks. */ public static void clearRollback() { currentContext().getRollback().clear(); } public static void addAffectedResource(String resourceId) { currentContext().getExecutionState().addAffectedResource(resourceId); } public static boolean acquireLock(String name) { return currentContext().getLockManager().acquireLock(name); } public static void releaseLock(String name) { currentContext().getLockManager().releaseLock(name); } public static void fail(String taskNameKey, Object[] detailArgs, Object... messageArgs) { execute(new FailTask(taskNameKey, detailArgs, messageArgs)); } public static void fail(String taskNameKey, Object detailArg, Object... messageArgs) { fail(taskNameKey, new Object[] { detailArg }, messageArgs); } public static void fail(String taskNameKey, Exception exception, Object[] detailArgs, Object... messageArgs) { execute(new FailTask(taskNameKey, exception, detailArgs, messageArgs)); } public static void fail(String taskNameKey, Exception exception, Object detailArg, Object... messageArgs) { fail(taskNameKey, exception, new Object[] { detailArg }, messageArgs); } public static String getMessage(String key, Object... args) { try { String message = MESSAGES.get(key, args); if (StringUtils.isNotBlank(message)) { return message; } } catch (MissingResourceException e) { // fall out and return the original key } return key; } /** * Waits for a list of tasks to complete, handling each as they complete. This uses the default client task polling * interval. * * * @param tasks * the tasks to monitor. * @param handler * the task handler. * @return true if all tasks completed successfully. */ public static <T> boolean waitForTask(List<ViPRTaskMonitor<T>> tasks, ViPRTaskHandler<T> handler) { return waitForTask(tasks, handler, ClientConfig.DEFAULT_TASK_POLLING_INTERVAL); } /** * Waits for a list of tasks to complete, handling each as they complete. * * @param tasks * the tasks to monitor. * @param handler * the task handler. * @param delay * the delay between each round of task polling. * @return true if all tasks completed successfully. */ public static <T> boolean waitForTask(List<ViPRTaskMonitor<T>> tasks, ViPRTaskHandler<T> handler, long delay) { try { boolean hasErrors = false; List<ViPRTaskMonitor<T>> remaining = new ArrayList<>(tasks); while (!remaining.isEmpty()) { // Recheck each task for completion List<ViPRTaskMonitor<T>> completed = new ArrayList<>(); for (ViPRTaskMonitor<T> task : remaining) { if (task.check()) { completed.add(task); } } // Remove all completed tasks from the remaining and handle them remaining.removeAll(completed); for (ViPRTaskMonitor<T> task : completed) { if (!completeTask(task, handler)) { hasErrors = true; } } // Sleep for a short time if there are remaining tasks if (!remaining.isEmpty()) { Thread.sleep(delay); } } return hasErrors; } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new ExecutionException(e); } } /** * Completes a task using the given handler. * * @param task * the task that was completed. * @param handler * the task handler. * @return true if the task completed successfully. */ private static <T> boolean completeTask(ViPRTaskMonitor<T> task, ViPRTaskHandler<T> handler) { try { T value = task.getValue(); handler.onSuccess(task.getTask(), value); return true; } catch (ExecutionException e) { handler.onFailure(task.getTask(), e); return false; } } /** * Checks for any errors in the tasks and throws the first found error. * * @param tasks * the tasks to check. */ public static <T> void checkForError(List<ViPRTaskMonitor<T>> tasks) { for (ViPRTaskMonitor<T> task : tasks) { if (task.getError() != null) { throw task.getError(); } } } /** * Interface used to provide common task handling to a group of tasks. * * @param <T> * the return type of the task. */ public static interface ViPRTaskHandler<T> { public void onSuccess(Task<T> task, T value); public void onFailure(Task<T> task, ExecutionException e); } }