package org.jbpm.persistence.processinstance;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.Lob;
import javax.persistence.PreUpdate;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.persistence.Version;
import org.drools.common.InternalKnowledgeRuntime;
import org.drools.common.InternalRuleBase;
import org.drools.impl.InternalKnowledgeBase;
import org.drools.impl.StatefulKnowledgeSessionImpl;
import org.drools.marshalling.impl.MarshallerReaderContext;
import org.drools.marshalling.impl.MarshallerWriteContext;
import org.drools.runtime.Environment;
import org.drools.runtime.process.ProcessInstance;
import org.jbpm.marshalling.impl.ProcessInstanceMarshaller;
import org.jbpm.marshalling.impl.ProcessMarshallerRegistry;
import org.jbpm.process.instance.impl.ProcessInstanceImpl;
@Entity
@Table(name = "JBPM_PROCESSINSTANCE_INFO")
public class ProcessInstanceInfo {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "InstanceId")
private Long processInstanceId;
@Version
@Column(name = "OPTLOCK")
private int version;
private String processId;
private Date startDate;
private Date lastReadDate;
private Date lastModificationDate;
private int state;
// TODO How do I mark a process instance info as dirty when the process
// instance has changed (so that byte array is regenerated and saved) ?
@Lob
private byte[] processInstanceByteArray;
// @OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
// @JoinColumn(name = "processInstanceId")
// private Set<EventType> eventTypes = new HashSet<EventType>();
@ElementCollection
@CollectionTable(name = "JBPM_PROCESSINSTANCE_INFO_EVENTTYPES", joinColumns = @JoinColumn(name = "JBPM_PROCESSINSTANCE_INFO_ID"))
private final Set<String> eventTypes = new HashSet<String>();
@Transient
private ProcessInstance processInstance;
@Transient
private Environment env;
protected ProcessInstanceInfo() {
}
public ProcessInstanceInfo(final ProcessInstance processInstance) {
this.processInstance = processInstance;
this.processId = processInstance.getProcessId();
startDate = new Date();
}
public ProcessInstanceInfo(final ProcessInstance processInstance,
final Environment env) {
this(processInstance);
this.env = env;
}
public Long getId() {
return processInstanceId;
}
public void setId(final Long processInstanceId) {
this.processInstanceId = processInstanceId;
}
public String getProcessId() {
return processId;
}
public Date getStartDate() {
return startDate;
}
public Date getLastModificationDate() {
return lastModificationDate;
}
public Date getLastReadDate() {
return lastReadDate;
}
public void updateLastReadDate() {
lastReadDate = new Date();
}
public int getState() {
return state;
}
public ProcessInstance getProcessInstance(
final InternalKnowledgeRuntime kruntime, final Environment env) {
this.env = env;
if (processInstance == null) {
try {
final ByteArrayInputStream bais = new ByteArrayInputStream(
processInstanceByteArray);
final MarshallerReaderContext context = new MarshallerReaderContext(
bais,
(InternalRuleBase) ((InternalKnowledgeBase) kruntime
.getKnowledgeBase()).getRuleBase(), null, null,
this.env);
final ProcessInstanceMarshaller marshaller = getMarshallerFromContext(context);
context.wm = ((StatefulKnowledgeSessionImpl) kruntime)
.getInternalWorkingMemory();
processInstance = marshaller.readProcessInstance(context);
context.close();
} catch (final IOException e) {
e.printStackTrace();
throw new IllegalArgumentException(
"IOException while loading process instance: "
+ e.getMessage(), e);
}
}
return processInstance;
}
private ProcessInstanceMarshaller getMarshallerFromContext(
final MarshallerReaderContext context) throws IOException {
final ObjectInputStream stream = context.stream;
final String processInstanceType = stream.readUTF();
return ProcessMarshallerRegistry.INSTANCE
.getMarshaller(processInstanceType);
}
private void saveProcessInstanceType(final MarshallerWriteContext context,
final ProcessInstance processInstance,
final String processInstanceType) throws IOException {
final ObjectOutputStream stream = context.stream;
// saves the processInstance type first
stream.writeUTF(processInstanceType);
}
@PreUpdate
public void update() {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final boolean variablesChanged = false;
try {
final MarshallerWriteContext context = new MarshallerWriteContext(
baos, null, null, null, null, this.env);
final String processType = ((ProcessInstanceImpl) processInstance)
.getProcess().getType();
saveProcessInstanceType(context, processInstance, processType);
final ProcessInstanceMarshaller marshaller = ProcessMarshallerRegistry.INSTANCE
.getMarshaller(processType);
marshaller.writeProcessInstance(context, processInstance);
context.close();
} catch (final IOException e) {
throw new IllegalArgumentException(
"IOException while storing process instance "
+ processInstance.getId() + ": " + e.getMessage());
}
final byte[] newByteArray = baos.toByteArray();
if (variablesChanged
|| !Arrays.equals(newByteArray, processInstanceByteArray)) {
this.state = processInstance.getState();
this.lastModificationDate = new Date();
this.processInstanceByteArray = newByteArray;
this.eventTypes.clear();
for (final String type : processInstance.getEventTypes()) {
eventTypes.add(type);
}
}
}
@Override
public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final ProcessInstanceInfo other = (ProcessInstanceInfo) obj;
if (this.processInstanceId != other.processInstanceId
&& (this.processInstanceId == null || !this.processInstanceId
.equals(other.processInstanceId))) {
return false;
}
if (this.version != other.version) {
return false;
}
if ((this.processId == null) ? (other.processId != null)
: !this.processId.equals(other.processId)) {
return false;
}
if (this.startDate != other.startDate
&& (this.startDate == null || !this.startDate
.equals(other.startDate))) {
return false;
}
if (this.lastReadDate != other.lastReadDate
&& (this.lastReadDate == null || !this.lastReadDate
.equals(other.lastReadDate))) {
return false;
}
if (this.lastModificationDate != other.lastModificationDate
&& (this.lastModificationDate == null || !this.lastModificationDate
.equals(other.lastModificationDate))) {
return false;
}
if (this.state != other.state) {
return false;
}
if (!Arrays.equals(this.processInstanceByteArray,
other.processInstanceByteArray)) {
return false;
}
if (this.eventTypes != other.eventTypes
&& (this.eventTypes == null || !this.eventTypes
.equals(other.eventTypes))) {
return false;
}
if (this.processInstance != other.processInstance
&& (this.processInstance == null || !this.processInstance
.equals(other.processInstance))) {
return false;
}
if (this.env != other.env
&& (this.env == null || !this.env.equals(other.env))) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 7;
hash = 61
* hash
+ (this.processInstanceId != null ? this.processInstanceId
.hashCode() : 0);
hash = 61 * hash + this.version;
hash = 61 * hash
+ (this.processId != null ? this.processId.hashCode() : 0);
hash = 61 * hash
+ (this.startDate != null ? this.startDate.hashCode() : 0);
hash = 61
* hash
+ (this.lastReadDate != null ? this.lastReadDate.hashCode() : 0);
hash = 61
* hash
+ (this.lastModificationDate != null ? this.lastModificationDate
.hashCode() : 0);
hash = 61 * hash + this.state;
hash = 61 * hash + Arrays.hashCode(this.processInstanceByteArray);
hash = 61 * hash
+ (this.eventTypes != null ? this.eventTypes.hashCode() : 0);
hash = 61
* hash
+ (this.processInstance != null ? this.processInstance
.hashCode() : 0);
hash = 61 * hash + (this.env != null ? this.env.hashCode() : 0);
return hash;
}
public int getVersion() {
return version;
}
public Set<String> getEventTypes() {
return eventTypes;
}
public void clearProcessInstance() {
processInstance = null;
}
}