/* 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.
*/
package org.activiti.rest.service.api.runtime;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.ObjectOutputStream;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.test.Deployment;
import org.activiti.rest.service.BaseSpringRestTestCase;
import org.activiti.rest.service.HttpMultipartHelper;
import org.activiti.rest.service.api.RestUrls;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.StringEntity;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* Test for all REST-operations related to Process instance variables.
*
* @author Frederik Heremans
*/
public class ProcessInstanceVariablesCollectionResourceTest extends BaseSpringRestTestCase {
/**
* Test getting all process variables.
* GET runtime/process-instances/{processInstanceId}/variables
*/
@Deployment(resources = {"org/activiti/rest/service/api/runtime/ProcessInstanceVariablesCollectionResourceTest.testProcess.bpmn20.xml"})
public void testGetProcessVariables() throws Exception {
Calendar cal = Calendar.getInstance();
// Start process with all types of variables
Map<String, Object> processVariables = new HashMap<String, Object>();
processVariables.put("stringProcVar", "This is a ProcVariable");
processVariables.put("intProcVar", 123);
processVariables.put("longProcVar", 1234L);
processVariables.put("shortProcVar", (short) 123);
processVariables.put("doubleProcVar", 99.99);
processVariables.put("booleanProcVar", Boolean.TRUE);
processVariables.put("dateProcVar", cal.getTime());
processVariables.put("byteArrayProcVar", "Some raw bytes".getBytes());
processVariables.put("overlappingVariable", "process-value");
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess", processVariables);
// Request all variables (no scope provides) which include global an local
CloseableHttpResponse response = executeRequest(new HttpGet(SERVER_URL_PREFIX +
RestUrls.createRelativeResourceUrl(RestUrls.URL_PROCESS_INSTANCE_VARIABLE_COLLECTION, processInstance.getId())), HttpStatus.SC_OK);
JsonNode responseNode = objectMapper.readTree(response.getEntity().getContent());
closeResponse(response);
assertNotNull(responseNode);
assertTrue(responseNode.isArray());
assertEquals(9, responseNode.size());
}
/**
* Test creating a single process variable.
* POST runtime/process-instance/{processInstanceId}/variables
*/
@Deployment(resources = {"org/activiti/rest/service/api/runtime/ProcessInstanceVariablesCollectionResourceTest.testProcess.bpmn20.xml"})
public void testCreateSingleProcessInstanceVariable() throws Exception {
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess");
ArrayNode requestNode = objectMapper.createArrayNode();
ObjectNode variableNode = requestNode.addObject();
variableNode.put("name", "myVariable");
variableNode.put("value", "simple string value");
variableNode.put("type", "string");
// Create a new local variable
HttpPost httpPost = new HttpPost(SERVER_URL_PREFIX +
RestUrls.createRelativeResourceUrl(RestUrls.URL_PROCESS_INSTANCE_VARIABLE_COLLECTION, processInstance.getId()));
httpPost.setEntity(new StringEntity(requestNode.toString()));
CloseableHttpResponse response = executeBinaryRequest(httpPost, HttpStatus.SC_CREATED);
JsonNode responseNode = objectMapper.readTree(response.getEntity().getContent()).get(0);
closeResponse(response);
assertNotNull(responseNode);
assertEquals("myVariable", responseNode.get("name").asText());
assertEquals("simple string value", responseNode.get("value").asText());
assertEquals("local", responseNode.get("scope").asText());
assertEquals("string", responseNode.get("type").asText());
assertNull(responseNode.get("valueUrl"));
assertTrue(runtimeService.hasVariableLocal(processInstance.getId(), "myVariable"));
assertEquals("simple string value", runtimeService.getVariableLocal(processInstance.getId(), "myVariable"));
}
/**
* Test creating a single process variable using a binary stream.
* POST runtime/process-instances/{processInstanceId}/variables
*/
@Deployment(resources = {"org/activiti/rest/service/api/runtime/ProcessInstanceVariablesCollectionResourceTest.testProcess.bpmn20.xml"})
public void testCreateSingleBinaryProcessVariable() throws Exception {
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess");
InputStream binaryContent = new ByteArrayInputStream("This is binary content".getBytes());
// Add name, type and scope
Map<String, String> additionalFields = new HashMap<String, String>();
additionalFields.put("name", "binaryVariable");
additionalFields.put("type", "binary");
HttpPost httpPost = new HttpPost(SERVER_URL_PREFIX +
RestUrls.createRelativeResourceUrl(RestUrls.URL_PROCESS_INSTANCE_VARIABLE_COLLECTION, processInstance.getId()));
httpPost.setEntity(HttpMultipartHelper.getMultiPartEntity("value", "application/octet-stream", binaryContent, additionalFields));
CloseableHttpResponse response = executeBinaryRequest(httpPost, HttpStatus.SC_CREATED);
JsonNode responseNode = objectMapper.readTree(response.getEntity().getContent());
closeResponse(response);
assertNotNull(responseNode);
assertEquals("binaryVariable", responseNode.get("name").asText());
assertTrue(responseNode.get("value").isNull());
assertEquals("local", responseNode.get("scope").asText());
assertEquals("binary", responseNode.get("type").asText());
assertFalse(responseNode.get("valueUrl").isNull());
assertTrue(responseNode.get("valueUrl").asText().endsWith(RestUrls.createRelativeResourceUrl(RestUrls.URL_PROCESS_INSTANCE_VARIABLE_DATA, processInstance.getId(), "binaryVariable")));
// Check actual value of variable in engine
Object variableValue = runtimeService.getVariableLocal(processInstance.getId(), "binaryVariable");
assertNotNull(variableValue);
assertTrue(variableValue instanceof byte[]);
assertEquals("This is binary content", new String((byte[])variableValue));
}
/**
* Test creating a single process variable using a binary stream containing a serializable.
* POST runtime/process-instances/{processInstanceId}/variables
*/
@Deployment(resources = {"org/activiti/rest/service/api/runtime/ProcessInstanceVariablesCollectionResourceTest.testProcess.bpmn20.xml"})
public void testCreateSingleSerializableProcessVariable() throws Exception {
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess");
TestSerializableVariable serializable = new TestSerializableVariable();
serializable.setSomeField("some value");
// Serialize object to readable stream for representation
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
ObjectOutputStream output = new ObjectOutputStream(buffer);
output.writeObject(serializable);
output.close();
InputStream binaryContent = new ByteArrayInputStream(buffer.toByteArray());
// Add name, type and scope
Map<String, String> additionalFields = new HashMap<String, String>();
additionalFields.put("name", "serializableVariable");
additionalFields.put("type", "serializable");
// Upload a valid BPMN-file using multipart-data
HttpPost httpPost = new HttpPost(SERVER_URL_PREFIX +
RestUrls.createRelativeResourceUrl(RestUrls.URL_PROCESS_INSTANCE_VARIABLE_COLLECTION, processInstance.getId()));
httpPost.setEntity(HttpMultipartHelper.getMultiPartEntity("value", "application/x-java-serialized-object", binaryContent, additionalFields));
CloseableHttpResponse response = executeBinaryRequest(httpPost, HttpStatus.SC_CREATED);
JsonNode responseNode = objectMapper.readTree(response.getEntity().getContent());
closeResponse(response);
assertNotNull(responseNode);
assertEquals("serializableVariable", responseNode.get("name").asText());
assertTrue(responseNode.get("value").isNull());
assertEquals("local", responseNode.get("scope").asText());
assertEquals("serializable", responseNode.get("type").asText());
assertFalse(responseNode.get("valueUrl").isNull());
assertTrue(responseNode.get("valueUrl").asText().endsWith(RestUrls.createRelativeResourceUrl(RestUrls.URL_PROCESS_INSTANCE_VARIABLE_DATA, processInstance.getId(), "serializableVariable")));
// Check actual value of variable in engine
Object variableValue = runtimeService.getVariableLocal(processInstance.getId(), "serializableVariable");
assertNotNull(variableValue);
assertTrue(variableValue instanceof TestSerializableVariable);
assertEquals("some value", ((TestSerializableVariable)variableValue).getSomeField());
}
/**
* Test creating a single process variable, testing edge case exceptions.
* POST runtime/process-instances/{processInstanceId}/variables
*/
@Deployment(resources = {"org/activiti/rest/service/api/runtime/ProcessInstanceVariablesCollectionResourceTest.testProcess.bpmn20.xml"})
public void testCreateSingleProcessVariableEdgeCases() throws Exception {
// Test adding variable to unexisting execution
ArrayNode requestNode = objectMapper.createArrayNode();
ObjectNode variableNode = requestNode.addObject();
variableNode.put("name", "existingVariable");
variableNode.put("value", "simple string value");
variableNode.put("type", "string");
HttpPost httpPost = new HttpPost(SERVER_URL_PREFIX +
RestUrls.createRelativeResourceUrl(RestUrls.URL_PROCESS_INSTANCE_VARIABLE_COLLECTION, "unexisting"));
httpPost.setEntity(new StringEntity(requestNode.toString()));
closeResponse(executeRequest(httpPost, HttpStatus.SC_NOT_FOUND));
// Test trying to create already existing variable
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess");
runtimeService.setVariable(processInstance.getId(), "existingVariable", "I already exist");
httpPost = new HttpPost(SERVER_URL_PREFIX +
RestUrls.createRelativeResourceUrl(RestUrls.URL_PROCESS_INSTANCE_VARIABLE_COLLECTION, processInstance.getId()));
httpPost.setEntity(new StringEntity(requestNode.toString()));
closeResponse(executeRequest(httpPost, HttpStatus.SC_CONFLICT));
// Test creating nameless variable
variableNode.removeAll();
variableNode.put("value", "simple string value");
httpPost = new HttpPost(SERVER_URL_PREFIX +
RestUrls.createRelativeResourceUrl(RestUrls.URL_PROCESS_INSTANCE_VARIABLE_COLLECTION, processInstance.getId()));
httpPost.setEntity(new StringEntity(requestNode.toString()));
closeResponse(executeRequest(httpPost, HttpStatus.SC_BAD_REQUEST));
// Test passing in empty array
requestNode.removeAll();
httpPost = new HttpPost(SERVER_URL_PREFIX +
RestUrls.createRelativeResourceUrl(RestUrls.URL_PROCESS_INSTANCE_VARIABLE_COLLECTION, processInstance.getId()));
httpPost.setEntity(new StringEntity(requestNode.toString()));
closeResponse(executeRequest(httpPost, HttpStatus.SC_BAD_REQUEST));
// Test passing in object instead of array
httpPost = new HttpPost(SERVER_URL_PREFIX +
RestUrls.createRelativeResourceUrl(RestUrls.URL_PROCESS_INSTANCE_VARIABLE_COLLECTION, processInstance.getId()));
httpPost.setEntity(new StringEntity(objectMapper.createObjectNode().toString()));
closeResponse(executeRequest(httpPost, HttpStatus.SC_BAD_REQUEST));
}
/**
* Test creating a single process variable, testing default types when omitted.
* POST runtime/process-instances/{processInstanceId}/variables
*/
@Deployment(resources = {"org/activiti/rest/service/api/runtime/ProcessInstanceVariablesCollectionResourceTest.testProcess.bpmn20.xml"})
public void testCreateSingleProcessVariableDefaultTypes() throws Exception {
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess");
// String type detection
ArrayNode requestNode = objectMapper.createArrayNode();
ObjectNode varNode = requestNode.addObject();
varNode.put("name", "stringVar");
varNode.put("value", "String value");
HttpPost httpPost = new HttpPost(SERVER_URL_PREFIX +
RestUrls.createRelativeResourceUrl(RestUrls.URL_PROCESS_INSTANCE_VARIABLE_COLLECTION, processInstance.getId()));
httpPost.setEntity(new StringEntity(requestNode.toString()));
closeResponse(executeRequest(httpPost, HttpStatus.SC_CREATED));
assertEquals("String value", runtimeService.getVariable(processInstance.getId(), "stringVar"));
// Integer type detection
varNode.put("name", "integerVar");
varNode.put("value", 123);
httpPost.setEntity(new StringEntity(requestNode.toString()));
closeResponse(executeRequest(httpPost, HttpStatus.SC_CREATED));
assertEquals(123, runtimeService.getVariable(processInstance.getId(), "integerVar"));
// Double type detection
varNode.put("name", "doubleVar");
varNode.put("value", 123.456);
httpPost.setEntity(new StringEntity(requestNode.toString()));
closeResponse(executeRequest(httpPost, HttpStatus.SC_CREATED));
assertEquals(123.456, runtimeService.getVariable(processInstance.getId(), "doubleVar"));
// Boolean type detection
varNode.put("name", "booleanVar");
varNode.put("value", Boolean.TRUE);
httpPost.setEntity(new StringEntity(requestNode.toString()));
closeResponse(executeRequest(httpPost, HttpStatus.SC_CREATED));
assertEquals(Boolean.TRUE, runtimeService.getVariable(processInstance.getId(), "booleanVar"));
}
/**
* Test creating multiple process variables in a single call.
* POST runtime/process-instance/{processInstanceId}/variables
*/
@Deployment(resources = {"org/activiti/rest/service/api/runtime/ProcessInstanceVariablesCollectionResourceTest.testProcess.bpmn20.xml"})
public void testCreateMultipleProcessVariables() throws Exception {
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess");
ArrayNode requestNode = objectMapper.createArrayNode();
// String variable
ObjectNode stringVarNode = requestNode.addObject();
stringVarNode.put("name", "stringVariable");
stringVarNode.put("value", "simple string value");
stringVarNode.put("type", "string");
// Integer
ObjectNode integerVarNode = requestNode.addObject();
integerVarNode.put("name", "integerVariable");
integerVarNode.put("value", 1234);
integerVarNode.put("type", "integer");
// Short
ObjectNode shortVarNode = requestNode.addObject();
shortVarNode.put("name", "shortVariable");
shortVarNode.put("value", 123);
shortVarNode.put("type", "short");
// Long
ObjectNode longVarNode = requestNode.addObject();
longVarNode.put("name", "longVariable");
longVarNode.put("value", 4567890L);
longVarNode.put("type", "long");
// Double
ObjectNode doubleVarNode = requestNode.addObject();
doubleVarNode.put("name", "doubleVariable");
doubleVarNode.put("value", 123.456);
doubleVarNode.put("type", "double");
// Boolean
ObjectNode booleanVarNode = requestNode.addObject();
booleanVarNode.put("name", "booleanVariable");
booleanVarNode.put("value", Boolean.TRUE);
booleanVarNode.put("type", "boolean");
// Date
Calendar varCal = Calendar.getInstance();
String isoString = getISODateString(varCal.getTime());
ObjectNode dateVarNode = requestNode.addObject();
dateVarNode.put("name", "dateVariable");
dateVarNode.put("value", isoString);
dateVarNode.put("type", "date");
// Create local variables with a single request
HttpPost httpPost = new HttpPost(SERVER_URL_PREFIX +
RestUrls.createRelativeResourceUrl(RestUrls.URL_PROCESS_INSTANCE_VARIABLE_COLLECTION, processInstance.getId()));
httpPost.setEntity(new StringEntity(requestNode.toString()));
CloseableHttpResponse response = executeRequest(httpPost, HttpStatus.SC_CREATED);
JsonNode responseNode = objectMapper.readTree(response.getEntity().getContent());
closeResponse(response);
assertNotNull(responseNode);
assertTrue(responseNode.isArray());
assertEquals(7, responseNode.size());
// Check if engine has correct variables set
Map<String, Object> variables = runtimeService.getVariablesLocal(processInstance.getId());
assertEquals(7, variables.size());
assertEquals("simple string value", variables.get("stringVariable"));
assertEquals(1234, variables.get("integerVariable"));
assertEquals((short)123, variables.get("shortVariable"));
assertEquals(4567890L, variables.get("longVariable"));
assertEquals(123.456, variables.get("doubleVariable"));
assertEquals(Boolean.TRUE, variables.get("booleanVariable"));
assertEquals(dateFormat.parse(isoString), variables.get("dateVariable"));
}
/**
* Test creating multiple process variables in a single call.
* POST runtime/process-instance/{processInstanceId}/variables?override=true
*/
@Deployment(resources = {"org/activiti/rest/service/api/runtime/ProcessInstanceVariablesCollectionResourceTest.testProcess.bpmn20.xml"})
public void testCreateMultipleProcessVariablesWithOverride() throws Exception {
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess");
runtimeService.setVariable(processInstance.getId(), "stringVariable", "initialValue");
ArrayNode requestNode = objectMapper.createArrayNode();
// String variable
ObjectNode stringVarNode = requestNode.addObject();
stringVarNode.put("name", "stringVariable");
stringVarNode.put("value", "simple string value");
stringVarNode.put("type", "string");
ObjectNode anotherVariable = requestNode.addObject();
anotherVariable.put("name", "stringVariable2");
anotherVariable.put("value", "another string value");
anotherVariable.put("type", "string");
// Create local variables with a single request
HttpPut httpPut = new HttpPut(SERVER_URL_PREFIX +
RestUrls.createRelativeResourceUrl(RestUrls.URL_PROCESS_INSTANCE_VARIABLE_COLLECTION, processInstance.getId()));
httpPut.setEntity(new StringEntity(requestNode.toString()));
CloseableHttpResponse response = executeRequest(httpPut, HttpStatus.SC_CREATED);
JsonNode responseNode = objectMapper.readTree(response.getEntity().getContent());
closeResponse(response);
assertNotNull(responseNode);
assertTrue(responseNode.isArray());
assertEquals(2, responseNode.size());
// Check if engine has correct variables set
Map<String, Object> variables = runtimeService.getVariablesLocal(processInstance.getId());
assertEquals(2, variables.size());
assertEquals("simple string value", variables.get("stringVariable"));
assertEquals("another string value", variables.get("stringVariable2"));
}
/**
* Test deleting all process variables.
* DELETE runtime/process-instance/{processInstanceId}/variables
*/
@Deployment(resources = {"org/activiti/rest/service/api/runtime/ProcessInstanceVariablesCollectionResourceTest.testProcess.bpmn20.xml"})
public void testDeleteAllProcessVariables() throws Exception {
Map<String, Object> processVariables = new HashMap<String, Object>();
processVariables.put("var1", "This is a ProcVariable");
processVariables.put("var2", 123);
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess", processVariables);
HttpDelete httpDelete = new HttpDelete(SERVER_URL_PREFIX +
RestUrls.createRelativeResourceUrl(RestUrls.URL_PROCESS_INSTANCE_VARIABLE_COLLECTION, processInstance.getId()));
closeResponse(executeRequest(httpDelete, HttpStatus.SC_NO_CONTENT));
// Check if local variables are gone and global remain unchanged
assertEquals(0, runtimeService.getVariablesLocal(processInstance.getId()).size());
}
}