/** * Licensed to The Apereo Foundation under one or more contributor license * agreements. See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * * The Apereo Foundation licenses this file to you under the Educational * Community License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of the License * at: * * http://opensource.org/licenses/ecl2.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. * */ package org.opencastproject.workflow.api; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.TreeSet; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; import javax.xml.bind.annotation.adapters.XmlAdapter; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; /** * A JAXB-annotated implementation of {@link WorkflowOperationInstance} */ @XmlType(name = "operation-instance", namespace = "http://workflow.opencastproject.org") @XmlRootElement(name = "operation-instance", namespace = "http://workflow.opencastproject.org") @XmlAccessorType(XmlAccessType.NONE) public class WorkflowOperationInstanceImpl implements WorkflowOperationInstance { static class DateAdapter extends XmlAdapter<Long, Date> { /** * {@inheritDoc} * * @see javax.xml.bind.annotation.adapters.XmlAdapter#marshal(java.lang.Object) */ @Override public Long marshal(Date v) throws Exception { return v == null ? null : v.getTime(); } /** * {@inheritDoc} * * @see javax.xml.bind.annotation.adapters.XmlAdapter#unmarshal(java.lang.Object) */ @Override public Date unmarshal(Long v) throws Exception { return v == null ? null : new Date(v); } } @XmlAttribute(name = "id") protected String template; @XmlAttribute(name = "job") protected Long jobId; @XmlAttribute(name = "state") protected OperationState state; @XmlAttribute(name = "description") protected String description; @XmlElement(name = "configuration") @XmlElementWrapper(name = "configurations") protected Set<WorkflowConfiguration> configurations; @XmlElement(name = "holdurl") protected String holdStateUserInterfaceUrl; @XmlElement(name = "hold-action-title") protected String holdActionTitle; @XmlAttribute(name = "fail-on-error") protected boolean failWorkflowOnException; @XmlAttribute(name = "if") protected String executeCondition; @XmlAttribute(name = "unless") protected String skipCondition; @XmlAttribute(name = "exception-handler-workflow") protected String exceptionHandlingWorkflow; @XmlAttribute(name = "abortable") protected Boolean abortable; @XmlAttribute(name = "continuable") protected Boolean continuable; @XmlJavaTypeAdapter(WorkflowOperationInstanceImpl.DateAdapter.class) @XmlElement(name = "started") protected Date dateStarted; @XmlJavaTypeAdapter(WorkflowOperationInstanceImpl.DateAdapter.class) @XmlElement(name = "completed") protected Date dateCompleted; @XmlElement(name = "time-in-queue") protected Long timeInQueue; @XmlAttribute(name = "max-attempts") protected int maxAttempts; @XmlAttribute(name = "failed-attempts") protected int failedAttempts; @XmlAttribute(name = "execution-host") protected String executionHost; @XmlElementWrapper(name = "execution-history") @XmlElement(name = "execution-history-entry") protected List<Long> executionHistory = new ArrayList<Long>(); @XmlJavaTypeAdapter(RetryStrategy.Adapter.class) @XmlAttribute(name = "retry-strategy") protected RetryStrategy retryStrategy; /** The position of this operation in the workflow instance */ protected int position; /** * No-arg constructor needed for JAXB serialization */ public WorkflowOperationInstanceImpl() { this.maxAttempts = 1; this.retryStrategy = RetryStrategy.NONE; } /** * Builds a new workflow operation instance based on another workflow operation. * * @param def * the workflow definition * @param position * the operation's position within the workflow */ public WorkflowOperationInstanceImpl(WorkflowOperationDefinition def, int position) { this(); this.position = position; setTemplate(def.getId()); setState(OperationState.INSTANTIATED); setDescription(def.getDescription()); setMaxAttempts(def.getMaxAttempts()); setFailWorkflowOnException(def.isFailWorkflowOnException()); setExceptionHandlingWorkflow(def.getExceptionHandlingWorkflow()); setExecutionCondition(def.getExecutionCondition()); setSkipCondition(def.getSkipCondition()); setRetryStrategy(def.getRetryStrategy()); Set<String> defConfigs = def.getConfigurationKeys(); this.configurations = new TreeSet<WorkflowConfiguration>(); if (defConfigs != null) { for (String key : defConfigs) { configurations.add(new WorkflowConfigurationImpl(key, def.getConfiguration(key))); } } switch (retryStrategy) { case RETRY: if (maxAttempts < 2) maxAttempts = 2; break; case HOLD: maxAttempts = -1; break; default: // Nothing to do break; } } /** * Constructs a new operation instance with the given id and initial state. * * @param id * the operation id * @param state * the state */ public WorkflowOperationInstanceImpl(String id, OperationState state) { this(); setTemplate(id); setState(state); } /** * {@inheritDoc} * * @see org.opencastproject.workflow.api.WorkflowOperationInstance#getId() */ @Override public Long getId() { return jobId; } /** * Sets the job identifier * * @param jobId * the job identifier */ @Override public void setId(Long jobId) { this.jobId = jobId; } /** * {@inheritDoc} * * @see org.opencastproject.workflow.api.WorkflowOperationInstance#getTemplate() */ @Override public String getTemplate() { return template; } /** * Sets the template * * @param template * the template */ public void setTemplate(String template) { this.template = template; } @Override public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public void setExecutionCondition(String condition) { this.executeCondition = condition; } @Override public String getExecutionCondition() { return executeCondition; } public void setSkipCondition(String condition) { this.skipCondition = condition; } @Override public String getSkipCondition() { return skipCondition; } static class Adapter extends XmlAdapter<WorkflowOperationInstanceImpl, WorkflowOperationInstance> { @Override public WorkflowOperationInstanceImpl marshal(WorkflowOperationInstance op) throws Exception { return (WorkflowOperationInstanceImpl) op; } @Override public WorkflowOperationInstance unmarshal(WorkflowOperationInstanceImpl op) throws Exception { return op; } } /** * {@inheritDoc} * * @see org.opencastproject.workflow.api.WorkflowOperationInstance#getState() */ @Override public OperationState getState() { return state; } @Override public void setState(OperationState state) { Date now = new Date(); if (OperationState.RUNNING.equals(state)) { this.dateStarted = now; } else if (OperationState.FAILED.equals(state)) { this.dateCompleted = now; } else if (OperationState.SUCCEEDED.equals(state)) { this.dateCompleted = now; } this.state = state; } public Set<WorkflowConfiguration> getConfigurations() { return configurations; } public void setConfiguration(Set<WorkflowConfiguration> configurations) { this.configurations = configurations; } /** * {@inheritDoc} * * @see org.opencastproject.workflow.api.WorkflowInstance#getConfiguration(java.lang.String) */ @Override public String getConfiguration(String key) { if (key == null || configurations == null) return null; for (WorkflowConfiguration config : configurations) { if (config.getKey().equals(key)) return config.getValue(); } return null; } /** * {@inheritDoc} * * @see org.opencastproject.workflow.api.WorkflowInstance#removeConfiguration(java.lang.String) */ @Override public void removeConfiguration(String key) { if (key == null || configurations == null) return; for (Iterator<WorkflowConfiguration> configIter = configurations.iterator(); configIter.hasNext();) { WorkflowConfiguration config = configIter.next(); if (config.getKey().equals(key)) { configIter.remove(); return; } } } /** * {@inheritDoc} * * @see org.opencastproject.workflow.api.WorkflowInstance#setConfiguration(java.lang.String, java.lang.String) */ @Override public void setConfiguration(String key, String value) { if (key == null) return; if (configurations == null) configurations = new TreeSet<WorkflowConfiguration>(); for (WorkflowConfiguration config : configurations) { if (config.getKey().equals(key)) { ((WorkflowConfigurationImpl) config).setValue(value); return; } } // No configurations were found, so add a new one configurations.add(new WorkflowConfigurationImpl(key, value)); } /** * {@inheritDoc} * * @see org.opencastproject.workflow.api.WorkflowOperationInstance#getConfigurationKeys() */ @Override public Set<String> getConfigurationKeys() { Set<String> keys = new TreeSet<String>(); if (configurations != null && !configurations.isEmpty()) { for (WorkflowConfiguration config : configurations) { keys.add(config.getKey()); } } return keys; } /** * @return the holdStateUserInterfaceUrl */ @Override public String getHoldStateUserInterfaceUrl() { return holdStateUserInterfaceUrl; } /** * @param holdStateUserInterfaceUrl * the holdStateUserInterfaceUrl to set */ public void setHoldStateUserInterfaceUrl(String holdStateUserInterfaceUrl) { this.holdStateUserInterfaceUrl = holdStateUserInterfaceUrl; } /** * Set the title for the link to this operations hold state UI, a default String if no title is set. */ public void setHoldActionTitle(String title) { this.holdActionTitle = title; } /** * Returns the title for the link to this operations hold state UI, a default String if no title is set. * * @return title to be displayed */ @Override public String getHoldActionTitle() { return holdActionTitle; } /** * {@inheritDoc} * * @see org.opencastproject.workflow.api.WorkflowOperationInstance#getExceptionHandlingWorkflow() */ @Override public String getExceptionHandlingWorkflow() { return exceptionHandlingWorkflow; } /** * {@inheritDoc} * * @see org.opencastproject.workflow.api.WorkflowOperationInstance#isFailWorkflowOnException() */ @Override public boolean isFailWorkflowOnException() { return failWorkflowOnException; } /** * @param failWorkflowOnException * the failWorkflowOnException to set */ public void setFailWorkflowOnException(boolean failWorkflowOnException) { this.failWorkflowOnException = failWorkflowOnException; } /** * @param exceptionHandlingWorkflow * the exceptionHandlingWorkflow to set */ public void setExceptionHandlingWorkflow(String exceptionHandlingWorkflow) { this.exceptionHandlingWorkflow = exceptionHandlingWorkflow; } /** * @return the dateStarted */ @Override public Date getDateStarted() { return dateStarted; } /** * @param dateStarted * the dateStarted to set */ public void setDateStarted(Date dateStarted) { this.dateStarted = dateStarted; } /** * @return the dateCompleted */ @Override public Date getDateCompleted() { return dateCompleted; } /** * @param dateCompleted * the dateCompleted to set */ public void setDateCompleted(Date dateCompleted) { this.dateCompleted = dateCompleted; } /** * {@inheritDoc} * * @see org.opencastproject.workflow.api.WorkflowOperationInstance#getTimeInQueue() */ @Override public Long getTimeInQueue() { return timeInQueue; } /** * @param timeInQueue * the timeInQueue to set */ public void setTimeInQueue(long timeInQueue) { this.timeInQueue = timeInQueue; } /** * {@inheritDoc} * * @see org.opencastproject.workflow.api.WorkflowOperationInstance#getPosition() */ @Override public int getPosition() { return position; } /** * Sets the workflow operation's position within the workflow. * * @param position * the position */ public void setPosition(int position) { this.position = position; } /** * {@inheritDoc} * * @see java.lang.Object#hashCode() */ @Override public int hashCode() { return position; } /** * {@inheritDoc} * * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object o) { if (this == o) return true; if (o instanceof WorkflowOperationInstance) { WorkflowOperationInstance other = (WorkflowOperationInstance) o; return other.getTemplate().equals(this.getTemplate()) && other.getPosition() == this.position; } else { return false; } } /** * {@inheritDoc} * * @see java.lang.Object#toString() */ @Override public String toString() { return "operation:'" + template + "', position:" + position + ", state:'" + this.state + "'"; } /** * {@inheritDoc} * * @see org.opencastproject.workflow.api.WorkflowOperationInstance#isAbortable() */ @Override public Boolean isAbortable() { return abortable; } /** * Defines whether this operation instance should be abortable from a hold state. * * @param abortable * <code>true</code> to allow the user to cancel the operation */ public void setAbortable(Boolean abortable) { this.abortable = abortable; } /** * {@inheritDoc} * * @see org.opencastproject.workflow.api.WorkflowOperationInstance#isContinuable() */ @Override public Boolean isContinuable() { return continuable; } /** * Defines whether this operation instance should be continuable from a hold state or whether it is resumed * automatically. * * @param continuable * <code>true</code> to allow the user to resume the operation */ public void setContinuable(Boolean continuable) { this.continuable = continuable; } /** * {@inheritDoc} * * @see org.opencastproject.workflow.api.WorkflowOperationInstance#getFailedAttempts() */ @Override public int getFailedAttempts() { return failedAttempts; } /** * @param failedAttempts * the failedAttempts to set */ public void setFailedAttempts(int failedAttempts) { this.failedAttempts = failedAttempts; } /** * {@inheritDoc} * * @see org.opencastproject.workflow.api.WorkflowOperationInstance#getRetryStrategy() */ @Override public RetryStrategy getRetryStrategy() { return retryStrategy; } /** * @param retryStrategy * the retry strategy */ public void setRetryStrategy(RetryStrategy retryStrategy) { this.retryStrategy = retryStrategy; } /** * {@inheritDoc} * * @see org.opencastproject.workflow.api.WorkflowOperationInstance#getExecutionHost() */ @Override public String getExecutionHost() { return executionHost; } /** * {@inheritDoc} * * @see org.opencastproject.workflow.api.WorkflowOperationInstance#setExecutionHost(java.lang.String) */ @Override public void setExecutionHost(String executionHost) { this.executionHost = executionHost; } /** * {@inheritDoc} * * @see org.opencastproject.workflow.api.WorkflowOperationInstance#getExecutionHistory() */ @Override public List<Long> getExecutionHistory() { return executionHistory; } /** * {@inheritDoc} * * @see org.opencastproject.workflow.api.WorkflowOperationInstance#addToExecutionHistory(long) */ @Override public void addToExecutionHistory(long jobId) { executionHistory.add(jobId); } /** * {@inheritDoc} * * @see org.opencastproject.workflow.api.WorkflowOperationInstance#getMaxAttempts() */ @Override public int getMaxAttempts() { return maxAttempts; } /** * @param maxAttempts * the maxAttempts to set * @throws IllegalArgumentException * if maxAttempts is less than one. */ public void setMaxAttempts(int maxAttempts) { if (maxAttempts < 1) { throw new IllegalArgumentException("maxAttempts must be >=1"); } this.maxAttempts = maxAttempts; } }