org.wso2.carbon.bpmn.rest.service.runtime.WorkflowTaskService.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.carbon.bpmn.rest.service.runtime.WorkflowTaskService.java

Source

/**
 * Copyright (c) 2015 WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 * <p/>
 * 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
 * <p/>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p/>
 * 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.wso2.carbon.bpmn.rest.service.runtime;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.impl.persistence.entity.VariableInstanceEntity;
import org.activiti.engine.task.*;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.cxf.jaxrs.ext.multipart.MultipartBody;
import org.wso2.carbon.bpmn.rest.common.RequestUtil;
import org.wso2.carbon.bpmn.rest.common.RestResponseFactory;
import org.wso2.carbon.bpmn.rest.common.RestUrls;
import org.wso2.carbon.bpmn.rest.common.exception.BPMNConflictException;
import org.wso2.carbon.bpmn.rest.common.exception.BPMNForbiddenException;
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.common.RestIdentityLink;
import org.wso2.carbon.bpmn.rest.model.runtime.*;
import org.wso2.carbon.bpmn.rest.service.base.BaseTaskService;

import javax.activation.DataHandler;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
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.*;
import java.util.*;

@Path("/tasks")
public class WorkflowTaskService extends BaseTaskService {

    private static final Log log = LogFactory.getLog(WorkflowTaskService.class);
    @Context
    UriInfo uriInfo;

    @GET
    @Path("/")
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public Response getTasks() {

        // Create a Task query request
        TaskQueryRequest request = new TaskQueryRequest();

        Map<String, String> requestParams = new HashMap<>();

        for (String property : allPropertiesList) {
            String value = uriInfo.getQueryParameters().getFirst(property);

            if (value != null) {
                requestParams.put(property, value);
            }
        }

        // Populate filter-parameters
        if (requestParams.containsKey("name")) {
            request.setName(requestParams.get("name"));
        }

        if (requestParams.containsKey("nameLike")) {
            request.setNameLike(requestParams.get("nameLike"));
        }

        if (requestParams.containsKey("description")) {
            request.setDescription(requestParams.get("description"));
        }

        if (requestParams.containsKey("descriptionLike")) {
            request.setDescriptionLike(requestParams.get("descriptionLike"));
        }

        if (requestParams.containsKey("priority")) {
            request.setPriority(Integer.valueOf(requestParams.get("priority")));
        }

        if (requestParams.containsKey("minimumPriority")) {
            request.setMinimumPriority(Integer.valueOf(requestParams.get("minimumPriority")));
        }

        if (requestParams.containsKey("maximumPriority")) {
            request.setMaximumPriority(Integer.valueOf(requestParams.get("maximumPriority")));
        }

        if (requestParams.containsKey("assignee")) {
            request.setAssignee(requestParams.get("assignee"));
        }

        if (requestParams.containsKey("owner")) {
            request.setOwner(requestParams.get("owner"));
        }

        if (requestParams.containsKey("unassigned")) {
            request.setUnassigned(Boolean.valueOf(requestParams.get("unassigned")));
        }

        if (requestParams.containsKey("delegationState")) {
            request.setDelegationState(requestParams.get("delegationState"));
        }

        if (requestParams.containsKey("candidateUser")) {
            request.setCandidateUser(requestParams.get("candidateUser"));
        }

        if (requestParams.containsKey("involvedUser")) {
            request.setInvolvedUser(requestParams.get("involvedUser"));
        }

        if (requestParams.containsKey("candidateGroup")) {
            request.setCandidateGroup(requestParams.get("candidateGroup"));
        }

        if (requestParams.containsKey("candidateGroups")) {
            String[] candidateGroups = requestParams.get("candidateGroups").split(",");
            List<String> groups = new ArrayList<String>(candidateGroups.length);
            for (String candidateGroup : candidateGroups) {
                groups.add(candidateGroup);
            }
            request.setCandidateGroupIn(groups);
        }

        if (requestParams.containsKey("processDefinitionKey")) {
            request.setProcessDefinitionKey(requestParams.get("processDefinitionKey"));
        }

        if (requestParams.containsKey("processDefinitionKeyLike")) {
            request.setProcessDefinitionKeyLike(requestParams.get("processDefinitionKeyLike"));
        }

        if (requestParams.containsKey("processDefinitionName")) {
            request.setProcessDefinitionName(requestParams.get("processDefinitionName"));
        }

        if (requestParams.containsKey("processDefinitionNameLike")) {
            request.setProcessDefinitionNameLike(requestParams.get("processDefinitionNameLike"));
        }

        if (requestParams.containsKey("processInstanceId")) {
            request.setProcessInstanceId(requestParams.get("processInstanceId"));
        }

        if (requestParams.containsKey("processInstanceBusinessKey")) {
            request.setProcessInstanceBusinessKey(requestParams.get("processInstanceBusinessKey"));
        }

        if (requestParams.containsKey("executionId")) {
            request.setExecutionId(requestParams.get("executionId"));
        }

        if (requestParams.containsKey("createdOn")) {
            request.setCreatedOn(RequestUtil.getDate(requestParams, "createdOn"));
        }

        if (requestParams.containsKey("createdBefore")) {
            request.setCreatedBefore(RequestUtil.getDate(requestParams, "createdBefore"));
        }

        if (requestParams.containsKey("createdAfter")) {
            request.setCreatedAfter(RequestUtil.getDate(requestParams, "createdAfter"));
        }

        if (requestParams.containsKey("excludeSubTasks")) {
            request.setExcludeSubTasks(Boolean.valueOf(requestParams.get("excludeSubTasks")));
        }

        if (requestParams.containsKey("taskDefinitionKey")) {
            request.setTaskDefinitionKey(requestParams.get("taskDefinitionKey"));
        }

        if (requestParams.containsKey("taskDefinitionKeyLike")) {
            request.setTaskDefinitionKeyLike(requestParams.get("taskDefinitionKeyLike"));
        }

        if (requestParams.containsKey("dueDate")) {
            request.setDueDate(RequestUtil.getDate(requestParams, "dueDate"));
        }

        if (requestParams.containsKey("dueBefore")) {
            request.setDueBefore(RequestUtil.getDate(requestParams, "dueBefore"));
        }

        if (requestParams.containsKey("dueAfter")) {
            request.setDueAfter(RequestUtil.getDate(requestParams, "dueAfter"));
        }

        if (requestParams.containsKey("active")) {
            request.setActive(Boolean.valueOf(requestParams.get("active")));
        }

        if (requestParams.containsKey("includeTaskLocalVariables")) {
            request.setIncludeTaskLocalVariables(Boolean.valueOf(requestParams.get("includeTaskLocalVariables")));
        }

        if (requestParams.containsKey("includeProcessVariables")) {
            request.setIncludeProcessVariables(Boolean.valueOf(requestParams.get("includeProcessVariables")));
        }

        if (requestParams.containsKey("tenantId")) {
            request.setTenantId(requestParams.get("tenantId"));
        }

        if (requestParams.containsKey("tenantIdLike")) {
            request.setTenantIdLike(requestParams.get("tenantIdLike"));
        }

        if (requestParams.containsKey("withoutTenantId") && Boolean.valueOf(requestParams.get("withoutTenantId"))) {
            request.setWithoutTenantId(Boolean.TRUE);
        }

        if (requestParams.containsKey("candidateOrAssigned")) {
            request.setCandidateOrAssigned(requestParams.get("candidateOrAssigned"));
        }

        DataResponse dataResponse = getTasksFromQueryRequest(request, uriInfo, requestParams);
        return Response.ok().entity(dataResponse).build();
        //return getTasksFromQueryRequest(request, requestParams);
    }

    /**
     * Create a new task instance for a process instance.
     * @param taskRequest
     * @return
     */
    @POST
    @Path("/")
    @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public Response createTask(TaskRequest taskRequest) {
        TaskService taskService = BPMNOSGIService.getTaskService();
        Task task = taskService.newTask();

        // Populate the task properties based on the request
        populateTaskFromRequest(task, taskRequest);
        if (taskRequest.isTenantIdSet()) {
            ((TaskEntity) task).setTenantId(taskRequest.getTenantId());
        }
        taskService.saveTask(task);
        return Response.ok()
                .entity(new RestResponseFactory().createTaskResponse(task, uriInfo.getBaseUri().toString()))
                .build();
    }

    @GET
    @Path("/{taskId}")
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public Response getTask(@PathParam("taskId") String taskId) {
        TaskResponse taskResponse = new RestResponseFactory().createTaskResponse(getTaskFromRequest(taskId),
                uriInfo.getBaseUri().toString());

        return Response.ok().entity(taskResponse).build();
    }

    @PUT
    @Path("/{taskId}")
    @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public Response updateTask(@PathParam("taskId") String taskId, TaskRequest taskRequest) {

        if (taskRequest == null) {
            throw new ActivitiException("A request body was expected when updating the task.");
        }

        Task task = getTaskFromRequest(taskId);

        // Populate the task properties based on the request
        populateTaskFromRequest(task, taskRequest);

        TaskService taskService = BPMNOSGIService.getTaskService();

        // Save the task and fetch agian, it's possible that an assignment-listener has updated
        // fields after it was saved so we can't use the in-memory task
        taskService.saveTask(task);
        task = taskService.createTaskQuery().taskId(task.getId()).singleResult();

        return Response.ok()
                .entity(new RestResponseFactory().createTaskResponse(task, uriInfo.getBaseUri().toString()))
                .build();
    }

    @POST
    @Path("/{taskId}")
    @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public Response executeTaskAction(@PathParam("taskId") String taskId, TaskActionRequest actionRequest) {
        if (actionRequest == null) {
            throw new ActivitiException("A request body was expected when executing a task action.");
        }

        Task task = getTaskFromRequest(taskId);

        TaskService taskService = BPMNOSGIService.getTaskService();

        if (TaskActionRequest.ACTION_COMPLETE.equals(actionRequest.getAction())) {
            completeTask(task, actionRequest, taskService);

        } else if (TaskActionRequest.ACTION_CLAIM.equals(actionRequest.getAction())) {
            claimTask(task, actionRequest, taskService);

        } else if (TaskActionRequest.ACTION_DELEGATE.equals(actionRequest.getAction())) {
            delegateTask(task, actionRequest, taskService);

        } else if (TaskActionRequest.ACTION_RESOLVE.equals(actionRequest.getAction())) {
            resolveTask(task, taskService);

        } else {
            throw new ActivitiIllegalArgumentException("Invalid action: '" + actionRequest.getAction() + "'.");
        }

        return Response.ok().status(Response.Status.OK).build();
    }

    @DELETE
    @Path("/{taskId}")
    public Response deleteTask(@PathParam("taskId") String taskId) {

        Boolean cascadeHistory = false;

        if (uriInfo.getQueryParameters().getFirst("cascadeHistory") != null) {
            cascadeHistory = Boolean.valueOf(uriInfo.getQueryParameters().getFirst("cascadeHistory"));
        }
        String deleteReason = uriInfo.getQueryParameters().getFirst("deleteReason");

        Task taskToDelete = getTaskFromRequest(taskId);
        if (taskToDelete.getExecutionId() != null) {
            // Can't delete a task that is part of a process instance
            throw new BPMNForbiddenException("Cannot delete a task that is part of a process-instance.");
        }

        TaskService taskService = BPMNOSGIService.getTaskService();

        if (cascadeHistory != null) {
            // Ignore delete-reason since the task-history (where the reason is recorded) will be deleted anyway
            taskService.deleteTask(taskToDelete.getId(), cascadeHistory);
        } else {
            // Delete with delete-reason
            taskService.deleteTask(taskToDelete.getId(), deleteReason);
        }
        return Response.ok().status(Response.Status.NO_CONTENT).build();
    }

    @GET
    @Path("/{taskId}/variables")
    @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public Response getVariables(@PathParam("taskId") String taskId) {

        String scope = uriInfo.getQueryParameters().getFirst("scope");

        List<RestVariable> result = new ArrayList<>();
        Map<String, RestVariable> variableMap = new HashMap<String, RestVariable>();

        // Check if it's a valid task to get the variables for
        Task task = getTaskFromRequest(taskId);

        RestVariable.RestVariableScope variableScope = RestVariable.getScopeFromString(scope);
        if (variableScope == null) {
            // Use both local and global variables
            addLocalVariables(task, variableMap, uriInfo.getBaseUri().toString());
            addGlobalVariables(task, variableMap, uriInfo.getBaseUri().toString());

        } else if (variableScope == RestVariable.RestVariableScope.GLOBAL) {
            addGlobalVariables(task, variableMap, uriInfo.getBaseUri().toString());

        } else if (variableScope == RestVariable.RestVariableScope.LOCAL) {
            addLocalVariables(task, variableMap, uriInfo.getBaseUri().toString());
        }

        // Get unique variables from map
        result.addAll(variableMap.values());

        RestVariableCollection restVariableCollection = new RestVariableCollection();
        restVariableCollection.setRestVariables(result);
        return Response.ok().entity(restVariableCollection).build();
    }

    @GET
    @Path("/{taskId}/variables/{variableName}")
    @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public RestVariable getVariable(@PathParam("taskId") String taskId,
            @PathParam("variableName") String variableName) {

        String scope = uriInfo.getQueryParameters().getFirst("scope");
        return getVariableFromRequest(taskId, variableName, scope, false, uriInfo.getBaseUri().toString());
    }

    @GET
    @Path("/{taskId}/variables/{variableName}/data")
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public Response getVariableData(@PathParam("taskId") String taskId,
            @PathParam("variableName") String variableName) {

        String scope = uriInfo.getQueryParameters().getFirst("scope");
        Response.ResponseBuilder responseBuilder = Response.ok();
        try {
            byte[] result = null;

            RestVariable variable = getVariableFromRequest(taskId, variableName, scope, true,
                    uriInfo.getBaseUri().toString());
            if (RestResponseFactory.BYTE_ARRAY_VARIABLE_TYPE.equals(variable.getType())) {
                result = (byte[]) variable.getValue();
                responseBuilder.type(MediaType.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();
                responseBuilder.type("application/x-java-serialized-object");

            } else {
                throw new ActivitiObjectNotFoundException("The variable does not have a binary data stream.", null);
            }
            return responseBuilder.entity(result).build();
        } catch (IOException ioe) {
            // Re-throw IOException
            throw new ActivitiException("Unexpected error getting variable data", ioe);
        }
    }

    @POST
    @Path("/{taskId}/variables")
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    public Response createBinaryTaskVariable(@PathParam("taskId") String taskId, MultipartBody multipartBody) {

        Task task = getTaskFromRequest(taskId);
        Object result = null;
        try {
            result = setBinaryVariable(multipartBody, task, true, uriInfo);
        } catch (IOException e) {
            throw new ActivitiIllegalArgumentException("Error Reading variable attachment", e);
        }

        if (result != null) {
            return Response.ok().status(Response.Status.CREATED).build();
        } else {
            throw new ActivitiIllegalArgumentException("Binary Task variable creation failed");
        }

    }

    @POST
    @Path("/{taskId}/variables")
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public Response createTaskVariable(@PathParam("taskId") String taskId,
            @Context HttpServletRequest httpServletRequest) {

        Task task = getTaskFromRequest(taskId);
        List<RestVariable> inputVariables = new ArrayList<>();
        List<RestVariable> resultVariables = new ArrayList<>();
        Response.ResponseBuilder responseBuilder = Response.ok();

        try {
            if (Utils.isApplicationJsonRequest(httpServletRequest)) {
                try {
                    @SuppressWarnings("unchecked")
                    List<Object> variableObjects = (List<Object>) new ObjectMapper()
                            .readValue(httpServletRequest.getInputStream(), List.class);
                    for (Object restObject : variableObjects) {
                        RestVariable restVariable = new ObjectMapper().convertValue(restObject, RestVariable.class);
                        inputVariables.add(restVariable);
                    }
                } catch (IOException e) {
                    throw new ActivitiIllegalArgumentException(
                            "request body could not be transformed to a RestVariable " + "instance.", e);
                }

            } else if (Utils.isApplicationXmlRequest(httpServletRequest)) {

                JAXBContext jaxbContext = null;
                try {
                    jaxbContext = JAXBContext.newInstance(RestVariableCollection.class);
                    Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
                    RestVariableCollection restVariableCollection = (RestVariableCollection) jaxbUnmarshaller
                            .unmarshal(httpServletRequest.getInputStream());
                    if (restVariableCollection == null) {
                        throw new ActivitiIllegalArgumentException("xml request body could not be transformed to a "
                                + "RestVariable Collection instance.");
                    }
                    List<RestVariable> restVariableList = restVariableCollection.getRestVariables();

                    if (restVariableList.size() == 0) {
                        throw new ActivitiIllegalArgumentException(
                                "xml request body could not identify any rest " + "variables to be updated");
                    }
                    for (RestVariable restVariable : restVariableList) {
                        inputVariables.add(restVariable);
                    }

                } catch (JAXBException | IOException e) {
                    throw new ActivitiIllegalArgumentException(
                            "xml request body could not be transformed to a " + "RestVariable instance.", e);
                }
            }

        } catch (Exception e) {
            throw new ActivitiIllegalArgumentException("Failed to serialize to a RestVariable instance", e);
        }

        if (inputVariables.size() == 0) {
            throw new ActivitiIllegalArgumentException("Request didn't contain a list of variables to create.");
        }

        RestVariable.RestVariableScope sharedScope = null;
        RestVariable.RestVariableScope varScope = null;
        Map<String, Object> variablesToSet = new HashMap<>();

        RestResponseFactory restResponseFactory = new RestResponseFactory();

        for (RestVariable var : inputVariables) {
            // Validate if scopes match
            varScope = var.getVariableScope();
            if (var.getName() == null) {
                throw new ActivitiIllegalArgumentException("Variable name is required");
            }

            if (varScope == null) {
                varScope = RestVariable.RestVariableScope.LOCAL;
            }
            if (sharedScope == null) {
                sharedScope = varScope;
            }
            if (varScope != sharedScope) {
                throw new ActivitiIllegalArgumentException(
                        "Only allowed to update multiple variables in the same scope.");
            }

            if (hasVariableOnScope(task, var.getName(), varScope)) {
                throw new BPMNConflictException(
                        "Variable '" + var.getName() + "' is already present on task '" + task.getId() + "'.");
            }

            Object actualVariableValue = restResponseFactory.getVariableValue(var);
            variablesToSet.put(var.getName(), actualVariableValue);
            resultVariables.add(restResponseFactory.createRestVariable(var.getName(), actualVariableValue, varScope,
                    task.getId(), RestResponseFactory.VARIABLE_TASK, false, uriInfo.getBaseUri().toString()));
        }

        RuntimeService runtimeService = BPMNOSGIService.getRuntimeService();

        TaskService taskService = BPMNOSGIService.getTaskService();

        if (!variablesToSet.isEmpty()) {
            if (sharedScope == RestVariable.RestVariableScope.LOCAL) {
                taskService.setVariablesLocal(task.getId(), variablesToSet);
            } else {
                if (task.getExecutionId() != null) {
                    // Explicitly set on execution, setting non-local variables on task will override local-variables if exists
                    runtimeService.setVariables(task.getExecutionId(), variablesToSet);
                } else {
                    // Standalone task, no global variables possible
                    throw new ActivitiIllegalArgumentException("Cannot set global variables on task '"
                            + task.getId() + "', task is not part of process.");
                }
            }
        }

        RestVariableCollection restVariableCollection = new RestVariableCollection();
        restVariableCollection.setRestVariables(resultVariables);
        responseBuilder.entity(restVariableCollection);

        return responseBuilder.status(Response.Status.CREATED).build();
    }

    @PUT
    @Path("/{taskId}/variables/{variableName}")
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    public Response updateBinaryTaskVariable(@PathParam("taskId") String taskId,
            @PathParam("variableName") String variableName, MultipartBody multipartBody) {
        Task task = getTaskFromRequest(taskId);
        RestVariable result = null;
        try {
            result = setBinaryVariable(multipartBody, task, false, uriInfo);
        } catch (IOException e) {
            throw new ActivitiIllegalArgumentException("Error Reading variable attachment", e);
        }
        if (result != null) {
            if (!result.getName().equals(variableName)) {
                throw new ActivitiIllegalArgumentException(
                        "Variable name in the body should be equal to the name used in the requested URL.");
            }
        }

        return Response.ok().entity(result).build();
    }

    @PUT
    @Path("/{taskId}/variables/{variableName}")
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public Response updateTaskVariable(@PathParam("taskId") String taskId,
            @PathParam("variableName") String variableName, @Context HttpServletRequest httpServletRequest) {
        Task task = getTaskFromRequest(taskId);

        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 = null;
            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 " + "Rest Variable 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.");
        }

        RestVariable result = setSimpleVariable(restVariable, task, false);
        return Response.ok().entity(result).build();
    }

    @DELETE
    @Path("/{taskId}/variables/{variableName}")
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public Response deleteVariable(@PathParam("taskId") String taskId,
            @PathParam("variableName") String variableName) {
        String scopeString = uriInfo.getQueryParameters().getFirst("scope");
        Task task = getTaskFromRequest(taskId);

        // Determine scope
        RestVariable.RestVariableScope scope = RestVariable.RestVariableScope.LOCAL;
        if (scopeString != null) {
            scope = RestVariable.getScopeFromString(scopeString);
        }

        if (!hasVariableOnScope(task, variableName, scope)) {
            throw new ActivitiObjectNotFoundException("Task '" + task.getId() + "' doesn't have a variable '"
                    + variableName + "' in scope " + scope.name().toLowerCase(), VariableInstanceEntity.class);
        }

        RuntimeService runtimeService = BPMNOSGIService.getRuntimeService();

        TaskService taskService = BPMNOSGIService.getTaskService();

        if (scope == RestVariable.RestVariableScope.LOCAL) {
            taskService.removeVariableLocal(task.getId(), variableName);
        } else {
            // Safe to use executionId, as the hasVariableOnScope whould have stopped a global-var update on standalone task
            runtimeService.removeVariable(task.getExecutionId(), variableName);
        }

        return Response.ok().status(Response.Status.NO_CONTENT).build();
    }

    @DELETE
    @Path("/{taskId}/variables")
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public Response deleteAllLocalTaskVariables(@PathParam("taskId") String taskId) {
        TaskService taskService = BPMNOSGIService.getTaskService();

        Task task = getTaskFromRequest(taskId);
        Collection<String> currentVariables = taskService.getVariablesLocal(task.getId()).keySet();
        taskService.removeVariablesLocal(task.getId(), currentVariables);

        return Response.ok().status(Response.Status.NO_CONTENT).build();
    }

    @GET
    @Path("/{taskId}/identitylinks")
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public Response getIdentityLinks(@PathParam("taskId") String taskId) {
        Task task = getTaskFromRequest(taskId);
        TaskService taskService = BPMNOSGIService.getTaskService();

        List<RestIdentityLink> restIdentityLinks = new RestResponseFactory().createRestIdentityLinks(
                taskService.getIdentityLinksForTask(task.getId()), uriInfo.getBaseUri().toString());
        RestIdentityLinkCollection restIdentityLinkCollection = new RestIdentityLinkCollection();
        restIdentityLinkCollection.setRestIdentityLinks(restIdentityLinks);
        return Response.ok().entity(restIdentityLinkCollection).build();
    }

    @GET
    @Path("/{taskId}/identitylinks/{family}")
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public Response getIdentityLinksForFamily(@PathParam("taskId") String taskId,
            @PathParam("family") String family) {

        Task task = getTaskFromRequest(taskId);

        TaskService taskService = BPMNOSGIService.getTaskService();

        if (family == null || (!RestUrls.SEGMENT_IDENTITYLINKS_FAMILY_GROUPS.equals(family)
                && !RestUrls.SEGMENT_IDENTITYLINKS_FAMILY_USERS.equals(family))) {
            throw new ActivitiIllegalArgumentException("Identity link family should be 'users' or 'groups'.");
        }

        boolean isUser = family.equals(RestUrls.SEGMENT_IDENTITYLINKS_FAMILY_USERS);
        List<RestIdentityLink> results = new ArrayList<RestIdentityLink>();

        List<IdentityLink> allLinks = taskService.getIdentityLinksForTask(task.getId());
        for (IdentityLink link : allLinks) {
            boolean match = false;
            if (isUser) {
                match = link.getUserId() != null;
            } else {
                match = link.getGroupId() != null;
            }

            if (match) {
                results.add(
                        new RestResponseFactory().createRestIdentityLink(link, uriInfo.getBaseUri().toString()));
            }
        }
        RestIdentityLinkCollection restIdentityLinkCollection = new RestIdentityLinkCollection();
        restIdentityLinkCollection.setRestIdentityLinks(results);
        return Response.ok().entity(restIdentityLinkCollection).build();
    }

    @GET
    @Path("/{taskId}/identitylinks/{family}/{identityId}/{type}")
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public Response getIdentityLink(@PathParam("taskId") String taskId, @PathParam("family") String family,
            @PathParam("identityId") String identityId, @PathParam("type") String type,
            @Context HttpServletRequest httpServletRequest) {

        Task task = getTaskFromRequest(taskId);
        validateIdentityLinkArguments(family, identityId, type);

        IdentityLink link = getIdentityLink(family, identityId, type, task.getId());

        return Response.ok()
                .entity(new RestResponseFactory().createRestIdentityLink(link, uriInfo.getBaseUri().toString()))
                .build();
    }

    @DELETE
    @Path("/{taskId}/identitylinks/{family}/{identityId}/{type}")
    public Response deleteIdentityLink(@PathParam("taskId") String taskId, @PathParam("family") String family,
            @PathParam("identityId") String identityId, @PathParam("type") String type) {

        Task task = getTaskFromRequest(taskId);

        validateIdentityLinkArguments(family, identityId, type);

        // Check if identitylink to delete exists
        getIdentityLink(family, identityId, type, task.getId());

        TaskService taskService = BPMNOSGIService.getTaskService();

        if (RestUrls.SEGMENT_IDENTITYLINKS_FAMILY_USERS.equals(family)) {
            taskService.deleteUserIdentityLink(task.getId(), identityId, type);
        } else {
            taskService.deleteGroupIdentityLink(task.getId(), identityId, type);
        }

        return Response.ok().status(Response.Status.NO_CONTENT).build();
    }

    @POST
    @Path("/{taskId}/identitylinks")
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public Response createIdentityLink(@PathParam("taskId") String taskId, RestIdentityLink identityLink) {

        Task task = getTaskFromRequest(taskId);

        if (identityLink.getGroup() == null && identityLink.getUser() == null) {
            throw new ActivitiIllegalArgumentException("A group or a user is required to create an identity link.");
        }

        if (identityLink.getGroup() != null && identityLink.getUser() != null) {
            throw new ActivitiIllegalArgumentException(
                    "Only one of user or group can be used to create an identity link.");
        }

        if (identityLink.getType() == null) {
            throw new ActivitiIllegalArgumentException("The identity link type is required.");
        }
        TaskService taskService = BPMNOSGIService.getTaskService();

        if (identityLink.getGroup() != null) {
            taskService.addGroupIdentityLink(task.getId(), identityLink.getGroup(), identityLink.getType());
        } else {
            taskService.addUserIdentityLink(task.getId(), identityLink.getUser(), identityLink.getType());
        }

        RestIdentityLink restIdentityLink = new RestResponseFactory().createRestIdentityLink(identityLink.getType(),
                identityLink.getUser(), identityLink.getGroup(), task.getId(), null, null,
                uriInfo.getBaseUri().toString());

        return Response.ok().status(Response.Status.CREATED).entity(restIdentityLink).build();
    }

    @POST
    @Path("/{taskId}/attachments")
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    public Response createAttachmentForBinary(@PathParam("taskId") String taskId, MultipartBody multipartBody,
            @Context HttpServletRequest httpServletRequest) {

        boolean debugEnabled = log.isDebugEnabled();
        List<org.apache.cxf.jaxrs.ext.multipart.Attachment> attachments = multipartBody.getAllAttachments();

        int attachmentSize = attachments.size();

        if (attachmentSize <= 0) {
            throw new ActivitiIllegalArgumentException("No Attachments found with the request body");
        }
        AttachmentDataHolder attachmentDataHolder = new AttachmentDataHolder();

        for (int i = 0; i < attachmentSize; i++) {
            org.apache.cxf.jaxrs.ext.multipart.Attachment attachment = attachments.get(i);

            String contentDispositionHeaderValue = attachment.getHeader("Content-Disposition");
            String contentType = attachment.getHeader("Content-Type");

            if (debugEnabled) {
                log.debug("Going to iterate:" + i);
                log.debug("contentDisposition:" + contentDispositionHeaderValue);
            }

            if (contentDispositionHeaderValue != null) {
                contentDispositionHeaderValue = contentDispositionHeaderValue.trim();

                Map<String, String> contentDispositionHeaderValueMap = Utils
                        .processContentDispositionHeader(contentDispositionHeaderValue);
                String dispositionName = contentDispositionHeaderValueMap.get("name");
                DataHandler dataHandler = attachment.getDataHandler();

                OutputStream outputStream = null;

                if ("name".equals(dispositionName)) {
                    try {
                        outputStream = Utils.getAttachmentStream(dataHandler.getInputStream());
                    } catch (IOException e) {
                        throw new ActivitiIllegalArgumentException("Attachment Name Reading error occured", e);
                    }

                    if (outputStream != null) {
                        String fileName = outputStream.toString();
                        attachmentDataHolder.setName(fileName);
                    }

                } else if ("type".equals(dispositionName)) {
                    try {
                        outputStream = Utils.getAttachmentStream(dataHandler.getInputStream());
                    } catch (IOException e) {
                        throw new ActivitiIllegalArgumentException("Attachment Type Reading error occured", e);
                    }

                    if (outputStream != null) {
                        String typeName = outputStream.toString();
                        attachmentDataHolder.setType(typeName);
                    }

                } else if ("description".equals(dispositionName)) {
                    try {
                        outputStream = Utils.getAttachmentStream(dataHandler.getInputStream());
                    } catch (IOException e) {
                        throw new ActivitiIllegalArgumentException("Attachment Description Reading error occured",
                                e);
                    }

                    if (outputStream != null) {
                        String description = outputStream.toString();
                        attachmentDataHolder.setDescription(description);
                    }
                }

                if (contentType != null) {
                    if ("file".equals(dispositionName)) {

                        InputStream inputStream = null;
                        try {
                            inputStream = dataHandler.getInputStream();
                        } catch (IOException e) {
                            throw new ActivitiIllegalArgumentException(
                                    "Error Occured During processing empty body.", e);
                        }

                        if (inputStream != null) {
                            attachmentDataHolder.setContentType(contentType);
                            byte[] attachmentArray = new byte[0];
                            try {
                                attachmentArray = IOUtils.toByteArray(inputStream);
                            } catch (IOException e) {
                                throw new ActivitiIllegalArgumentException("Processing Attachment Body Failed.", e);
                            }
                            attachmentDataHolder.setAttachmentArray(attachmentArray);
                        }
                    }
                }
            }
        }

        attachmentDataHolder.printDebug();

        if (attachmentDataHolder.getName() == null) {
            throw new ActivitiIllegalArgumentException("Attachment name is required.");
        }

        if (attachmentDataHolder.getAttachmentArray() == null) {
            throw new ActivitiIllegalArgumentException(
                    "Empty attachment body was found in request body after " + "decoding the request" + ".");
        }

        TaskService taskService = BPMNOSGIService.getTaskService();
        Task task = getTaskFromRequest(taskId);
        Response.ResponseBuilder responseBuilder = Response.ok();

        AttachmentResponse result = null;
        try {

            InputStream inputStream = new ByteArrayInputStream(attachmentDataHolder.getAttachmentArray());
            Attachment createdAttachment = taskService.createAttachment(attachmentDataHolder.getContentType(),
                    task.getId(), task.getProcessInstanceId(), attachmentDataHolder.getName(),
                    attachmentDataHolder.getDescription(), inputStream);

            responseBuilder.status(Response.Status.CREATED);
            result = new RestResponseFactory().createAttachmentResponse(createdAttachment,
                    uriInfo.getBaseUri().toString());
        } catch (Exception e) {
            throw new ActivitiException("Error creating attachment response", e);
        }

        return responseBuilder.status(Response.Status.CREATED).entity(result).build();
    }

    @POST
    @Path("/{taskId}/attachments")
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public Response createAttachmentForNonBinary(@PathParam("taskId") String taskId,
            @Context HttpServletRequest httpServletRequest, AttachmentRequest attachmentRequest) {

        AttachmentResponse result = null;
        Task task = getTaskFromRequest(taskId);
        Response.ResponseBuilder responseBuilder = Response.ok();
        if (attachmentRequest == null) {
            throw new ActivitiIllegalArgumentException("AttachmentRequest properties not found in request");
        }

        result = createSimpleAttachment(attachmentRequest, task);
        return responseBuilder.status(Response.Status.CREATED).entity(result).build();
    }

    @GET
    @Path("/{taskId}/attachments")
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public Response getAttachments(@PathParam("taskId") String taskId) {
        List<AttachmentResponse> result = new ArrayList<AttachmentResponse>();
        HistoricTaskInstance task = getHistoricTaskFromRequest(taskId);

        TaskService taskService = BPMNOSGIService.getTaskService();

        RestResponseFactory restResponseFactory = new RestResponseFactory();
        String baseUri = uriInfo.getBaseUri().toString();

        for (Attachment attachment : taskService.getProcessInstanceAttachments(task.getProcessInstanceId())) {
            result.add(restResponseFactory.createAttachmentResponse(attachment, baseUri));
        }

        AttachmentResponseCollection attachmentResponseCollection = new AttachmentResponseCollection();
        attachmentResponseCollection.setAttachmentResponseList(result);

        return Response.ok().entity(attachmentResponseCollection).build();
    }

    @GET
    @Path("/{taskId}/attachments/{attachmentId}")
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public Response getAttachment(@PathParam("taskId") String taskId,
            @PathParam("attachmentId") String attachmentId) {

        HistoricTaskInstance task = getHistoricTaskFromRequest(taskId);
        TaskService taskService = BPMNOSGIService.getTaskService();

        Attachment attachment = taskService.getAttachment(attachmentId);
        if (attachment == null) {
            throw new ActivitiObjectNotFoundException(
                    "Task '" + task.getId() + "' doesn't have an attachment with id '" + attachmentId + "'.",
                    Comment.class);
        }

        return Response.ok().entity(
                new RestResponseFactory().createAttachmentResponse(attachment, uriInfo.getBaseUri().toString()))
                .build();
    }

    @DELETE
    @Path("/{taskId}/attachments/{attachmentId}")
    public Response deleteAttachment(@PathParam("taskId") String taskId,
            @PathParam("attachmentId") String attachmentId) {

        Task task = getTaskFromRequest(taskId);
        TaskService taskService = BPMNOSGIService.getTaskService();
        Attachment attachment = taskService.getAttachment(attachmentId);
        if (attachment == null) {
            throw new ActivitiObjectNotFoundException(
                    "Task '" + task.getId() + "' doesn't have an attachment with id '" + attachmentId + "'.",
                    Comment.class);
        }

        taskService.deleteAttachment(attachmentId);

        return Response.ok().status(Response.Status.NO_CONTENT).build();
    }

    @GET
    @Path("/{taskId}/attachments/{attachmentId}/content")
    public Response getAttachmentContent(@PathParam("taskId") String taskId,
            @PathParam("attachmentId") String attachmentId) {

        TaskService taskService = BPMNOSGIService.getTaskService();
        HistoricTaskInstance task = getHistoricTaskFromRequest(taskId);
        Attachment attachment = taskService.getAttachment(attachmentId);

        if (attachment == null) {
            throw new ActivitiObjectNotFoundException(
                    "Task '" + task.getId() + "' doesn't have an attachment with id '" + attachmentId + "'.",
                    Attachment.class);
        }

        InputStream attachmentStream = taskService.getAttachmentContent(attachmentId);
        if (attachmentStream == null) {
            throw new ActivitiObjectNotFoundException(
                    "Attachment with id '" + attachmentId + "' doesn't have content associated with it.",
                    Attachment.class);
        }

        Response.ResponseBuilder responseBuilder = Response.ok();

        String type = attachment.getType();
        MediaType mediaType = MediaType.valueOf(type);
        if (mediaType != null) {
            responseBuilder.type(mediaType);
        } else {
            responseBuilder.type("application/octet-stream");
        }

        byte[] attachmentArray;
        try {
            attachmentArray = IOUtils.toByteArray(attachmentStream);
        } catch (IOException e) {
            throw new ActivitiException("Error creating attachment data", e);
        }
        String dispositionValue = "inline; filename=\"" + attachment.getName() + "\"";
        responseBuilder.header("Content-Disposition", dispositionValue);
        return responseBuilder.entity(attachmentArray).build();
    }

    @GET
    @Path("/{taskId}/comments")
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public Response getComments(@PathParam("taskId") String taskId) {
        HistoricTaskInstance task = getHistoricTaskFromRequest(taskId);
        TaskService taskService = BPMNOSGIService.getTaskService();
        List<CommentResponse> commentResponseList = new RestResponseFactory()
                .createRestCommentList(taskService.getTaskComments(task.getId()), uriInfo.getBaseUri().toString());
        CommentResponseCollection commentResponseCollection = new CommentResponseCollection();
        commentResponseCollection.setCommentResponseList(commentResponseList);

        return Response.ok().entity(commentResponseCollection).build();
    }

    @POST
    @Path("/{taskId}/comments")
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public Response createComment(@PathParam("taskId") String taskId, CommentRequest comment) {

        Task task = getTaskFromRequest(taskId);

        if (comment.getMessage() == null) {
            throw new ActivitiIllegalArgumentException("Comment text is required.");
        }

        String processInstanceId = null;
        TaskService taskService = BPMNOSGIService.getTaskService();
        if (comment.isSaveProcessInstanceId()) {
            Task taskEntity = taskService.createTaskQuery().taskId(task.getId()).singleResult();
            processInstanceId = taskEntity.getProcessInstanceId();
        }
        Comment createdComment = taskService.addComment(task.getId(), processInstanceId, comment.getMessage());

        CommentResponse commentResponse = new RestResponseFactory().createRestComment(createdComment,
                uriInfo.getBaseUri().toString());
        return Response.ok().status(Response.Status.CREATED).entity(commentResponse).build();
    }

    @GET
    @Path("/{taskId}/comments/{commentId}")
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public Response getComment(@PathParam("taskId") String taskId, @PathParam("commentId") String commentId) {

        HistoricTaskInstance task = getHistoricTaskFromRequest(taskId);
        TaskService taskService = BPMNOSGIService.getTaskService();
        Comment comment = taskService.getComment(commentId);
        if (comment == null || !task.getId().equals(comment.getTaskId())) {
            throw new ActivitiObjectNotFoundException(
                    "Task '" + task.getId() + "' doesn't have a comment with id '" + commentId + "'.",
                    Comment.class);
        }

        return Response.ok()
                .entity(new RestResponseFactory().createRestComment(comment, uriInfo.getBaseUri().toString()))
                .build();
    }

    @DELETE
    @Path("/{taskId}/comments/{commentId}")
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public Response deleteComment(@PathParam("taskId") String taskId, @PathParam("commentId") String commentId) {

        // Check if task exists
        Task task = getTaskFromRequest(taskId);
        TaskService taskService = BPMNOSGIService.getTaskService();

        Comment comment = taskService.getComment(commentId);
        if (comment == null || comment.getTaskId() == null || !comment.getTaskId().equals(task.getId())) {
            throw new ActivitiObjectNotFoundException(
                    "Task '" + task.getId() + "' doesn't have a comment with id '" + commentId + "'.",
                    Comment.class);
        }

        taskService.deleteComment(commentId);
        return Response.ok().status(Response.Status.NO_CONTENT).build();
    }

    @GET
    @Path("/{taskId}/events")
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public Response getEvents(@PathParam("taskId") String taskId) {
        HistoricTaskInstance task = getHistoricTaskFromRequest(taskId);
        TaskService taskService = BPMNOSGIService.getTaskService();
        List<EventResponse> eventResponseList = new RestResponseFactory()
                .createEventResponseList(taskService.getTaskEvents(task.getId()), uriInfo.getBaseUri().toString());

        EventResponseCollection eventResponseCollection = new EventResponseCollection();
        eventResponseCollection.setEventResponseList(eventResponseList);

        return Response.ok().entity(eventResponseCollection).build();
    }

    @GET
    @Path("/{taskId}/events/{eventId}")
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public Response getEvent(@PathParam("taskId") String taskId, @PathParam("eventId") String eventId) {

        HistoricTaskInstance task = getHistoricTaskFromRequest(taskId);
        TaskService taskService = BPMNOSGIService.getTaskService();

        Event event = taskService.getEvent(eventId);
        if (event == null || !task.getId().equals(event.getTaskId())) {
            throw new ActivitiObjectNotFoundException(
                    "Task '" + task.getId() + "' doesn't have an event with id '" + eventId + "'.", Event.class);
        }

        EventResponse eventResponse = new RestResponseFactory().createEventResponse(event,
                uriInfo.getBaseUri().toString());
        return Response.ok().entity(eventResponse).build();
    }

    @DELETE
    @Path("/{taskd}/events/{eventId}")
    public Response deleteEvent(@PathParam("taskId") String taskId, @PathParam("eventId") String eventId) {

        // Check if task exists
        Task task = getTaskFromRequest(taskId);
        TaskService taskService = BPMNOSGIService.getTaskService();
        Event event = taskService.getEvent(eventId);
        if (event == null || event.getTaskId() == null || !event.getTaskId().equals(task.getId())) {
            throw new ActivitiObjectNotFoundException(
                    "Task '" + task.getId() + "' doesn't have an event with id '" + event + "'.", Event.class);
        }

        taskService.deleteComment(eventId);
        return Response.ok().status(Response.Status.NO_CONTENT).build();
    }

    protected HistoricTaskInstance getHistoricTaskFromRequest(String taskId) {

        HistoryService historyService = BPMNOSGIService.getHistoryService();

        HistoricTaskInstance task = historyService.createHistoricTaskInstanceQuery().taskId(taskId).singleResult();
        if (task == null) {
            throw new ActivitiObjectNotFoundException("Could not find a task with id '" + taskId + "'.",
                    Task.class);
        }
        return task;
    }

    protected AttachmentResponse createSimpleAttachment(AttachmentRequest attachmentRequest, Task task) {

        if (attachmentRequest.getName() == null) {
            throw new ActivitiIllegalArgumentException("Attachment name is required.");
        }

        TaskService taskService = BPMNOSGIService.getTaskService();

        Attachment createdAttachment = taskService.createAttachment(attachmentRequest.getType(), task.getId(),
                task.getProcessInstanceId(), attachmentRequest.getName(), attachmentRequest.getDescription(),
                attachmentRequest.getExternalUrl());

        return new RestResponseFactory().createAttachmentResponse(createdAttachment,
                uriInfo.getBaseUri().toString());
    }

    /*    protected AttachmentResponse createBinaryAttachment(HttpServletRequest httpServletRequest, Task task, Response.ResponseBuilder responseBuilder) throws
        IOException {
        
    String name = uriInfo.getQueryParameters().getFirst("name");
    String description = uriInfo.getQueryParameters().getFirst("description");
    String type = uriInfo.getQueryParameters().getFirst("type");
        
        
    byte[] byteArray = Utils.processMultiPartFile(httpServletRequest, "Attachment content");
    if (byteArray == null) {
        throw new ActivitiIllegalArgumentException("Empty attachment body was found in request body after " +
                "decoding the request" +
                ".");
    }
        
    if (name == null) {
        throw new ActivitiIllegalArgumentException("Attachment name is required.");
    }
        
        
    TaskService taskService = BPMNOSGIService.getTaskService();
        
    try {
        InputStream inputStream = new ByteArrayInputStream(byteArray);
        Attachment createdAttachment = taskService.createAttachment(type, task.getId(), task.getProcessInstanceId(), name,
                description, inputStream);
        
        responseBuilder.status(Response.Status.CREATED);
        return new RestResponseFactory().createAttachmentResponse(createdAttachment, uriInfo.getBaseUri().toString());
        
    } catch (Exception e) {
        throw new ActivitiException("Error creating attachment response", e);
    }
        }*/

    protected IdentityLink getIdentityLink(String family, String identityId, String type, String taskId) {
        boolean isUser = family.equals(RestUrls.SEGMENT_IDENTITYLINKS_FAMILY_USERS);

        TaskService taskService = BPMNOSGIService.getTaskService();
        // Perhaps it would be better to offer getting a single identitylink from the API
        List<IdentityLink> allLinks = taskService.getIdentityLinksForTask(taskId);
        for (IdentityLink link : allLinks) {
            boolean rightIdentity = false;
            if (isUser) {
                rightIdentity = identityId.equals(link.getUserId());
            } else {
                rightIdentity = identityId.equals(link.getGroupId());
            }

            if (rightIdentity && link.getType().equals(type)) {
                return link;
            }
        }
        throw new ActivitiObjectNotFoundException("Could not find the requested identity link.",
                IdentityLink.class);
    }

    protected void validateIdentityLinkArguments(String family, String identityId, String type) {
        if (family == null || (!RestUrls.SEGMENT_IDENTITYLINKS_FAMILY_GROUPS.equals(family)
                && !RestUrls.SEGMENT_IDENTITYLINKS_FAMILY_USERS.equals(family))) {
            throw new ActivitiIllegalArgumentException("Identity link family should be 'users' or 'groups'.");
        }
        if (identityId == null) {
            throw new ActivitiIllegalArgumentException("IdentityId is required.");
        }
        if (type == null) {
            throw new ActivitiIllegalArgumentException("Type is required.");
        }
    }

    protected RestVariable setSimpleVariable(RestVariable restVariable, Task task, boolean isNew) {
        if (restVariable.getName() == null) {
            throw new ActivitiIllegalArgumentException("Variable name is required");
        }

        // Figure out scope, revert to local is omitted
        RestVariable.RestVariableScope scope = restVariable.getVariableScope();
        if (scope == null) {
            scope = RestVariable.RestVariableScope.LOCAL;
        }

        RestResponseFactory restResponseFactory = new RestResponseFactory();
        Object actualVariableValue = restResponseFactory.getVariableValue(restVariable);
        setVariable(task, restVariable.getName(), actualVariableValue, scope, isNew);

        return restResponseFactory.createRestVariable(restVariable.getName(), actualVariableValue, scope,
                task.getId(), RestResponseFactory.VARIABLE_TASK, false, uriInfo.getBaseUri().toString());
    }

    protected void completeTask(Task task, TaskActionRequest actionRequest, TaskService taskService) {

        if (actionRequest.getVariables() != null) {
            Map<String, Object> variablesToSet = new HashMap<String, Object>();

            RestResponseFactory restResponseFactory = new RestResponseFactory();
            for (RestVariable var : actionRequest.getVariables()) {
                if (var.getName() == null) {
                    throw new ActivitiIllegalArgumentException("Variable name is required");
                }

                Object actualVariableValue = restResponseFactory.getVariableValue(var);
                variablesToSet.put(var.getName(), actualVariableValue);
            }

            taskService.complete(task.getId(), variablesToSet);
        } else {
            taskService.complete(task.getId());
        }

    }

    protected void claimTask(Task task, TaskActionRequest actionRequest, TaskService taskService) {
        // In case the task is already claimed, a ActivitiTaskAlreadyClaimedException is thrown and converted to
        // a CONFLICT response by the ExceptionHandlerAdvice
        List list = taskService.createTaskQuery().taskId(task.getId())
                .taskCandidateUser(actionRequest.getAssignee()).list();
        if (list == null || list.isEmpty()) {//user is not a candidate
            throw new BPMNForbiddenException(
                    "The user:" + actionRequest.getAssignee() + " is not a candidate user of this task");
        } else {
            taskService.claim(task.getId(), actionRequest.getAssignee());
        }

    }

    protected void delegateTask(Task task, TaskActionRequest actionRequest, TaskService taskService) {
        if (actionRequest.getAssignee() == null) {
            throw new ActivitiIllegalArgumentException("An assignee is required when delegating a task.");
        }

        taskService.delegateTask(task.getId(), actionRequest.getAssignee());
    }

    protected void resolveTask(Task task, TaskService taskService) {
        taskService.resolveTask(task.getId());
    }

    protected void populateTaskFromRequest(Task task, TaskRequest taskRequest) {
        if (taskRequest.isNameSet()) {
            task.setName(taskRequest.getName());
        }
        if (taskRequest.isAssigneeSet()) {
            task.setAssignee(taskRequest.getAssignee());
        }
        if (taskRequest.isDescriptionSet()) {
            task.setDescription(taskRequest.getDescription());
        }
        if (taskRequest.isDuedateSet()) {
            task.setDueDate(taskRequest.getDueDate());
        }
        if (taskRequest.isOwnerSet()) {
            task.setOwner(taskRequest.getOwner());
        }
        if (taskRequest.isParentTaskIdSet()) {
            task.setParentTaskId(taskRequest.getParentTaskId());
        }
        if (taskRequest.isPrioritySet()) {
            task.setPriority(taskRequest.getPriority());
        }
        if (taskRequest.isCategorySet()) {
            task.setCategory(taskRequest.getCategory());
        }
        if (taskRequest.isTenantIdSet()) {
            task.setTenantId(taskRequest.getTenantId());
        }
        if (taskRequest.isFormKeySet()) {
            task.setFormKey(taskRequest.getFormKey());
        }

        if (taskRequest.isDelegationStateSet()) {
            DelegationState delegationState = getDelegationState(taskRequest.getDelegationState());
            task.setDelegationState(delegationState);
        }
    }
}