package org.wso2.carbon.bpmn.rest.service.runtime;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.ActivitiIllegalArgumentException;
import org.activiti.engine.ActivitiObjectNotFoundException;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.impl.persistence.entity.VariableInstanceEntity;
import org.activiti.engine.runtime.Execution;
import org.apache.cxf.jaxrs.ext.multipart.MultipartBody;
import org.wso2.carbon.bpmn.rest.common.RestResponseFactory;
import org.wso2.carbon.bpmn.rest.common.utils.BPMNOSGIService;
import org.wso2.carbon.bpmn.rest.common.utils.Utils;
import org.wso2.carbon.bpmn.rest.engine.variable.RestVariable;
import org.wso2.carbon.bpmn.rest.model.common.DataResponse;
import org.wso2.carbon.bpmn.rest.model.runtime.*;
import org.wso2.carbon.bpmn.rest.service.base.BaseExecutionService;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Path("/executions")
public class ExecutionService extends BaseExecutionService {
@Context
protected UriInfo uriInfo;
/**
* Get the process execution identified by given execution ID
* @param executionId
* @return ExecutionResponse
*/
@GET
@Path("/{executionId}")
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public Response getExecution(@PathParam("executionId") String executionId, @Context UriInfo uriInfo) {
ExecutionResponse executionResponse = new RestResponseFactory()
.createExecutionResponse(getExecutionFromRequest(executionId), uriInfo.getBaseUri().toString());
return Response.ok().entity(executionResponse).build();
}
/**
* Execute an action on an execution
* @param executionId
* @param actionRequest
* @return Response
*/
@PUT
@Path("/{executionId}")
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
@Consumes({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public Response performExecutionAction(@PathParam("executionId") String executionId, ExecutionActionRequest
actionRequest) {
Execution execution = getExecutionFromRequest(executionId);
RuntimeService runtimeService = BPMNOSGIService.getRuntimeService();
if (ExecutionActionRequest.ACTION_SIGNAL.equals(actionRequest.getAction())) {
if (actionRequest.getVariables() != null) {
runtimeService.signal(execution.getId(), getVariablesToSet(actionRequest));
} else {
runtimeService.signal(execution.getId());
}
} else if (ExecutionActionRequest.ACTION_SIGNAL_EVENT_RECEIVED.equals(actionRequest.getAction())) {
if (actionRequest.getSignalName() == null) {
throw new ActivitiIllegalArgumentException("Signal name is required");
}
if (actionRequest.getVariables() != null) {
runtimeService.signalEventReceived(actionRequest.getSignalName(), execution.getId(),
getVariablesToSet(actionRequest));
} else {
runtimeService.signalEventReceived(actionRequest.getSignalName(), execution.getId());
}
} else if (ExecutionActionRequest.ACTION_MESSAGE_EVENT_RECEIVED.equals(actionRequest.getAction())) {
if (actionRequest.getMessageName() == null) {
throw new ActivitiIllegalArgumentException("Message name is required");
}
if (actionRequest.getVariables() != null) {
runtimeService.messageEventReceived(actionRequest.getMessageName(), execution.getId(),
getVariablesToSet(actionRequest));
} else {
runtimeService.messageEventReceived(actionRequest.getMessageName(), execution.getId());
}
} else {
throw new ActivitiIllegalArgumentException("Invalid action: '" + actionRequest.getAction() + "'.");
}
Response.ResponseBuilder response = Response.ok();
// Re-fetch the execution, could have changed due to action or even completed
execution = runtimeService.createExecutionQuery().executionId(execution.getId()).singleResult();
if (execution == null) {
// Execution is finished, return empty body to inform user
response.status(Response.Status.NO_CONTENT);
} else {
response.entity(new RestResponseFactory()
.createExecutionResponse(execution, uriInfo.getBaseUri().toString())).build();
}
return response.build();
}
@GET
@Path("/{executionId}/activities")
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public Response getActiveActivities(@PathParam("executionId") String executionId) {
Execution execution = getExecutionFromRequest(executionId);
RuntimeService runtimeService = BPMNOSGIService.getRuntimeService();
List<String> activityIdList = runtimeService.getActiveActivityIds(execution.getId());
ActiveActivityCollection activeActivityCollection = new ActiveActivityCollection();
activeActivityCollection.setActiveActivityList(activityIdList);
return Response.ok().entity(activeActivityCollection).build();
}
@GET
@Path("/")
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public Response getProcessInstances() {
// Populate query based on request
ExecutionQueryRequest queryRequest = new ExecutionQueryRequest();
Map<String, String> allRequestParams = new HashMap<>();
for (String property:allPropertiesList){
String value= uriInfo.getQueryParameters().getFirst(property);
if(value != null){
allRequestParams.put(property, value);
}
}
String id = uriInfo.getQueryParameters().getFirst("id");
if (id != null) {
queryRequest.setId(id);
}
String processInstanceId = uriInfo.getQueryParameters().getFirst("processInstanceId");
if (processInstanceId != null) {
queryRequest.setProcessInstanceId(processInstanceId);
}
String processInstanceBusinessKey = uriInfo.getQueryParameters().getFirst("processInstanceBusinessKey");
if (processInstanceBusinessKey != null) {
queryRequest.setProcessBusinessKey(processInstanceBusinessKey);
}
String processDefinitionKey = uriInfo.getQueryParameters().getFirst("processDefinitionKey");
if (processDefinitionKey != null) {
queryRequest.setProcessDefinitionKey(processDefinitionKey);
}
String processDefinitionId = uriInfo.getQueryParameters().getFirst("processDefinitionId");
if (processDefinitionId != null) {
queryRequest.setProcessDefinitionId(processDefinitionId);
}
String messageEventSubscriptionName = uriInfo.getQueryParameters().getFirst("messageEventSubscriptionName");
if (messageEventSubscriptionName != null) {
queryRequest.setMessageEventSubscriptionName(messageEventSubscriptionName);
}
String signalEventSubscriptionName = uriInfo.getQueryParameters().getFirst("signalEventSubscriptionName");
if (signalEventSubscriptionName != null) {
queryRequest.setSignalEventSubscriptionName(signalEventSubscriptionName);
}
String activityId = uriInfo.getQueryParameters().getFirst("activityId");
if (activityId != null) {
queryRequest.setActivityId(activityId);
}
String parentId = uriInfo.getQueryParameters().getFirst("parentId");
if (parentId != null) {
queryRequest.setParentId(parentId);
}
String tenantId = uriInfo.getQueryParameters().getFirst("tenantId");
if (tenantId != null) {
queryRequest.setTenantId(tenantId);
}
String tenantIdLike = uriInfo.getQueryParameters().getFirst("tenantIdLike");
if (tenantIdLike != null) {
queryRequest.setTenantIdLike(tenantIdLike);
}
String withoutTenantId = uriInfo.getQueryParameters().getFirst("withoutTenantId");
if (withoutTenantId != null) {
if (Boolean.valueOf(withoutTenantId)) {
queryRequest.setWithoutTenantId(Boolean.TRUE);
}
}
//add common parameters such as sort,order,start etc.
//allRequestParams = Utils.prepareCommonParameters(allRequestParams, uriInfo);
DataResponse dataResponse = getQueryResponse(queryRequest, allRequestParams, uriInfo);
return Response.ok().entity(dataResponse).build();
}
@PUT
@Path("/")
public Response executeExecutionAction(ExecutionActionRequest actionRequest) {
if (!ExecutionActionRequest.ACTION_SIGNAL_EVENT_RECEIVED.equals(actionRequest.getAction())) {
throw new ActivitiIllegalArgumentException("Illegal action: '" + actionRequest.getAction() +"'.");
}
if (actionRequest.getSignalName() == null) {
throw new ActivitiIllegalArgumentException("Signal name is required.");
}
RuntimeService runtimeService = BPMNOSGIService.getRuntimeService();
if (actionRequest.getVariables() != null) {
runtimeService.signalEventReceived(actionRequest.getSignalName(), getVariablesToSet(actionRequest));
} else {
runtimeService.signalEventReceived(actionRequest.getSignalName());
}
return Response.ok().status(Response.Status.NO_CONTENT).build();
}
@GET
@Path("/{executionId}/variables")
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public Response getVariables(@PathParam("executionId") String executionId) {
String scope = uriInfo.getQueryParameters().getFirst("scope");
Execution execution = getExecutionFromRequest(executionId);
List<RestVariable> restVariableList = processVariables(execution, scope, RestResponseFactory
.VARIABLE_EXECUTION, uriInfo);
RestVariableCollection restVariableCollection = new RestVariableCollection();
restVariableCollection.setRestVariables(restVariableList);
return Response.ok().entity(restVariableCollection).build();
}
@PUT
@Path("/{executionId}/variables")
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
@Consumes({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public Response createOrUpdateExecutionVariable(@PathParam("executionId") String executionId, @Context
HttpServletRequest httpServletRequest) {
Execution execution = getExecutionFromRequest(executionId);
return createExecutionVariable(execution, true, RestResponseFactory.VARIABLE_EXECUTION, httpServletRequest,
uriInfo );
}
@PUT
@Path("/{executionId}/variables")
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response createOrUpdateBinaryExecutionVariable(@PathParam("executionId") String executionId,
MultipartBody multipartBody) {
Execution execution = getExecutionFromRequest(executionId);
RestVariable restVariable = createBinaryExecutionVariable(execution,RestResponseFactory.VARIABLE_EXECUTION,
uriInfo, true, multipartBody);
return Response.ok().status(Response.Status.CREATED).entity(restVariable).build();
}
@POST
@Path("/{executionId}/variables")
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
@Consumes({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public Response createExecutionVariable(@PathParam("executionId") String executionId, @Context HttpServletRequest
httpServletRequest) {
Execution execution = getExecutionFromRequest(executionId);
return createExecutionVariable(execution, false, RestResponseFactory.VARIABLE_EXECUTION, httpServletRequest,
uriInfo);
}
@POST
@Path("/{executionId}/variables")
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response createBinaryExecutionVariable(@PathParam("executionId") String executionId, MultipartBody
multipartBody) {
Execution execution = getExecutionFromRequest(executionId);
RestVariable restVariable = createBinaryExecutionVariable(execution,RestResponseFactory.VARIABLE_EXECUTION,
uriInfo, true, multipartBody);
return Response.ok().status(Response.Status.CREATED).entity(restVariable).build();
}
@DELETE
@Path("/{executionId}/variables")
public Response deleteLocalVariables(@PathParam("executionId") String executionId) {
Execution execution = getExecutionFromRequest(executionId);
deleteAllLocalVariables(execution);
return Response.ok().status(Response.Status.NO_CONTENT).build();
}
@GET
@Path("/{executionId}/variables/{variableName}")
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public RestVariable getVariable(@PathParam("executionId") String executionId,
@PathParam("variableName") String variableName) {
String scope = uriInfo.getQueryParameters().getFirst("scope");
Execution execution = getExecutionFromRequest(executionId);
return getVariableFromRequest(execution, variableName, scope, false, uriInfo);
}
@PUT
@Path("/{executionId}/variables/{variableName}")
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response updateBinaryVariable(@PathParam("executionId") String executionId,
@PathParam("variableName") String variableName,
MultipartBody multipartBody) {
Execution execution = getExecutionFromRequest(executionId);
RestVariable result = createBinaryExecutionVariable(execution,RestResponseFactory.VARIABLE_EXECUTION,
uriInfo, false, multipartBody);
return Response.ok().status(Response.Status.CREATED).entity(result).build();
}
@PUT
@Path("/{executionId}/variables/{variableName}")
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
@Consumes({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public Response updateVariable(@PathParam("executionId") String executionId,
@PathParam("variableName") String variableName,
@Context HttpServletRequest httpServletRequest) {
Execution execution = getExecutionFromRequest(executionId);
RestVariable result = null;
RestVariable restVariable = null;
if (Utils.isApplicationJsonRequest(httpServletRequest)) {
try {
restVariable = new ObjectMapper().readValue(httpServletRequest.getInputStream(), RestVariable.class);
} catch (Exception e) {
throw new ActivitiIllegalArgumentException("Error converting request body to RestVariable instance", e);
}
} else if (Utils.isApplicationXmlRequest(httpServletRequest)) {
JAXBContext jaxbContext;
try {
jaxbContext = JAXBContext.newInstance(RestVariable.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
restVariable = (RestVariable) jaxbUnmarshaller.unmarshal(httpServletRequest.getInputStream());
} catch (JAXBException | IOException e) {
throw new ActivitiIllegalArgumentException("xml request body could not be transformed to a RestVariable instance.", e);
}
}
if (restVariable == null) {
throw new ActivitiException("Invalid body was supplied");
}
if (!restVariable.getName().equals(variableName)) {
throw new ActivitiIllegalArgumentException("Variable name in the body should be equal to the name used in the requested URL.");
}
result = setSimpleVariable(restVariable, execution, false, uriInfo);
return Response.ok().status(Response.Status.CREATED).entity(result).build();
}
@DELETE
@Path("/{executionId}/variables/{variableName}")
@Consumes({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public Response deleteVariable(@PathParam("executionId") String executionId,
@PathParam("variableName") String variableName) {
String scope = uriInfo.getQueryParameters().getFirst("scope");
Execution execution = getExecutionFromRequest(executionId);
// Determine scope
RestVariable.RestVariableScope variableScope = RestVariable.RestVariableScope.LOCAL;
if (scope != null) {
variableScope = RestVariable.getScopeFromString(scope);
}
if (!hasVariableOnScope(execution, variableName, variableScope)) {
throw new ActivitiObjectNotFoundException("Execution '" + execution.getId() + "' doesn't have a variable '" +
variableName + "' in scope " + variableScope.name().toLowerCase(), VariableInstanceEntity.class);
}
RuntimeService runtimeService = BPMNOSGIService.getRuntimeService();
if (variableScope == RestVariable.RestVariableScope.LOCAL) {
runtimeService.removeVariableLocal(execution.getId(), variableName);
} else {
// Safe to use parentId, as the hasVariableOnScope would have stopped a global-var update on a root-execution
runtimeService.removeVariable(execution.getParentId(), variableName);
}
return Response.ok().status(Response.Status.NO_CONTENT).build();
}
@GET
@Path("/{executionId}/variables/{variableName}/data")
public Response getVariableData(@PathParam("executionId") String executionId,
@PathParam("variableName") String variableName) {
String scope = uriInfo.getQueryParameters().getFirst("scope");
try {
byte[] result = null;
Response.ResponseBuilder response = Response.ok();
Execution execution = getExecutionFromRequest(executionId);
RestVariable variable = getVariableFromRequest(execution, variableName, scope, true, uriInfo);
if (RestResponseFactory.BYTE_ARRAY_VARIABLE_TYPE.equals(variable.getType())) {
result = (byte[]) variable.getValue();
response.type("application/octet-stream");
} else if(RestResponseFactory.SERIALIZABLE_VARIABLE_TYPE.equals(variable.getType())) {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
ObjectOutputStream outputStream = new ObjectOutputStream(buffer);
outputStream.writeObject(variable.getValue());
outputStream.close();
result = buffer.toByteArray();
response.type("application/x-java-serialized-object");
} else {
throw new ActivitiObjectNotFoundException("The variable does not have a binary data stream.", null);
}
return response.entity(result).build();
} catch (IOException ioe) {
throw new ActivitiException("Error getting variable " + variableName, ioe);
}
}
}