/* * Copyright (c) 2015 EMC Corporation * All Rights Reserved */ package com.emc.sa.catalog; import static com.emc.storageos.db.client.URIUtil.uri; import java.net.URI; import java.net.URISyntaxException; import java.util.*; import javax.annotation.PostConstruct; import javax.annotation.Resource; import com.emc.storageos.api.service.impl.resource.ArgValidator; import com.emc.storageos.db.client.util.ExecutionWindowHelper; import com.emc.storageos.db.client.model.*; import com.emc.storageos.db.client.model.uimodels.*; import com.emc.storageos.svcs.errorhandling.resources.APIException; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.emc.sa.asset.AssetOptionsContext; import com.emc.sa.asset.AssetOptionsManager; import com.emc.sa.descriptor.ServiceDescriptor; import com.emc.sa.descriptor.ServiceDescriptors; import com.emc.sa.descriptor.ServiceField; import com.emc.sa.model.dao.ModelClient; import com.emc.sa.model.util.CreationTimeComparator; import com.emc.sa.model.util.SortedIndexUtils; import com.emc.sa.util.ResourceType; import com.emc.sa.util.CatalogSerializationUtils; import com.emc.sa.util.TextUtils; import com.emc.sa.zookeeper.OrderCompletionQueue; import com.emc.sa.zookeeper.OrderExecutionQueue; import com.emc.sa.zookeeper.OrderMessage; import com.emc.sa.zookeeper.OrderNumberSequence; import com.emc.storageos.auth.TokenManager; import com.emc.storageos.db.client.model.UnManagedDiscoveredObjects.UnManagedExportMask; import com.emc.storageos.db.client.model.UnManagedDiscoveredObjects.UnManagedFileSystem; import com.emc.storageos.db.client.model.UnManagedDiscoveredObjects.UnManagedVolume; import com.emc.storageos.security.authentication.StorageOSUser; import com.emc.vipr.model.catalog.AssetOption; import com.google.common.collect.Maps; @Component public class OrderManagerImpl implements OrderManager { private static final Logger log = LoggerFactory.getLogger(OrderManagerImpl.class); private long noDeletePeriod = 2592000000L; @Autowired private ModelClient client; @Autowired private OrderExecutionQueue orderExecutionQueue; @Autowired private OrderCompletionQueue orderCompletionQueue; @Resource(name = "tokenManager") private TokenManager tokenManager; @Autowired private OrderNumberSequence orderNumberSequence; @Autowired private CatalogServiceManager catalogServiceManager; @Autowired private ApprovalManager approvalManager; @Autowired private NotificationManager notificationManager; @Autowired private ServiceDescriptors serviceDescriptors; @Autowired private AssetOptionsManager assetOptionsManager; @PostConstruct private void init() throws Exception { orderCompletionQueue.listenForRequests(new OrderCompletionConsumer(this)); } public Order getOrderById(URI id) { if (id == null) { return null; } Order order = client.orders().findById(id); return order; } public void setNoDeletePeriod(long noDeletePeriod) { this.noDeletePeriod = noDeletePeriod; } public long getNoDeletePeriod() { return noDeletePeriod; } public Order createOrder(Order order, List<OrderParameter> orderParameters, StorageOSUser user) { CatalogService catalogService = catalogServiceManager.getCatalogServiceById(order.getCatalogServiceId()); ServiceDescriptor serviceDescriptor = serviceDescriptors.getDescriptor(Locale.getDefault(), catalogService.getBaseService()); order.setOrderNumber(getNextOrderNumber()); order.setSummary(catalogService.getTitle()); if (order.getScheduledEventId() == null) { // Order is scheduled with traditional way but not the new scheduler framework if (catalogService.getExecutionWindowRequired()) { if (catalogService.getDefaultExecutionWindowId() == null || catalogService.getDefaultExecutionWindowId().getURI().equals(ExecutionWindow.NEXT)) { // For default execution window, null is deemed as NEXT window as well. // But we always need to set order execution window to NEXT explicitly to different it // with INFINITE window in new scheduler framework. // Set schedule time to latest updated time. It would still be scheduled in executed window Calendar scheduleTime = Calendar.getInstance(TimeZone.getTimeZone("UTC")); scheduleTime.setTime(new Date()); order.setScheduledTime(scheduleTime); order.setExecutionWindowId(new NamedURI(ExecutionWindow.NEXT, "NEXT")); } else { // Set schedule time to // either 1) the next execution window starting time // or 2) the current time if it is in current execution window ExecutionWindow executionWindow = client.findById(catalogService.getDefaultExecutionWindowId().getURI()); ExecutionWindowHelper helper = new ExecutionWindowHelper(executionWindow); order.setScheduledTime(helper.getScheduledTime()); order.setExecutionWindowId(catalogService.getDefaultExecutionWindowId()); } } else { // In traditional order procedure, // If no execution window is indicated, order will be submitted to DQ immediately. ; } } else { // Order is scheduled with new scheduler framework // ExecutionWindow and ScheduleTime are already set via Parameter ; } order.setMessage(""); order.setSubmittedByUserId(user.getUserName()); order.setOrderStatus(OrderStatus.PENDING.name()); createExecutionState(order, user); client.save(order); Map<String, String> assetOptions = getAssetValues(serviceDescriptor, orderParameters); for (OrderParameter orderParameter : orderParameters) { ServiceField serviceField = findServiceField(serviceDescriptor, orderParameter.getLabel()); String friendlyLabel = serviceField.getLabel(); StringBuilder friendlyValue = new StringBuilder(); List<String> values = TextUtils.parseCSV(orderParameter.getValue()); for (String value : values) { if (friendlyValue.length() > 0) { friendlyValue.append(","); } friendlyValue.append(getFriendlyValue(serviceField, value, assetOptions, user)); } orderParameter.setFriendlyLabel(friendlyLabel); orderParameter.setFriendlyValue(friendlyValue.toString()); createOrderParameter(orderParameter); } return order; } public void createOrderParameter(OrderParameter orderParameter) { client.save(orderParameter); } // For 'asset' type fields, put in the label instead of the actual value. // This prevents the receipt page from having things like FileSystem: 123 private String getFriendlyValue(ServiceField serviceField, String value, Map<String, String> assetValues, StorageOSUser user) { if (serviceField != null && StringUtils.isNotBlank(value)) { String[] labels = value.split(","); for (int i = 0; i < labels.length; i++) { if (serviceField.isAsset()) { labels[i] = getOptionLabelForAsset(labels[i], serviceField.getAssetType(), assetValues, user); } else if (serviceField.getOptions() != null && serviceField.getOptions().size() > 0) { String optionLabel = serviceField.getOptions().get(labels[i]); if (StringUtils.isNotBlank(optionLabel)) { labels[i] = optionLabel; } } } return StringUtils.join(labels, ","); } return ""; } private boolean canGetResourceLabel(String resourceId) { switch (ResourceType.fromResourceId(resourceId)) { case VOLUME: case PROJECT: case HOST: case CLUSTER: case QUOTA_DIRECTORY: case VIRTUAL_ARRAY: case VIRTUAL_POOL: case COMPUTE_VIRTUAL_POOL: case CONSISTENCY_GROUP: case STORAGE_SYSTEM: case EXPORT_GROUP: case FILE_SHARE: case FILE_SNAPSHOT: case BLOCK_SNAPSHOT: case BLOCK_SNAPSHOT_SESSION: case VCENTER: case VCENTER_DATA_CENTER: case SMIS_PROVIDER: case STORAGE_POOL: case NETWORK_SYSTEM: case PROTECTION_SYSTEM: case UNMANAGED_VOLUME: case UNMANAGED_FILESYSTEM: case UNMANAGED_EXPORTMASK: case BLOCK_CONTINUOUS_COPY: case VPLEX_CONTINUOUS_COPY: case STORAGE_PORT: return true; default: return false; } } private String getOptionLabelForAsset(String key, String assetType, Map<String, String> assetValues, StorageOSUser user) { try { if (canGetResourceLabel(key)) { return getResourceLabel(key); } else if (CatalogSerializationUtils.isSerializedObject(key)) { Map<URI, List<URI>> port = (Map<URI, List<URI>>) CatalogSerializationUtils.serializeFromString(key); String s = new String("{"); for (Map.Entry<URI, List<URI> > entry : port.entrySet()) { s += getResourceLabel(entry.getKey().toString()); s += ":["; List<String> portLabels = new ArrayList<String>(); for (URI p : entry.getValue()) { portLabels.add(getResourceLabel(p.toString())); } s += String.join(",", portLabels); s += "]"; } s += "}"; log.info(String.format("Serialized label: %s", s)); return s; } else { // Defer to AssetOptions if it's not a ViPR resource log.info(String.format("AssetType %s not a ViPR resource, deferring to AssetOptions to get value.", key)); AssetOptionsContext context = assetOptionsManager.createDefaultContext(user); List<AssetOption> options = assetOptionsManager.getOptions(context, assetType, assetValues); for (AssetOption option : options) { if (option.key.equals(key)) { return option.value; } } } } catch (Exception e) { log.error(String.format("Error getting label for asset %s", key), e); } return key; } protected String getResourceLabel(String resourceId) { DataObject dataObject = null; try { URI id = uri(resourceId); switch (ResourceType.fromResourceId(resourceId)) { case VOLUME: dataObject = client.findById(Volume.class, id); break; case PROJECT: dataObject = client.findById(Project.class, id); break; case HOST: dataObject = client.findById(Host.class, id); break; case CLUSTER: dataObject = client.findById(Cluster.class, id); break; case COMPUTE_VIRTUAL_POOL: dataObject = client.findById(ComputeVirtualPool.class, id); break; case QUOTA_DIRECTORY: dataObject = client.findById(QuotaDirectory.class, id); break; case VIRTUAL_ARRAY: dataObject = client.findById(VirtualArray.class, getTargetVirtualArray(id)); break; case VIRTUAL_POOL: dataObject = client.findById(VirtualPool.class, id); break; case CONSISTENCY_GROUP: dataObject = client.findById(BlockConsistencyGroup.class, id); break; case STORAGE_SYSTEM: dataObject = client.findById(StorageSystem.class, id); break; case EXPORT_GROUP: dataObject = client.findById(ExportGroup.class, id); break; case FILE_SHARE: dataObject = client.findById(FileShare.class, id); break; case FILE_SNAPSHOT: dataObject = client.findById(Snapshot.class, id); break; case BLOCK_SNAPSHOT: dataObject = client.findById(BlockSnapshot.class, id); break; case BLOCK_SNAPSHOT_SESSION: dataObject = client.findById(BlockSnapshotSession.class, id); break; case VCENTER: dataObject = client.findById(Vcenter.class, id); break; case VCENTER_DATA_CENTER: dataObject = client.findById(VcenterDataCenter.class, id); break; case SMIS_PROVIDER: dataObject = client.findById(StorageProvider.class, id); break; case STORAGE_POOL: dataObject = client.findById(StoragePool.class, id); break; case NETWORK_SYSTEM: dataObject = client.findById(Network.class, id); break; case PROTECTION_SYSTEM: dataObject = client.findById(ProtectionSystem.class, id); break; case UNMANAGED_VOLUME: dataObject = client.findById(UnManagedVolume.class, id); break; case UNMANAGED_FILESYSTEM: dataObject = client.findById(UnManagedFileSystem.class, id); break; case UNMANAGED_EXPORTMASK: dataObject = client.findById(UnManagedExportMask.class, id); break; case BLOCK_CONTINUOUS_COPY: dataObject = client.findById(BlockMirror.class, id); break; case VPLEX_CONTINUOUS_COPY: dataObject = client.findById(VplexMirror.class, id); break; case STORAGE_PORT: dataObject = client.findById(StoragePort.class, id); break; case INITIATOR: dataObject = client.findById(Initiator.class, id); break; } } catch (Exception e) { log.error(String.format("Error getting resource %s", resourceId), e); } if (dataObject != null) { return augmentDataObjectName(dataObject); } else { return resourceId; } } private URI getTargetVirtualArray(URI id) { if (id.toString().startsWith("tgt:")) { return URI.create(StringUtils.substringAfter(id.toString(), ":")); } else { return id; } } /** * Allows manipulation of the string returned from the DataObjectResponse.getName() */ private String augmentDataObjectName(DataObject dataObject) { if (dataObject instanceof Host) { URI clusterResource = ((Host) dataObject).getCluster(); if (clusterResource != null) { Cluster cluster = client.findById(Cluster.class, clusterResource); if (cluster != null) { return String.format("%s [cluster: %s]", dataObject.getLabel(), cluster.getLabel()); } } } else if (dataObject instanceof StoragePort) { return ((StoragePort) dataObject).getPortName(); } return dataObject.getLabel(); } private Map<String, String> getAssetValues(ServiceDescriptor descriptor, List<OrderParameter> orderParameters) { Map<String, String> assetOptionParams = Maps.newHashMap(); if (descriptor != null && orderParameters != null) { // Create a map of assetType -> Value for (ServiceField serviceField : descriptor.getAllFieldList()) { OrderParameter orderParameter = findOrderParameter(serviceField.getName(), orderParameters); if (orderParameter != null) { assetOptionParams.put(serviceField.getAssetType(), orderParameter.getValue()); } } } return assetOptionParams; } private ServiceField findServiceField(ServiceDescriptor serviceDescriptor, String serviceFieldName) { for (ServiceField serviceField : serviceDescriptor.getAllFieldList()) { if (StringUtils.equalsIgnoreCase(serviceFieldName, serviceField.getName())) { return serviceField; } } log.info(String.format("Unexpected service field value found: %s", serviceFieldName)); ServiceField field = new ServiceField(); field.setName(serviceFieldName); field.setLabel(serviceFieldName); return field; } private OrderParameter findOrderParameter(String serviceFieldName, List<OrderParameter> orderParameters) { for (OrderParameter orderParameter : orderParameters) { if (serviceFieldName.equals(orderParameter.getLabel())) { return orderParameter; } } return null; } private ExecutionState createExecutionState(Order order, StorageOSUser user) { ExecutionState state = new ExecutionState(); String proxyToken = tokenManager.getProxyToken(user); state.setProxyToken(proxyToken); state.setExecutionStatus(ExecutionStatus.NONE.name()); client.save(state); order.setExecutionStateId(state.getId()); return state; } public void updateOrder(Order order) { client.save(order); } public void canBeDeleted(Order order, OrderStatus orderStatus) { if (order.getScheduledEventId()!=null) { throw APIException.badRequests.scheduledOrderNotAllowed("deactivation"); } if (createdWithinOneMonth(order)) { throw APIException.badRequests.orderWithinOneMonth(order.getId()); } OrderStatus status = OrderStatus.valueOf(order.getOrderStatus()); if (orderStatus != null && status != orderStatus) { throw APIException.badRequests.orderCanNotBeDeleted(order.getId(), status.toString()); } if (!status.canBeDeleted()) { throw APIException.badRequests.orderCanNotBeDeleted(order.getId(), status.toString()); } } private boolean createdWithinOneMonth(Order order) { long now = System.currentTimeMillis(); long createdTime = order.getCreationTime().getTimeInMillis(); return (now - createdTime) < noDeletePeriod; } public void deleteOrder(Order order) { canBeDeleted(order, null); URI orderId = order.getId(); List<ApprovalRequest> approvalRequests = approvalManager.findApprovalsByOrderId(orderId); client.delete(approvalRequests); List<OrderParameter> orderParameters = getOrderParameters(orderId); client.delete(orderParameters); ExecutionState state = getOrderExecutionState(order.getExecutionStateId()); if (state != null) { StringSet logIds = state.getLogIds(); URI id = null; for (String logId : logIds) { try { id = new URI(logId); } catch (URISyntaxException e) { log.error("Invalid id {} e=", logId, e); continue; } ExecutionLog execlog = client.getModelClient().findById(ExecutionLog.class, id); client.delete(execlog); } List<ExecutionTaskLog> logs = client.executionTaskLogs().findByIds(state.getTaskLogIds()); for (ExecutionTaskLog taskLog: logs) { client.delete(taskLog); } client.delete(state); } client.delete(order); } public List<Order> getOrders(URI tenantId) { return client.orders().findAll(tenantId.toString()); } public List<Order> getUserOrders(StorageOSUser user, long startTime, long endTime, int maxCount) { return client.orders().findOrdersByUserId(user.getUserName(), startTime, endTime, maxCount); } public long getOrderCount(StorageOSUser user, long startTime, long endTime) { return client.orders().getOrdersCount(user.getUserName(), startTime, endTime); } public Map<String, Long> getOrderCount(List<URI> tids, long startTime, long endTime) { return client.orders().getOrdersCount(tids, startTime, endTime); } public List<Order> findOrdersByStatus(URI tenantId, OrderStatus orderStatus) { return client.orders().findByOrderStatus(tenantId.toString(), orderStatus); } public List<Order> findOrdersByTimeRange(URI tenantId, Date startTime, Date endTime, int maxCount) { return client.orders().findByTimeRange(tenantId, startTime, endTime, maxCount); } public List<ExecutionLog> getOrderExecutionLogs(Order order) { ExecutionState executionState = getOrderExecutionState(order.getExecutionStateId()); List<ExecutionLog> logs = client.executionLogs().findByIds(executionState.getLogIds()); Collections.sort(logs, CreationTimeComparator.OLDEST_FIRST); return logs; } public List<ExecutionTaskLog> getOrderExecutionTaskLogs(Order order) { ExecutionState executionState = getOrderExecutionState(order.getExecutionStateId()); List<ExecutionTaskLog> logs = client.executionTaskLogs().findByIds(executionState.getTaskLogIds()); Collections.sort(logs, CreationTimeComparator.OLDEST_FIRST); return logs; } public ExecutionState getOrderExecutionState(URI executionStateId) { return client.executionStates().findById(executionStateId); } public List<OrderParameter> getOrderParameters(URI orderId) { List<OrderParameter> parameters = client.orderParameters().findByOrderId(orderId); SortedIndexUtils.sort(parameters); return parameters; } public List<OrderAndParams> getOrdersAndParams(List<URI> ids) { List<OrderAndParams> ordersAndParams = new ArrayList<OrderAndParams>(); if (ids == null) { return null; } for (URI id : ids) { Order order = client.orders().findById(id); if (order != null) { List<OrderParameter> params = getOrderParameters(order.getId()); OrderAndParams orderAndParams = new OrderAndParams(); orderAndParams.setOrder(order); orderAndParams.setParameters(params); ordersAndParams.add(orderAndParams); } } return ordersAndParams; } private void submitOrderToQueue(Order order) { try { orderExecutionQueue.putItem(new OrderMessage(order.getId().toString())); } catch (Exception e) { log.error(String.format("Unable to send order %s to Order Execution queue", order.getId()), e); failOrder(order, "Unable to send order to Order Execution queue", e); throw new RuntimeException(e); } } public String getNextOrderNumber() { return Long.toString(orderNumberSequence.nextOrderNumber()); } public void processOrder(Order order) { CatalogService service = catalogServiceManager.getCatalogServiceById(order.getCatalogServiceId()); OrderStatus status = OrderStatus.valueOf(order.getOrderStatus()); switch (status) { case PENDING: processPendingOrder(order, service); break; case APPROVAL: processApprovalOrder(order, service); break; case APPROVED: processApprovedOrder(order, service); break; case REJECTED: processRejectedOrder(order, service); break; case SCHEDULED: processScheduledOrder(order, service); break; case CANCELLED: processCancelledOrder(order, service); break; case SUCCESS: processSuccessOrder(order, service); processScheduledEvent(order); break; case ERROR: processErrorOrder(order, service); processScheduledEvent(order); break; } } private void processPendingOrder(Order order, CatalogService service) { if (Boolean.TRUE.equals(service.getApprovalRequired())) { if (order.getScheduledEventId() != null) { // For scheduled event, the 1st order's APPROVAL request will be used to approve // the whole set of following orders (i.e. taking effect on the scheduled event. ScheduledEvent scheduledEvent = client.scheduledEvents().findById(order.getScheduledEventId()); if (scheduledEvent != null) { // Scheduler would always set the outofdate orders to ERROR and schedule a new order. // Here all the previous scheduled orders have not been approved yet. // We would always send a new approval request for the latest scheduled order. if (scheduledEvent.getEventStatus() == ScheduledEventStatus.APPROVAL) { requireApproval(order, service); return; } // For the following orders, skipping order approval request (the event is already APPROVED) } else { // Send Approval Request for the 1st order (Now the scheduled event is not persisted into DB yet.) requireApproval(order, service); return; } } else { // send approval request for the original order request. requireApproval(order, service); return; } } if (order.getScheduledEventId() != null) { // scheduledEvent always requires orders to be scheduled. scheduleOrder(order, service); } else if (Boolean.TRUE.equals(service.getExecutionWindowRequired())) { // direct orders would be executed in the next execution window. scheduleOrder(order, service); } else { submitOrderToQueue(order); } } private void processApprovalOrder(Order order, CatalogService service) { ApprovalRequest approval = approvalManager.findFirstApprovalsByOrderId(order.getId()); ApprovalStatus status = ApprovalStatus.valueOf(approval.getApprovalStatus()); switch (status) { case APPROVED: approveOrder(order, service); authorizeScheduledEvent(order, service, true); break; case REJECTED: rejectOrder(order, service); authorizeScheduledEvent(order, service, false); break; } } private void processApprovedOrder(Order order, CatalogService service) { ApprovalRequest approval = approvalManager.findFirstApprovalsByOrderId(order.getId()); notificationManager.notifyUserOfApprovalStatus(order, service, approval); if (order.getScheduledEventId() != null) { // orders always need to be scheduled via scheduledEvent. scheduleOrder(order, service); } else if (Boolean.TRUE.equals(service.getExecutionWindowRequired())) { // direct orders would be executed in the next execution window. scheduleOrder(order, service); } else { submitOrderToQueue(order); } } private void processRejectedOrder(Order order, CatalogService service) { ApprovalRequest approval = approvalManager.findFirstApprovalsByOrderId(order.getId()); notificationManager.notifyUserOfApprovalStatus(order, service, approval); } private void processScheduledOrder(Order order, CatalogService service) { ApprovalRequest approval = approvalManager.findFirstApprovalsByOrderId(order.getId()); notificationManager.notifyUserOfOrderStatus(order, service, approval); } private void processCancelledOrder(Order order, CatalogService service) { ApprovalRequest approval = approvalManager.findFirstApprovalsByOrderId(order.getId()); notificationManager.notifyUserOfOrderStatus(order, service, approval); } private void processSuccessOrder(Order order, CatalogService service) { ApprovalRequest approval = approvalManager.findFirstApprovalsByOrderId(order.getId()); notificationManager.notifyUserOfOrderStatus(order, service, approval); } private void processErrorOrder(Order order, CatalogService service) { ApprovalRequest approval = approvalManager.findFirstApprovalsByOrderId(order.getId()); notificationManager.notifyUserOfOrderStatus(order, service, approval); } private void approveOrder(Order order, CatalogService service) { order.setOrderStatus(OrderStatus.APPROVED.name()); updateOrder(order); processApprovedOrder(order, service); } private void authorizeScheduledEvent(Order order, CatalogService service, boolean approved) { ScheduledEvent scheduledEvent = client.scheduledEvents().findById(order.getScheduledEventId()); if (scheduledEvent != null) { scheduledEvent.setEventStatus(approved? ScheduledEventStatus.APPROVED: ScheduledEventStatus.REJECTED); client.save(scheduledEvent); } } private void rejectOrder(Order order, CatalogService service) { order.setOrderStatus(OrderStatus.REJECTED.name()); updateOrder(order); processRejectedOrder(order, service); } private void scheduleOrder(Order order, CatalogService service) { log.info(String.format("Order %s is scheduled", order.getId())); order.setOrderStatus(OrderStatus.SCHEDULED.name()); updateOrder(order); processScheduledOrder(order, service); } private void requireApproval(Order order, CatalogService service) { log.info(String.format("Order %s requires approval", order.getId())); order.setOrderStatus(OrderStatus.APPROVAL.name()); updateOrder(order); ApprovalRequest approvalRequest = new ApprovalRequest(); approvalRequest.setApprovalStatus(ApprovalStatus.PENDING.name()); approvalRequest.setOrderId(order.getId()); if (order.getScheduledEventId() != null) { approvalRequest.setScheduledEventId(order.getScheduledEventId()); } approvalRequest.setTenant(order.getTenant()); approvalManager.createApproval(approvalRequest); notificationManager.notifyApproversOfApprovalRequest(order, service, approvalRequest); } private void failOrder(Order order, String message, Throwable e) { ExecutionLog failedLog = new ExecutionLog(); failedLog.setMessage(message); failedLog.addStackTrace(e); failedLog.setLevel(ExecutionLog.LogLevel.ERROR.name()); failedLog.setDate(new Date()); client.save(failedLog); ExecutionState state = getOrderExecutionState(order.getExecutionStateId()); state.addExecutionLog(failedLog); state.setEndDate(new Date()); state.setExecutionStatus(ExecutionStatus.FAILED.name()); client.save(state); order.setMessage(message); order.setOrderStatus(OrderStatus.ERROR.name()); updateOrder(order); } public void processScheduledEvent(Order order) { URI scheduledEventId = order.getScheduledEventId(); ScheduledEvent scheduledEvent = client.scheduledEvents().findById(scheduledEventId); if (scheduledEvent != null) { if (scheduledEvent.getEventType() == ScheduledEventType.ONCE) { // For ONCE event, update its status to FINISHED after order finished. // For REOCCURRENCE event, update its status during scheduler thread. if (OrderStatus.valueOf(order.getOrderStatus()).equals(OrderStatus.SUCCESS) || OrderStatus.valueOf(order.getOrderStatus()).equals(OrderStatus.PARTIAL_SUCCESS) || OrderStatus.valueOf(order.getOrderStatus()).equals(OrderStatus.ERROR) ) { scheduledEvent.setEventStatus(ScheduledEventStatus.FINISHED); client.save(scheduledEvent); } } } return; } }