/*
* Licensed under the Apache 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://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*
* Contributions from 2013-2017 where performed either by US government
* employees, or under US Veterans Health Administration contracts.
*
* US Veterans Health Administration contributions by government employees
* are work of the U.S. Government and are not subject to copyright
* protection in the United States. Portions contributed by government
* employees are USGovWork (17USC ยง105). Not subject to copyright.
*
* Contribution by contractors to the US Veterans Health Administration
* during this period are contractually contributed under the
* Apache License, Version 2.0.
*
* See: https://www.usa.gov/government-works
*
* Contributions prior to 2013:
*
* Copyright (C) International Health Terminology Standards Development Organisation.
* Licensed under the Apache License, Version 2.0.
*
*/
package sh.isaac.provider.workflow.model.contents;
//~--- JDK imports ------------------------------------------------------------
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
//~--- non-JDK imports --------------------------------------------------------
import sh.isaac.api.commit.Stamp;
import sh.isaac.api.externalizable.ByteArrayDataBuffer;
import sh.isaac.provider.stamp.StampSerializer;
import sh.isaac.provider.workflow.BPMNInfo;
//~--- classes ----------------------------------------------------------------
/**
* The metadata defining a given process (or workflow instance). This doesn't
* include its Detail which is available via {@link ProcessHistory}
*
* {@link AbstractStorableWorkflowContents}
*
* @author <a href="mailto:jefron@westcoastinformatics.com">Jesse Efron</a>
*/
public class ProcessDetail
extends AbstractStorableWorkflowContents {
/**
* A map of all component nids modified within the workflow process to the time of initial modification.
* Therefore, if a component has been modified multiple times within a
* single process, only the first of those times are required on the map.
*/
private final Map<Integer, Stamp> componentToIntitialEditMap = new HashMap<>();
/** The time the workflow process was launched. */
private long timeLaunched = -1L;
/**
* The time the workflow process was finished (either via canceled Or
* Concluded).
*/
private long timeCanceledOrConcluded = -1L;
/** The workflow process's current "owner". */
private UUID ownerId = BPMNInfo.UNOWNED_PROCESS;
/** The stamp serializer. */
private final StampSerializer stampSerializer = new StampSerializer();
/** The workflow definition key for which the Process Detail is relevant. */
private UUID definitionId;
/** The user who originally defined (created) the workflow process. */
private UUID creatorId;
/** The time the workflow process was created. */
private long timeCreated;
/** The workflow process's status. */
private ProcessStatus status;
/** The workflow process's name. */
private String name;
/** The workflow process's description. */
private String description;
/** Definition uuid most significant bits. */
private long definitionIdMsb;
/** Definition uuid least significant bits. */
private long definitionIdLsb;
/** Creator uuid most significant bits. */
private long creatorIdMsb;
/** Creator uuid least significant bits. */
private long creatorIdLsb;
/** Owner uuid most significant bits. */
private long ownerIdMsb;
/** Owner uuid least significant bits. */
private long ownerIdLsb;
//~--- constant enums ------------------------------------------------------
/**
* The exhaustive list of possible ways an instantiated process may be ended.
*/
public enum EndWorkflowType {
/** Process is stopped without reaching a completed state. */
CANCELED,
/** Process has been finished by reaching a completed state. */
CONCLUDED
}
/**
* The exhaustive list of possible process statuses.
*/
public enum ProcessStatus {
/** Process is being defined and has yet to be launched. */
DEFINED,
/** Process has been launched. */
LAUNCHED,
/** A previously launched or defined process that has been canceled. */
CANCELED,
/** A previously launched process that is completed. */
CONCLUDED
}
;
//~--- constructors --------------------------------------------------------
/**
* Constructor for a new process based on serialized content.
*
* @param data
* The data to deserialize into its components
*/
public ProcessDetail(byte[] data) {
readData(new ByteArrayDataBuffer(data));
}
/**
* Constructor for a new process based on specified entry fields.
*
* @param definitionId the definition id
* @param creatorId the creator id
* @param timeCreated the time created
* @param status the status
* @param name the name
* @param description the description
*/
public ProcessDetail(UUID definitionId,
UUID creatorId,
long timeCreated,
ProcessStatus status,
String name,
String description) {
this.definitionId = definitionId;
this.creatorId = creatorId;
this.timeCreated = timeCreated;
this.status = status;
this.name = name;
this.description = description;
this.ownerId = creatorId;
this.definitionIdMsb = definitionId.getMostSignificantBits();
this.definitionIdLsb = definitionId.getLeastSignificantBits();
this.creatorIdMsb = creatorId.getMostSignificantBits();
this.creatorIdLsb = creatorId.getLeastSignificantBits();
this.ownerIdMsb = this.ownerId.getMostSignificantBits();
this.ownerIdLsb = this.ownerId.getLeastSignificantBits();
}
//~--- methods -------------------------------------------------------------
/**
* Equals.
*
* @param obj the obj
* @return true, if successful
*/
@Override
public boolean equals(Object obj) {
final ProcessDetail other = (ProcessDetail) obj;
return this.definitionId.equals(other.definitionId) &&
this.componentToIntitialEditMap.equals(other.componentToIntitialEditMap) &&
this.creatorId.equals(other.creatorId) &&
(this.timeCreated == other.timeCreated) &&
(this.timeLaunched == other.timeLaunched) &&
(this.timeCanceledOrConcluded == other.timeCanceledOrConcluded) &&
(this.status == other.status) &&
this.name.equals(other.name) &&
this.description.equals(other.description) &&
this.ownerId.equals(other.ownerId);
}
/**
* Hash code.
*
* @return the int
*/
@Override
public int hashCode() {
return this.definitionId.hashCode() + this.componentToIntitialEditMap.hashCode() + this.creatorId.hashCode() +
new Long(this.timeCreated).hashCode() + new Long(this.timeLaunched).hashCode() +
new Long(this.timeCanceledOrConcluded).hashCode() + this.status.hashCode() + this.name.hashCode() +
this.description.hashCode() + this.ownerId.hashCode();
}
/**
* To string.
*
* @return the string
*/
@Override
public String toString() {
final StringBuffer buf = new StringBuffer();
for (final Integer compNid: this.componentToIntitialEditMap.keySet()) {
buf.append("\n\t\t\tFor Component Nid: " + compNid + " first edited in workflow at Stamp: " +
this.componentToIntitialEditMap.get(compNid));
}
LocalDateTime date = LocalDateTime.from(Instant.ofEpochMilli(this.timeCreated)
.atZone(ZoneId.systemDefault()));
final String timeCreatedString = BPMNInfo.workflowDateFormatter.format(date);
date = LocalDateTime.from(Instant.ofEpochMilli(this.timeLaunched)
.atZone(ZoneId.systemDefault()));
final String timeLaunchedString = BPMNInfo.workflowDateFormatter.format(date);
date = LocalDateTime.from(Instant.ofEpochMilli(this.timeCanceledOrConcluded)
.atZone(ZoneId.systemDefault()));
final String timeCanceledOrConcludedString = BPMNInfo.workflowDateFormatter.format(date);
return "\n\t\tId: " + this.id + "\n\t\tDefinition Id: " + this.definitionId.toString() +
"\n\t\tComponents to Sequences Map: " + buf.toString() + "\n\t\tCreator Id: " +
this.creatorId.toString() + "\n\t\tTime Created: " + timeCreatedString + "\n\t\tTime Launched: " +
timeLaunchedString + "\n\t\tTime Canceled or Concluded: " + timeCanceledOrConcludedString +
"\n\t\tStatus: " + this.status + "\n\t\tName: " + this.name + "\n\t\tDescription: " + this.description +
"\n\t\tOwner Id: " + this.ownerId.toString();
}
/**
* Put additional workflow fields.
*
* @param out the out
*/
@Override
protected void putAdditionalWorkflowFields(ByteArrayDataBuffer out) {
out.putLong(this.definitionIdMsb);
out.putLong(this.definitionIdLsb);
out.putInt(this.componentToIntitialEditMap.size());
for (final Integer componentNid: this.componentToIntitialEditMap.keySet()) {
out.putNid(componentNid);
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
this.stampSerializer.serialize(new DataOutputStream(baos),
this.componentToIntitialEditMap.get(componentNid));
out.putByteArrayField(baos.toByteArray());
} catch (final IOException e) {
throw new RuntimeException(e);
}
}
out.putLong(this.creatorIdMsb);
out.putLong(this.creatorIdLsb);
out.putLong(this.timeCreated);
out.putLong(this.timeLaunched);
out.putLong(this.timeCanceledOrConcluded);
out.putByteArrayField(this.status.name()
.getBytes());
out.putByteArrayField(this.name.getBytes());
out.putByteArrayField(this.description.getBytes());
out.putLong(this.ownerIdMsb);
out.putLong(this.ownerIdLsb);
}
//~--- get methods ---------------------------------------------------------
/**
* Checks if active.
*
* @return true, if active
*/
public boolean isActive() {
return (this.status == ProcessStatus.LAUNCHED) || (this.status == ProcessStatus.DEFINED);
}
/**
* Gets the additional workflow fields.
*
* @param in the in
* @return the additional workflow fields
*/
@Override
protected void getAdditionalWorkflowFields(ByteArrayDataBuffer in) {
this.definitionIdMsb = in.getLong();
this.definitionIdLsb = in.getLong();
this.definitionId = new UUID(this.definitionIdMsb, this.definitionIdLsb);
final int collectionCount = in.getInt();
for (int i = 0; i < collectionCount; i++) {
final int compNid = in.getNid();
try (ByteArrayInputStream baos = new ByteArrayInputStream(in.getByteArrayField())) {
this.componentToIntitialEditMap.put(compNid, this.stampSerializer.deserialize(new DataInputStream(baos)));
} catch (final IOException e) {
throw new RuntimeException(e);
}
}
this.creatorIdMsb = in.getLong();
this.creatorIdLsb = in.getLong();
this.creatorId = new UUID(this.creatorIdMsb, this.creatorIdLsb);
this.timeCreated = in.getLong();
this.timeLaunched = in.getLong();
this.timeCanceledOrConcluded = in.getLong();
this.status = ProcessStatus.valueOf(new String(in.getByteArrayField()));
this.name = new String(in.getByteArrayField());
this.description = new String(in.getByteArrayField());
this.ownerIdMsb = in.getLong();
this.ownerIdLsb = in.getLong();
this.ownerId = new UUID(this.ownerIdMsb, this.ownerIdLsb);
}
/**
* Checks if canceled.
*
* @return true, if canceled
*/
public boolean isCanceled() {
return this.status == ProcessStatus.CANCELED;
}
/**
* Gets the process's component nids.
*
* @return map of component nids to ordered stamp sequences
*/
public Map<Integer, Stamp> getComponentToInitialEditMap() {
return this.componentToIntitialEditMap;
}
/**
* Checks if concluded.
*
* @return true, if concluded
*/
public boolean isConcluded() {
return this.status == ProcessStatus.CONCLUDED;
}
/**
* Gets the process creator.
*
* @return the process creator's id
*/
public UUID getCreatorId() {
return this.creatorId;
}
/**
* Gets the definition Id associated with the process.
*
* @return the key of the definition from which the process is created
*/
public UUID getDefinitionId() {
return this.definitionId;
}
/**
* The description of the process.
*
* @return process description
*/
public String getDescription() {
return this.description;
}
/**
* The name of the process.
*
* @return process name
*/
public String getName() {
return this.name;
}
/**
* Retrieves the current owner of the process.
*
* @return 0-based UUID: Process is not owned.
* Otherwise, return the process's current owner id
*/
public UUID getOwnerId() {
return this.ownerId;
}
//~--- set methods ---------------------------------------------------------
/**
* Sets the current owner of the process.
*
* @param nid The userId obtaining a lock on the instance. Note '0' means no owner.
*/
public void setOwnerId(UUID nid) {
this.ownerId = nid;
this.ownerIdMsb = this.ownerId.getMostSignificantBits();
this.ownerIdLsb = this.ownerId.getLeastSignificantBits();
}
//~--- get methods ---------------------------------------------------------
/**
* Gets the process's current status.
*
* @return the process Status
*/
public ProcessStatus getStatus() {
return this.status;
}
//~--- set methods ---------------------------------------------------------
/**
* Sets the process's current status as this is updated over the course of
* the process.
*
* @param status
* The process's current status
*/
public void setStatus(ProcessStatus status) {
this.status = status;
}
//~--- get methods ---------------------------------------------------------
/**
* Gets the time the process ended either via cancelation or conclusion as
* long primitive type.
*
* @return the time the process was canceled or concluded
*/
public long getTimeCanceledOrConcluded() {
return this.timeCanceledOrConcluded;
}
//~--- set methods ---------------------------------------------------------
/**
* Sets the time the process ended either via cancelation or conclusion as
* this isn't available during the object's construction.
*
* @param time
* The time the process was canceled/concluded as long primitive
* type
*/
public void setTimeCanceledOrConcluded(long time) {
this.timeCanceledOrConcluded = time;
}
//~--- get methods ---------------------------------------------------------
/**
* Gets the time the process was created as long primitive type.
*
* @return the time the process was created
*/
public long getTimeCreated() {
return this.timeCreated;
}
/**
* Get the time the process was launched as long primitive type.
*
* @return the time launched
*/
public long getTimeLaunched() {
return this.timeLaunched;
}
//~--- set methods ---------------------------------------------------------
/**
* Sets the time the process as launched as this isn't available during the
* object's construction.
*
* @param time
* The time the process was launched as long primitive type
*/
public void setTimeLaunched(long time) {
this.timeLaunched = time;
}
}