/* * Copyright (c) 2015 EMC Corporation * All Rights Reserved */ package com.emc.sa.engine; import java.util.Date; import java.util.List; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.exception.ExceptionUtils; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.emc.sa.engine.bind.BindingUtils; import com.emc.sa.engine.lock.ExecutionLockManager; import com.emc.sa.engine.service.ExecutionService; import com.emc.sa.engine.service.ExecutionServiceFactory; import com.emc.sa.engine.service.ServiceNotFoundException; 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.ExecutionStatus; import com.emc.storageos.db.client.model.uimodels.Order; import com.emc.storageos.db.client.model.uimodels.OrderStatus; import com.emc.sa.model.dao.ModelClient; import com.emc.storageos.coordinator.client.service.CoordinatorClient; import com.google.common.collect.Lists; @Component public class ExecutionEngineImpl implements ExecutionEngine { private static final Logger LOG = Logger.getLogger(ExecutionEngineImpl.class); @Autowired private ModelClient modelClient; @Autowired private ExecutionServiceFactory serviceFactory; @Autowired private CoordinatorClient coordinatorClient; public ModelClient getModelClient() { return modelClient; } public void setModelClient(ModelClient modelClient) { this.modelClient = modelClient; } public ExecutionServiceFactory getServiceFactory() { return serviceFactory; } public void setServiceFactory(ExecutionServiceFactory serviceFactory) { this.serviceFactory = serviceFactory; } public CoordinatorClient getCoordinatorClient() { return coordinatorClient; } public void setCoordinatorClient(CoordinatorClient coordinatorClient) { this.coordinatorClient = coordinatorClient; } @Override public void executeOrder(Order order) { LOG.info(String.format("Executing order: %s [%s]", order.getOrderNumber(), order.getId())); initContext(order); try { updateOrderStatus(order, OrderStatus.EXECUTING); ExecutionService service = createService(order); runService(service); orderCompleted(order, service.getCompletedOrderStatus()); } catch (ExecutionException e) { orderFailed(order, e); } catch (RuntimeException e) { LOG.error("Unexpected error executing order: " + order.getId()); orderFailed(order, new ExecutionException(e)); } finally { destroyContext(order); } } protected void initContext(Order order) { ExecutionUtils.createContext(getModelClient(), order); // Adds execution lock support ExecutionLockManager lockManager = new ExecutionLockManager(getCoordinatorClient()); ExecutionUtils.currentContext().setLockManager(lockManager); ExecutionUtils.currentContext().setCoordinatorClient(coordinatorClient); } protected void destroyContext(Order order) { ExecutionLockManager lockManager = ExecutionUtils.currentContext().getLockManager(); if (lockManager != null) { lockManager.destroyLocks(); } ExecutionUtils.destroyContext(); } protected void orderCompleted(Order order, OrderStatus status) { LOG.info("Order complete: " + order.getId()); order.setDateCompleted(new Date()); updateOrderStatus(order, status); finishExecuting(ExecutionStatus.COMPLETED); } protected void orderFailed(Order order, ExecutionException e) { LOG.error("Order failed: " + order.getId(), e.getCause()); order.setMessage(getErrorMessage(e)); order.setDateCompleted(new Date()); updateOrderStatus(order, OrderStatus.ERROR); finishExecuting(ExecutionStatus.FAILED); } protected String getErrorMessage(ExecutionException e) { Throwable rootCause = ExceptionUtils.getRootCause(e); if (StringUtils.isNotBlank(rootCause.getMessage())) { return rootCause.getMessage(); } else { return ExceptionUtils.getFullStackTrace(e.getCause()); } } protected void runService(ExecutionService service) { try { init(service); precheck(service); execute(service); } catch (ExecutionException e) { logError(e, service); try { rollback(); } catch (ExecutionException re) { logError(re, service); } throw e; } finally { destroy(service); } } protected ExecutionService createService(Order order) { try { CatalogService catalogService = getModelClient().catalogServices().findById(order.getCatalogServiceId()); return serviceFactory.createService(order, catalogService); } catch (ServiceNotFoundException e) { LOG.error("Could not create service for order: " + order.getId(), e); throw new ExecutionException(e); } catch (RuntimeException e) { LOG.error("Unexpected exception while creating service for order: " + order.getId(), e); throw new ExecutionException(e); } } protected void init(ExecutionService service) throws ExecutionException { try { ExecutionContext context = ExecutionUtils.currentContext(); LOG.debug("Initialize " + context.getServiceName()); bindParameters(context, service); service.init(); } catch (ExecutionException e) { throw e; } catch (Exception e) { throw new ExecutionException(e); } } protected void precheck(ExecutionService service) throws ExecutionException { try { ExecutionContext context = ExecutionUtils.currentContext(); LOG.debug("Precheck " + context.getServiceName()); updateExecutionStatus(ExecutionStatus.PRECHECK); service.precheck(); } catch (ExecutionException e) { throw e; } catch (Exception e) { throw new ExecutionException(e); } } protected void execute(ExecutionService service) throws ExecutionException { try { ExecutionContext context = ExecutionUtils.currentContext(); LOG.debug("Executing " + context.getServiceName()); updateExecutionStatus(ExecutionStatus.EXECUTE); service.execute(); } catch (ExecutionException e) { throw e; } catch (Exception e) { throw new ExecutionException(e); } } protected void destroy(ExecutionService service) { try { ExecutionContext context = ExecutionUtils.currentContext(); LOG.debug("Destroy " + context.getServiceName()); service.destroy(); } catch (ExecutionException e) { throw e; } catch (RuntimeException e) { throw new ExecutionException(e); } } protected void rollback() throws RollbackException { ExecutionContext context = ExecutionUtils.currentContext(); if (context.getRollback().size() > 0) { LOG.debug("Rolling back: " + context.getServiceName()); try { updateExecutionStatus(ExecutionStatus.ROLLBACK); // Execute the rollbacks in the opposite order List<ExecutionTask<?>> rollback = Lists.reverse(context.getRollback()); for (ExecutionTask<?> task : rollback) { ExecutionUtils.execute(task); } } catch (ExecutionException e) { throw new RollbackException(e.getCause()); } catch (Exception e) { throw new RollbackException(e); } } } protected void logError(ExecutionException error, ExecutionService service) { try { String message = getDeepestCauseMessage(error); if (message == null) { message = error.getCause().getClass().getName(); } ExecutionContext context = ExecutionUtils.currentContext(); context.logError(error.getCause(), message); } catch (RuntimeException e) { LOG.error("Unexpected runtime exception while logging error", e); } } protected String getDeepestCauseMessage(Throwable t) { @SuppressWarnings("unchecked") List<Throwable> throwables = ExceptionUtils.getThrowableList(t); String message = null; for (Throwable cause : throwables) { if (cause.getMessage() != null) { message = cause.getMessage(); } } return message; } protected void bindParameters(ExecutionContext context, ExecutionService service) { BindingUtils.bind(service, context.getParameters()); } protected void updateOrderStatus(Order order, OrderStatus status) { order.setOrderStatus(status.name()); if (modelClient != null) { modelClient.save(order); } } protected void updateExecutionStatus(ExecutionStatus status) { ExecutionState state = ExecutionUtils.currentContext().getExecutionState(); state.setExecutionStatus(status.name()); if (modelClient != null) { modelClient.save(state); } } protected void finishExecuting(ExecutionStatus status) { ExecutionState state = ExecutionUtils.currentContext().getExecutionState(); state.setCurrentTask(""); state.setEndDate(new Date()); state.setExecutionStatus(status.name()); if (modelClient != null) { modelClient.save(state); } } }