Java tutorial
/************************************************************************* * Copyright 2009-2015 Eucalyptus Systems, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses/. * * Please contact Eucalyptus Systems, Inc., 6755 Hollister Ave., Goleta * CA 93117, USA or visit http://www.eucalyptus.com/licenses/ if you need * additional information or have any questions. ************************************************************************/ package com.eucalyptus.simpleworkflow; import static com.eucalyptus.simpleworkflow.common.SimpleWorkflowMetadata.WorkflowExecutionMetadata; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.concurrent.TimeUnit; import javax.annotation.Nullable; import javax.persistence.CascadeType; import javax.persistence.CollectionTable; import javax.persistence.Column; import javax.persistence.ElementCollection; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; import javax.persistence.FetchType; import javax.persistence.JoinColumn; import javax.persistence.Lob; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; import javax.persistence.OrderBy; import javax.persistence.OrderColumn; import javax.persistence.PersistenceContext; import javax.persistence.PrePersist; import javax.persistence.PreUpdate; import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; import org.hibernate.annotations.Type; import com.eucalyptus.component.ComponentIds; import com.eucalyptus.entities.UserMetadata; import com.eucalyptus.simpleworkflow.common.SimpleWorkflow; import com.eucalyptus.simpleworkflow.common.SimpleWorkflowMetadatas; import com.eucalyptus.simpleworkflow.common.model.WorkflowEventAttributes; import com.eucalyptus.util.CollectionUtils; import com.eucalyptus.auth.principal.FullName; import com.eucalyptus.auth.principal.OwnerFullName; import com.google.common.base.Functions; import com.google.common.base.Predicates; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; /** * */ @Entity @PersistenceContext(name = "eucalyptus_simpleworkflow") @Table(name = "swf_workflow_execution") @Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL) public class WorkflowExecution extends UserMetadata<WorkflowExecution.ExecutionStatus> implements WorkflowExecutionMetadata { private static final long serialVersionUID = 1L; public enum ExecutionStatus { Open, Closed,; public String toString() { return name().toUpperCase(); } } public enum CloseStatus { Completed, Failed, Canceled, Terminated, Continued_As_New, Timed_Out; public String toString() { return name().toUpperCase(); } public static CloseStatus fromString(final String value) { return Iterables .tryFind(Arrays.asList(values()), CollectionUtils.propertyPredicate(value, Functions.toStringFunction())) .or(CloseStatus.Completed); } } public enum DecisionStatus { Idle, Pending, Active,; } @ManyToOne @JoinColumn(name = "domain_id", nullable = false, updatable = false) @Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL) private Domain domain; @ManyToOne @JoinColumn(name = "workflow_type_id", nullable = false, updatable = false) @Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL) private WorkflowType workflowType; @Column(name = "workflow_id", length = 256, nullable = false, updatable = false) private String workflowId; @Column(name = "child_policy", nullable = false, updatable = false) private String childPolicy; @Column(name = "domain", length = 256, nullable = false, updatable = false) private String domainName; @Column(name = "domain_uuid", nullable = false, updatable = false) private String domainUuid; @Column(name = "task_list", length = 256, nullable = false, updatable = false) private String taskList; @Column(name = "exec_start_to_close_timeout", nullable = false, updatable = false) private Integer executionStartToCloseTimeout; @Column(name = "task_start_to_close_timeout", updatable = false) private Integer taskStartToCloseTimeout; @Column(name = "cancel_requested", nullable = false) private Boolean cancelRequested; @Column(name = "decision_status") @Enumerated(EnumType.STRING) private DecisionStatus decisionStatus; @Column(name = "decision_timestamp", nullable = false) @Temporal(TemporalType.TIMESTAMP) private Date decisionTimestamp; @Column(name = "close_status") @Enumerated(EnumType.STRING) private CloseStatus closeStatus; @Column(name = "close_timestamp") @Temporal(TemporalType.TIMESTAMP) private Date closeTimestamp; @Column(name = "retention_timestamp") @Temporal(TemporalType.TIMESTAMP) private Date retentionTimestamp; @ElementCollection @CollectionTable(name = "swf_workflow_execution_tags") @Column(name = "tag", length = 256) @JoinColumn(name = "workflow_execution_id") @OrderColumn(name = "tag_index") @Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL) private List<String> tagList; @Column(name = "latest_activity_timestamp") @Temporal(TemporalType.TIMESTAMP) private Date latestActivityTaskScheduled; @Column(name = "latest_execution_context") @Lob @Type(type = "org.hibernate.type.StringClobType") private String latestExecutionContext; @Column(name = "timeout_timestamp") @Temporal(TemporalType.TIMESTAMP) private Date timeoutTimestamp; @OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.REMOVE }, orphanRemoval = true, mappedBy = "workflowExecution") @OrderBy("eventOrder") private List<WorkflowHistoryEvent> workflowHistory; @OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.REMOVE }, orphanRemoval = true, mappedBy = "workflowExecution") @OrderColumn(name = "scheduled_event_id") private List<ActivityTask> activityTasks; @OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.REMOVE }, orphanRemoval = true, mappedBy = "workflowExecution") @OrderColumn(name = "decision_task_comp_event_id") private List<Timer> timers; protected WorkflowExecution() { } protected WorkflowExecution(final OwnerFullName owner, final String displayName) { super(owner, displayName); } public static WorkflowExecution create(final OwnerFullName owner, final String name, /* runId */ final Domain domain, final WorkflowType workflowType, final String workflowId, final String childPolicy, final String taskList, @Nullable final Integer executionStartToCloseTimeout, @Nullable final Integer taskStartToCloseTimeout, final List<String> tags, final List<WorkflowEventAttributes> eventAttributes) { final WorkflowExecution workflowExecution = new WorkflowExecution(owner, name); workflowExecution.setDomain(domain); workflowExecution.setDomainName(domain.getDisplayName()); workflowExecution.setDomainUuid(domain.getNaturalId()); workflowExecution.setWorkflowType(workflowType); workflowExecution.setWorkflowId(workflowId); workflowExecution.setState(ExecutionStatus.Open); workflowExecution.setChildPolicy(childPolicy); workflowExecution.setTaskList(taskList); workflowExecution.setExecutionStartToCloseTimeout(executionStartToCloseTimeout); workflowExecution.setTaskStartToCloseTimeout(taskStartToCloseTimeout); workflowExecution.setTagList(tags); workflowExecution.setCancelRequested(false); workflowExecution.setDecisionStatus(DecisionStatus.Pending); workflowExecution.setDecisionTimestamp(new Date()); workflowExecution.setWorkflowHistory(Lists.<WorkflowHistoryEvent>newArrayList()); for (final WorkflowEventAttributes attributes : eventAttributes) { workflowExecution.addHistoryEvent(WorkflowHistoryEvent.create(workflowExecution, attributes)); } return workflowExecution; } public Date calculateNextTimeout() { final Long timeout = CollectionUtils.reduce( Iterables.filter( Lists.newArrayList(toTimeout(getCreationTimestamp(), getExecutionStartToCloseTimeout()), toTimeout(getDecisionTimestamp(), getDecisionStatus() == DecisionStatus.Active ? getTaskStartToCloseTimeout() : null)), Predicates.notNull()), Long.MAX_VALUE, CollectionUtils.lmin()); return timeout == Long.MAX_VALUE ? null : new Date(timeout); } public boolean isWorkflowTimedOut(final long timestamp, final long maximumDurationMillis) { final Long timeout = toTimeout(getCreationTimestamp(), getExecutionStartToCloseTimeout()); return (timeout != null && timeout < timestamp) || (maximumDurationMillis > 0 && (getCreationTimestamp().getTime() + maximumDurationMillis) < timestamp); } private static Long toTimeout(final Date from, final Integer period) { return period == null ? null : from.getTime() + TimeUnit.SECONDS.toMillis(period); } public static WorkflowExecution exampleWithOwner(final OwnerFullName owner) { return new WorkflowExecution(owner, null); } public static WorkflowExecution exampleWithName(final OwnerFullName owner, final String name) { return new WorkflowExecution(owner, name); } public static WorkflowExecution exampleWithPendingDecision(final OwnerFullName owner, final String domain, final String taskList) { final WorkflowExecution workflowExecution = new WorkflowExecution(owner, null); workflowExecution.setDomainName(domain); workflowExecution.setTaskList(taskList); workflowExecution.setDecisionStatus(DecisionStatus.Pending); workflowExecution.setState(ExecutionStatus.Open); workflowExecution.setStateChangeStack(null); workflowExecution.setLastState(null); return workflowExecution; } public static WorkflowExecution exampleWithUniqueName(final OwnerFullName owner, final String domain, final String runId) { final WorkflowExecution workflowExecution = new WorkflowExecution(owner, runId); workflowExecution.setUniqueName(createUniqueName(owner.getAccountNumber(), domain, runId)); return workflowExecution; } public static WorkflowExecution exampleForOpenWorkflow() { return exampleForOpenWorkflow(null, null, null); } public static WorkflowExecution exampleForOpenWorkflow(final OwnerFullName owner, final String domain, final String workflowId) { return exampleForOpenWorkflow(owner, domain, workflowId, null); } public static WorkflowExecution exampleForOpenWorkflow(final OwnerFullName owner, final String domain, final String workflowId, final String runId) { final WorkflowExecution workflowExecution = new WorkflowExecution(owner, runId); workflowExecution.setDomainName(domain); workflowExecution.setWorkflowId(workflowId); workflowExecution.setState(ExecutionStatus.Open); workflowExecution.setStateChangeStack(null); workflowExecution.setLastState(null); return workflowExecution; } public static WorkflowExecution exampleForClosedWorkflow() { return exampleForClosedWorkflow(null, null, null); } public static WorkflowExecution exampleForClosedWorkflow(final OwnerFullName owner, final String domain, final String workflowId) { final WorkflowExecution workflowExecution = new WorkflowExecution(owner, null); workflowExecution.setDomainName(domain); workflowExecution.setWorkflowId(workflowId); workflowExecution.setState(ExecutionStatus.Closed); workflowExecution.setStateChangeStack(null); workflowExecution.setLastState(null); return workflowExecution; } @Override protected String createUniqueName() { return createUniqueName(getOwnerAccountNumber(), SimpleWorkflowMetadatas.toDisplayName().apply(getDomain()), getDisplayName()); } private static String createUniqueName(final String accountNumber, final String domain, final String runId) { return accountNumber + ":" + domain + ":" + runId; } public Long addHistoryEvent(final WorkflowHistoryEvent event) throws WorkflowHistorySizeLimitException { // Order would be filled in on save, but we may need the event // identifier before the entity is stored event.setEventOrder((long) workflowHistory.size()); workflowHistory.add(event); if (workflowHistory.size() > SimpleWorkflowProperties.getWorkflowExecutionHistorySize()) { throw new WorkflowHistorySizeLimitException(this); } updateTimeStamps(); // ensure workflow version incremented return event.getEventId(); } public void closeWorkflow(final CloseStatus closeStatus, final WorkflowHistoryEvent event) { setState(WorkflowExecution.ExecutionStatus.Closed); setCloseStatus(closeStatus); setCloseTimestamp(new Date()); setRetentionTimestamp(new Date(getCloseTimestamp().getTime() + TimeUnit.DAYS.toMillis(getDomain().getWorkflowExecutionRetentionPeriodInDays()))); addHistoryEvent(event); } @Override public String getPartition() { return "eucalyptus"; } @Override public FullName getFullName() { return FullName.create.vendor("euca").region(ComponentIds.lookup(SimpleWorkflow.class).name()) .namespace(this.getOwnerAccountNumber()).relativeId("domain", SimpleWorkflowMetadatas.toDisplayName().apply(getDomain()), "run-id", getDisplayName()); } public Domain getDomain() { return domain; } public void setDomain(final Domain domain) { this.domain = domain; } public WorkflowType getWorkflowType() { return workflowType; } public void setWorkflowType(final WorkflowType workflowType) { this.workflowType = workflowType; } public String getWorkflowId() { return workflowId; } public void setWorkflowId(final String workflowId) { this.workflowId = workflowId; } public String getChildPolicy() { return childPolicy; } public void setChildPolicy(final String childPolicy) { this.childPolicy = childPolicy; } public String getDomainName() { return domainName; } public void setDomainName(final String domainName) { this.domainName = domainName; } public String getDomainUuid() { return domainUuid; } public void setDomainUuid(final String domainUuid) { this.domainUuid = domainUuid; } public String getTaskList() { return taskList; } public void setTaskList(final String taskList) { this.taskList = taskList; } public Integer getExecutionStartToCloseTimeout() { return executionStartToCloseTimeout; } public void setExecutionStartToCloseTimeout(final Integer executionStartToCloseTimeout) { this.executionStartToCloseTimeout = executionStartToCloseTimeout; } public Integer getTaskStartToCloseTimeout() { return taskStartToCloseTimeout; } public void setTaskStartToCloseTimeout(final Integer taskStartToCloseTimeout) { this.taskStartToCloseTimeout = taskStartToCloseTimeout; } public Boolean getCancelRequested() { return cancelRequested; } public void setCancelRequested(final Boolean cancelRequested) { this.cancelRequested = cancelRequested; } public DecisionStatus getDecisionStatus() { return decisionStatus; } public void setDecisionStatus(final DecisionStatus decisionStatus) { this.decisionStatus = decisionStatus; } public Date getDecisionTimestamp() { return decisionTimestamp; } public void setDecisionTimestamp(final Date decisionTimestamp) { this.decisionTimestamp = decisionTimestamp; } public CloseStatus getCloseStatus() { return closeStatus; } public void setCloseStatus(final CloseStatus closeStatus) { this.closeStatus = closeStatus; } public Date getCloseTimestamp() { return closeTimestamp; } public void setCloseTimestamp(final Date closeTimestamp) { this.closeTimestamp = closeTimestamp; } public Date getRetentionTimestamp() { return retentionTimestamp; } public void setRetentionTimestamp(final Date retentionTimestamp) { this.retentionTimestamp = retentionTimestamp; } public List<String> getTagList() { return tagList; } public void setTagList(final List<String> tagList) { this.tagList = tagList; } public Date getLatestActivityTaskScheduled() { return latestActivityTaskScheduled; } public void setLatestActivityTaskScheduled(final Date latestActivityTaskScheduled) { this.latestActivityTaskScheduled = latestActivityTaskScheduled; } public String getLatestExecutionContext() { return latestExecutionContext; } public void setLatestExecutionContext(final String latestExecutionContext) { this.latestExecutionContext = latestExecutionContext; } public Date getTimeoutTimestamp() { return timeoutTimestamp; } public void setTimeoutTimestamp(final Date timeoutTimestamp) { this.timeoutTimestamp = timeoutTimestamp; } public List<WorkflowHistoryEvent> getWorkflowHistory() { return workflowHistory; } public void setWorkflowHistory(final List<WorkflowHistoryEvent> workflowHistory) { this.workflowHistory = workflowHistory; } @PreUpdate @PrePersist protected void updateTimeout() { updateTimeStamps(); setTimeoutTimestamp(calculateNextTimeout()); } public static final class WorkflowHistorySizeLimitException extends RuntimeException { private static final long serialVersionUID = 1L; private final String accountNumber; private final String domain; private final String runId; private final String workflowId; public WorkflowHistorySizeLimitException(final WorkflowExecution workflowExecution) { this(workflowExecution.getOwnerAccountNumber(), workflowExecution.getDomainName(), workflowExecution.getDisplayName(), workflowExecution.getWorkflowId()); } public WorkflowHistorySizeLimitException(final String accountNumber, final String domain, final String runId, final String workflowId) { this.accountNumber = accountNumber; this.domain = domain; this.runId = runId; this.workflowId = workflowId; } public String getAccountNumber() { return accountNumber; } public String getDomain() { return domain; } public String getRunId() { return runId; } public String getWorkflowId() { return workflowId; } } }