/* * Copyright (c) 2012 EMC Corporation * All Rights Reserved */ package com.emc.storageos.db.client.model; import java.beans.Transient; import java.io.Serializable; import java.net.URI; import java.util.Calendar; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.db.client.util.ExceptionUtils; import com.emc.storageos.model.ResourceOperationTypeEnum; import com.emc.storageos.svcs.errorhandling.model.ServiceCoded; import com.emc.storageos.svcs.errorhandling.model.ServiceError; import com.emc.storageos.svcs.errorhandling.resources.ServiceCode; import com.google.common.collect.Maps; /** * Operation status */ @XmlRootElement(name = "operation") public class Operation extends AbstractSerializableNestedObject implements ClockIndependentValue, Serializable { private static final Logger _log = LoggerFactory.getLogger(Operation.class); // enumeration of status value public enum Status { pending, ready, error, suspended_no_error, suspended_error; public static Status toStatus(String status) { try { return valueOf(status.toLowerCase()); } catch (Exception e) { throw new IllegalArgumentException("status: " + status + " is not a valid status"); } } } static final String STATUS_FIELD = "status"; static final String PROGRESS_FIELD = "progress"; static final String MESSAGE_FIELD = "message"; static final String NAME_FIELD = "name"; static final String DESCRIPTION_FIELD = "description"; static final String START_TIME_FIELD = "starttime"; static final String END_TIME_FIELD = "endtime"; static final String SERVICE_CODE_FIELD = "servicecode"; static final String ASSOCIATED_RESOURCES_FIELD = "associated"; // track - set of fields modified protected Set<String> _changedFields; // ID of the actual TASK from the Task CF private Map<URI, Task> tasks = Maps.newHashMap(); public Operation() { _changedFields = new HashSet<String>(); updateStatus(Status.pending.name()); } @Deprecated public Operation(String status) { this(status, null, null); } @Deprecated public Operation(String status, ServiceCode code) { this(status, code, null); } @Deprecated public Operation(String status, String message) { this(status, null, message); } @Deprecated public Operation(String status, ServiceCode code, String message) { setStatus(status); if (message != null) { setMessage(message); } if (code != null) { setServiceCode(code.getCode()); } } /** * This method sets the status of the operation to "ready" * * @return */ public void ready() { ready("Operation completed successfully"); } /** * This method sets the status of the operation to "ready" and updates progress to be 100% * * @return */ public void ready(String message) { setMessage(message); setProgress(100); updateStatus(Status.ready.name()); } public void suspendedNoError() { suspendedNoError("Operation has been suspended due to request or configuration"); } public void suspendedNoError(String message) { setMessage(message); updateStatus(Status.suspended_no_error.name()); } public void suspendedError(String message) { setMessage(message); updateStatus(Status.suspended_error.name()); } /** * This method sets the status of the operation to "error" * * @return */ public void suspendedError(ServiceCoded sc) { if (sc != null) { setServiceCode(sc.getServiceCode().getCode()); setMessage(sc.getMessage()); } updateStatus(Status.suspended_error.name()); if (sc instanceof Exception) { _log.info("Setting operation to suspended with error due to an exception {}", ExceptionUtils.getExceptionMessage((Exception) sc)); _log.info("Caused by: ", (Exception) sc); } } public void pending() { setMessage("Operation has been restarted"); updateStatus(Status.pending.name()); } /** * This method sets the status of the operation to "error" * * @return */ public void error(ServiceCoded sc) { if (sc != null) { setServiceCode(sc.getServiceCode().getCode()); setMessage(sc.getMessage()); } updateStatus(Status.error.name()); if (sc instanceof Exception) { _log.info("Setting operation to error due to an exception {}", ExceptionUtils.getExceptionMessage((Exception) sc)); _log.info("Caused by: ", (Exception) sc); } } public ServiceError getServiceError() { ServiceCode serviceCode = ServiceCode.toServiceCode(getServiceCode()); ServiceError serviceError = ServiceError.buildServiceError(serviceCode, getMessage()); return serviceError; } @Override public int ordinal() { return Status.valueOf(getStatus()).ordinal(); } /** * Get status * * @return */ @XmlElement public String getStatus() { return getStringField(STATUS_FIELD); } /** * Convenience for setting the description and message in a uniform way * in an operation object. * * @param resourceType input resource type */ public void setResourceType(ResourceOperationTypeEnum resourceType) { setName(resourceType.getName()); setDescription(resourceType.getDescription()); } /** * Use methods "ready(...)" or "error(...)" to set the status instead * * @param status * @throws IllegalArgumentException */ @Deprecated public void setStatus(String status) throws IllegalArgumentException { if (isValidStatus(status) == false) { throw new IllegalArgumentException("status: " + status + " is not a valid status"); } setField(STATUS_FIELD, status); updateChangedField(STATUS_FIELD); } private void updateStatus(String status) throws IllegalArgumentException { if (isValidStatus(status) == false) { throw new IllegalArgumentException("status: " + status + " is not a valid status"); } setField(STATUS_FIELD, status); updateChangedField(STATUS_FIELD); } /** * Get progress * * @return null if no progress information is available */ @XmlElement public Integer getProgress() { return getIntField(PROGRESS_FIELD); } public void setProgress(int progress) { setField(PROGRESS_FIELD, progress); updateChangedField(PROGRESS_FIELD); } @XmlElement public String getDescription() { return getStringField(DESCRIPTION_FIELD); } public void setDescription(String description) { if (getDescription() == null) { setField(DESCRIPTION_FIELD, description); updateChangedField(DESCRIPTION_FIELD); } } @XmlElement public String getName() { return getStringField(NAME_FIELD); } public void setName(String name) { if (getName() == null) { setField(NAME_FIELD, name); updateChangedField(NAME_FIELD); } } @XmlElement public Calendar getStartTime() { return getDateField(START_TIME_FIELD); } public void setStartTime(Calendar time) { setField(START_TIME_FIELD, time); updateChangedField(START_TIME_FIELD); } @XmlElement public Calendar getEndTime() { return getDateField(END_TIME_FIELD); } public void setEndTime(Calendar time) { setField(END_TIME_FIELD, time); updateChangedField(END_TIME_FIELD); } /** * Get any message for operation * * @return */ @XmlElement public String getMessage() { return getStringField(MESSAGE_FIELD); } public void setMessage(String message) { setField(MESSAGE_FIELD, message); updateChangedField(MESSAGE_FIELD); } /** * Get service code * * @return null if no service code is available */ @XmlElement public Integer getServiceCode() { return getIntField(SERVICE_CODE_FIELD); } public void setServiceCode(int code) { setField(SERVICE_CODE_FIELD, code); updateChangedField(SERVICE_CODE_FIELD); } @XmlElement public List<String> getAssociatedResourcesField() { return getListOfStringsField(ASSOCIATED_RESOURCES_FIELD); } public void setAssociatedResourcesField(String associatedIds) { setField(ASSOCIATED_RESOURCES_FIELD, associatedIds); updateChangedField(ASSOCIATED_RESOURCES_FIELD); } public String rawAssociatedResources() { return getStringField(ASSOCIATED_RESOURCES_FIELD); } // During the request, holds the task from the Task CF that maps to this Operation @Transient public Task getTask(URI id) { return tasks.get(id); } public void addTask(URI id, Task task) { tasks.put(id, task); } @Override public final String toString() { return String .format("Operation(Status:%s, Progress:%s, Name:%s, Message:%s, Description:%s, StartTime:%s, EndTime:%s, ServiceCode:%s)", getStatus(), getProgress(), getName(), getMessage(), getDescription(), getStartTime(), getEndTime(), getServiceCode()); } private void updateChangedField(String fieldName) { if (_changedFields == null) { _changedFields = new HashSet<String>(); } _changedFields.add(fieldName); } /** * Checks to see whether the provided status is one of the defined valid * status(es). * * @param statusStr - Status String * @return true, if the provided status is valid. otherwise false. */ private boolean isValidStatus(String statusStr) { boolean valid = false; Status[] validStatus = Status.values(); for (Status status : validStatus) { if (status.name().toUpperCase().equals(statusStr.toUpperCase())) { valid = true; break; } } return valid; } /** * Verifies the Terminal state of the task status * @param status Status of task * @return True if status is error or ready. Else false */ public static boolean isTerminalState(Status status) { return (status == Status.ready || status == Status.error); } }