Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(Issue 16) Fix for deserialization problems with process variables #1

Merged
merged 2 commits into from
Jan 27, 2012
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions ActivitiGrailsPlugin.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import org.codehaus.groovy.grails.commons.ConfigurationHolder as CH
import org.codehaus.groovy.grails.commons.ControllerArtefactHandler
import org.springframework.core.io.Resource
import org.grails.activiti.ActivitiConstants
import org.grails.activiti.serializable.SerializableVariableType

/**
*
Expand Down Expand Up @@ -86,6 +87,9 @@ class ActivitiGrailsPlugin {
mailServerDefaultFrom = CH.config.activiti.mailServerDefaultFrom?:ActivitiConstants.DEFAULT_MAIL_SERVER_FROM
dataSource = ref("dataSource")
transactionManager = ref("transactionManager")

// Define custom serializable types for fix issue with serialization
customPreVariableTypes = [new SerializableVariableType()]
}

processEngine(org.activiti.spring.ProcessEngineFactoryBean) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.grails.activiti.serializable

/**
*
* @author <a href='mailto:pais@exigenservices.com'>Ilya Drabenia</a>
*
* @since 5.8.2
*/
class GroovyObjectInputStream extends ObjectInputStream {
private ClassLoader myClassLoader;

public GroovyObjectInputStream(ClassLoader myClassLoader) throws IOException, SecurityException {
super();
this.myClassLoader = myClassLoader;
}

public GroovyObjectInputStream(InputStream inp, ClassLoader myClassLoader) throws IOException {
super(inp);
this.myClassLoader = myClassLoader;
}

@Override
protected Class resolveClass(ObjectStreamClass desc) throws IOException,
ClassNotFoundException {
String name = desc.getName();
return Class.forName(name, false, new GroovyClassLoader(myClassLoader));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package org.grails.activiti.serializable

import org.activiti.engine.ActivitiException
import org.activiti.engine.impl.persistence.entity.VariableInstanceEntity
import org.activiti.engine.impl.variable.ByteArrayType
import org.activiti.engine.impl.variable.ValueFields
import org.activiti.engine.impl.util.IoUtil
import org.activiti.engine.impl.context.Context
import org.grails.activiti.serializable.GroovyObjectInputStream

/**
*
* @author <a href='mailto:pais@exigenservices.com'>Ilya Drabenia</a>
*
* @since 5.8.2
*/
class SerializableVariableType extends ByteArrayType {
public static final String TYPE_NAME = "groovySerializable"

private static final long serialVersionUID = 1L

String getTypeName() {
return TYPE_NAME;
}

Object getValue(ValueFields valueFields) {
Object cachedObject = valueFields.getCachedValue()
if (cachedObject != null) {
return cachedObject
}
byte[] bytes = (byte[]) super.getValue(valueFields)
ByteArrayInputStream bais = new ByteArrayInputStream(bytes)
Object deserializedObject = null
try {
GroovyObjectInputStream ois = new GroovyObjectInputStream(bais, Thread.currentThread().contextClassLoader)
deserializedObject = ois.readObject()
valueFields.setCachedValue(deserializedObject)

if (valueFields instanceof VariableInstanceEntity) {
Context
.getCommandContext()
.getDbSqlSession()
.addDeserializedObject(deserializedObject, bytes, (VariableInstanceEntity) valueFields)
}

}
catch (Exception e) {
throw new ActivitiException("coudn't deserialize object in variable '" + valueFields.getName() + "'", e)
}
finally {
IoUtil.closeSilently(bais)
}
return deserializedObject
}

void setValue(Object value, ValueFields valueFields) {
byte[] byteArray = serialize(value, valueFields)
valueFields.setCachedValue(value)
super.setValue(byteArray, valueFields)
}

static byte[] serialize(Object value, ValueFields valueFields) {
if (value == null) {
return null
}
ByteArrayOutputStream baos = new ByteArrayOutputStream()
ObjectOutputStream ois = null
try {
ois = new ObjectOutputStream(baos)
ois.writeObject(value)
}
catch (Exception e) {
throw new ActivitiException("coudn't deserialize value '" + value + "' in variable '" + valueFields.getName() + "'", e)
}
finally {
IoUtil.closeSilently(ois)
}
return baos.toByteArray()
}

boolean isAbleToStore(Object value) {
return value instanceof Serializable;
}
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.grails.activiti.flow.variable

/**
* @author Ilya Drabenia
*/
class FakeFlowVariable implements Serializable {
String name
int age
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:activiti="http://activiti.org/bpmn"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"
typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath"
targetNamespace="http://www.activiti.org/test">
<process id="sampleProcess" name="sampleProcess">
<startEvent id="startevent1" name="Start"></startEvent>
<userTask id="usertask1" name="User Task" activiti:assignee="qa"></userTask>
<serviceTask id="servicetask1" name="Service Task" activiti:expression="${&quot;Service Task&quot;}"></serviceTask>
<sequenceFlow id="flow1" name="" sourceRef="startevent1" targetRef="usertask1"></sequenceFlow>
<sequenceFlow id="flow2" name="" sourceRef="usertask1" targetRef="servicetask1"></sequenceFlow>
<endEvent id="endevent1" name="End"></endEvent>
<sequenceFlow id="flow3" name="" sourceRef="servicetask1" targetRef="endevent1"></sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_sampleProcess">
<bpmndi:BPMNPlane bpmnElement="sampleProcess" id="BPMNPlane_sampleProcess">
<bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
<omgdc:Bounds height="35" width="35" x="555" y="90"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1">
<omgdc:Bounds height="55" width="105" x="520" y="170"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="servicetask1" id="BPMNShape_servicetask1">
<omgdc:Bounds height="55" width="105" x="520" y="280"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
<omgdc:Bounds height="35" width="35" x="555" y="380"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
<omgdi:waypoint x="572" y="125"></omgdi:waypoint>
<omgdi:waypoint x="572" y="170"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
<omgdi:waypoint x="572" y="225"></omgdi:waypoint>
<omgdi:waypoint x="572" y="280"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
<omgdi:waypoint x="572" y="335"></omgdi:waypoint>
<omgdi:waypoint x="572" y="380"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package org.grails.activiti.flow.variable

import org.activiti.engine.RuntimeService
import org.activiti.engine.TaskService
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.activiti.engine.RepositoryService

/**
*
* @author <a href='mailto:pais@exigenservices.com'>Ilya Drabenia</a>
*
* @since 5.8.2
*/
class VariableSerializationTests {
RuntimeService runtimeService
RepositoryService repositoryService

@Test
void testSettingAndGettingOfProcessVariable() {
repositoryService.createDeployment()
.addClasspathResource("org/grails/activiti/flow/variable/SampleProcess.bpmn20.xml")
.name("sampleProcess")
.deploy()

// start process
def process = runtimeService.startProcessInstanceByKey("sampleProcess")

// check flow variables
runtimeService.setVariable(process.processInstanceId, "var1", new FakeFlowVariable(name: 'Matt', age: 21))
def flowVariable = (FakeFlowVariable) runtimeService.getVariable(process.processInstanceId, "var1")

// kill process
runtimeService.deleteProcessInstance(process.id, "Test is completed")
}
}