/*
* The contents of this file are subject to the terms of the Common Development and
* Distribution License (the License). You may not use this file except in compliance with the
* License.
*
* You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
* specific language governing permission and limitations under the License.
*
* When distributing Covered Software, include this CDDL Header Notice in each file and include
* the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
* Header, with the fields enclosed by brackets [] replaced by your own identifying
* information: "Portions copyright [year] [name of copyright owner]".
*
* Copyright 2015 ForgeRock AS.
*/
package org.forgerock.openidm.workflow.activiti.impl;
import static org.forgerock.json.resource.Responses.newQueryResponse;
import static org.forgerock.json.resource.Responses.newResourceResponse;
import static org.forgerock.openidm.util.ResourceUtil.notSupportedOnInstance;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricTaskInstanceQuery;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.impl.persistence.entity.HistoricTaskInstanceEntity;
import org.forgerock.services.context.Context;
import org.forgerock.json.JsonValue;
import org.forgerock.json.resource.ActionRequest;
import org.forgerock.json.resource.ActionResponse;
import org.forgerock.json.resource.BadRequestException;
import org.forgerock.json.resource.CreateRequest;
import org.forgerock.json.resource.CollectionResourceProvider;
import org.forgerock.json.resource.DeleteRequest;
import org.forgerock.json.resource.InternalServerErrorException;
import org.forgerock.json.resource.NotSupportedException;
import org.forgerock.json.resource.PatchRequest;
import org.forgerock.json.resource.QueryRequest;
import org.forgerock.json.resource.QueryResourceHandler;
import org.forgerock.json.resource.QueryResponse;
import org.forgerock.json.resource.ReadRequest;
import org.forgerock.json.resource.ResourceException;
import org.forgerock.json.resource.ResourceResponse;
import org.forgerock.services.context.SecurityContext;
import org.forgerock.json.resource.SortKey;
import org.forgerock.json.resource.UpdateRequest;
import org.forgerock.openidm.workflow.activiti.ActivitiConstants;
import org.forgerock.openidm.workflow.activiti.impl.mixin.HistoricTaskInstanceEntityMixIn;
import org.forgerock.util.promise.Promise;
import java.util.Arrays;
import java.util.Map;
/**
* Resource implementation of HistoricTaskInstance related to Activiti operations.
*/
public class TaskInstanceHistoryResource implements CollectionResourceProvider {
private static final ObjectMapper MAPPER;
private ProcessEngine processEngine;
static {
MAPPER = new ObjectMapper();
MAPPER.addMixIn(HistoricTaskInstanceEntity.class,
HistoricTaskInstanceEntityMixIn.class);
MAPPER.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
MAPPER.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true);
}
/**
* Construct the TaskInstanceHistoryResource.
*
* @param processEngine the Activiti engine used for this resource
*/
public TaskInstanceHistoryResource(ProcessEngine processEngine) {
this.processEngine = processEngine;
}
/**
* {@inheritDoc}
*/
@Override
public Promise<ActionResponse, ResourceException> actionCollection(Context context, ActionRequest request) {
return notSupportedOnInstance(request).asPromise();
}
/**
* {@inheritDoc}
*/
@Override
public Promise<ActionResponse, ResourceException> actionInstance(Context context, String resourceId, ActionRequest request) {
return notSupportedOnInstance(request).asPromise();
}
/**
* {@inheritDoc}
*/
@Override
public Promise<ResourceResponse, ResourceException> createInstance(Context context, CreateRequest request) {
return notSupportedOnInstance(request).asPromise();
}
/**
* {@inheritDoc}
*/
@Override
public Promise<ResourceResponse, ResourceException> deleteInstance(Context context, String resourceId, DeleteRequest request) {
return notSupportedOnInstance(request).asPromise();
}
/**
* {@inheritDoc}
*/
@Override
public Promise<ResourceResponse, ResourceException> patchInstance(Context context, String resourceId, PatchRequest request) {
return notSupportedOnInstance(request).asPromise();
}
/**
* {@inheritDoc}
*/
@Override
public Promise<QueryResponse, ResourceException> queryCollection(Context context, QueryRequest request, QueryResourceHandler handler) {
try {
Authentication.setAuthenticatedUserId(context.asContext(SecurityContext.class).getAuthenticationId());
HistoricTaskInstanceQuery query = processEngine.getHistoryService().createHistoricTaskInstanceQuery();
if (ActivitiConstants.QUERY_FILTERED.equals(request.getQueryId())
|| ActivitiConstants.QUERY_ALL_IDS.equals(request.getQueryId())) {
if (ActivitiConstants.QUERY_FILTERED.equals(request.getQueryId())) {
setTaskParams(query, request);
setSortKeys(query, request);
}
for (HistoricTaskInstance i : query.list()) {
Map<String, Object> value = MAPPER.convertValue(i, Map.class);
handler.handleResource(newResourceResponse(i.getId(), null, new JsonValue(value)));
}
return newQueryResponse().asPromise();
} else {
return new BadRequestException("Unknown query-id").asPromise();
}
} catch (Exception ex) {
return new InternalServerErrorException(ex.getMessage(), ex).asPromise();
}
}
/**
* {@inheritDoc}
*/
@Override
public Promise<ResourceResponse, ResourceException> readInstance(Context context, String resourceId, ReadRequest request) {
return notSupportedOnInstance(request).asPromise();
}
/**
* {@inheritDoc}
*/
@Override
public Promise<ResourceResponse, ResourceException> updateInstance(Context context, String resourceId, UpdateRequest request) {
return notSupportedOnInstance(request).asPromise();
}
/**
* Process the query parameters of the request and set it on the HistoricTaskInstanceQuery
* being passed in.
*
* @param query Query to update
* @param request incoming request
*/
private void setTaskParams(HistoricTaskInstanceQuery query, QueryRequest request) {
for (Map.Entry<String, String> param : request.getAdditionalParameters().entrySet()) {
switch (param.getKey()) {
case ActivitiConstants.ACTIVITI_EXECUTIONID:
query.executionId(param.getValue());
break;
case ActivitiConstants.ACTIVITI_PROCESSDEFINITIONID:
query.processDefinitionId(param.getValue());
break;
case ActivitiConstants.ACTIVITI_PROCESSDEFINITIONKEY:
query.processDefinitionKey(param.getValue());
break;
case ActivitiConstants.ACTIVITI_PROCESSINSTANCEID:
query.processInstanceId(param.getValue());
break;
case ActivitiConstants.ACTIVITI_ASSIGNEE:
query.taskAssignee(param.getValue());
break;
case ActivitiConstants.ACTIVITI_CANDIDATEGROUP:
String[] taskCandidateGroups = param.getValue().split(",");
if (taskCandidateGroups.length > 1) {
query.taskCandidateGroupIn(Arrays.asList(taskCandidateGroups));
} else {
query.taskCandidateGroup(param.getValue());
}
break;
case ActivitiConstants.ACTIVITI_CANDIDATEUSER:
query.taskCandidateUser(param.getValue());
break;
case ActivitiConstants.ACTIVITI_TASKID:
query.taskId(param.getValue());
break;
case ActivitiConstants.ACTIVITI_TASKNAME:
query.taskName(param.getValue());
break;
case ActivitiConstants.ACTIVITI_OWNER:
query.taskOwner(param.getValue());
break;
case ActivitiConstants.ACTIVITI_DESCRIPTION:
query.taskDescription(param.getValue());
break;
case ActivitiConstants.ACTIVITI_FINISHED:
if (Boolean.parseBoolean(param.getValue())) {
query.finished();
}
break;
case ActivitiConstants.ACTIVITI_UNFINISHED:
if (Boolean.parseBoolean(param.getValue())) {
query.unfinished();
}
break;
case ActivitiConstants.ACTIVITI_PROCESSFINISHED:
if (Boolean.parseBoolean(param.getValue())) {
query.processFinished();
}
break;
case ActivitiConstants.ACTIVITI_PROCESSUNFINISHED:
if (Boolean.parseBoolean(param.getValue())) {
query.processUnfinished();
}
break;
case ActivitiConstants.ACTIVITI_PRIORITY:
query.taskPriority(Integer.parseInt(param.getValue()));
break;
case ActivitiConstants.ACTIVITI_DELETEREASON:
query.taskDeleteReason(param.getValue());
break;
case ActivitiConstants.ACTIVITI_TENANTID:
query.taskTenantId(param.getValue());
break;
}
}
// handle instance variables that can be queried on as well
for (Map.Entry<String, String> entry : ActivitiUtil.fetchVarParams(request).entrySet()) {
query = query.processVariableValueEquals(entry.getKey(), entry.getValue());
}
}
/**
* Sets what the result set should be filtered by.
*
* @param query TaskQuery that needs to be modified for filtering
* @param request incoming request
* @throws NotSupportedException
*/
private void setSortKeys(HistoricTaskInstanceQuery query, QueryRequest request) throws NotSupportedException {
for (SortKey key : request.getSortKeys()) {
if (key.getField() != null && !key.getField().isEmpty()) {
switch (key.getField().toString().substring(1)) { // remove leading JsonPointer slash
case ActivitiConstants.ACTIVITI_TASKID:
query.orderByTaskId();
break;
case ActivitiConstants.ACTIVITI_TASKNAME:
query.orderByTaskName();
break;
case ActivitiConstants.ACTIVITI_DESCRIPTION:
query.orderByTaskDescription();
break;
case ActivitiConstants.ACTIVITI_PRIORITY:
query.orderByTaskPriority();
break;
case ActivitiConstants.ACTIVITI_ASSIGNEE:
query.orderByTaskAssignee();
break;
case ActivitiConstants.ACTIVITI_PROCESSINSTANCEID:
query.orderByProcessInstanceId();
break;
case ActivitiConstants.ACTIVITI_EXECUTIONID:
query.orderByExecutionId();
break;
case ActivitiConstants.ACTIVITI_TENANTID:
query.orderByTenantId();
break;
case ActivitiConstants.ID:
query.orderByHistoricActivityInstanceId();
break;
case ActivitiConstants.ACTIVITI_PROCESSDEFINITIONID:
query.orderByProcessDefinitionId();
break;
case ActivitiConstants.ACTIVITI_DURATIONINMILLIS:
query.orderByHistoricTaskInstanceDuration();
break;
case ActivitiConstants.ACTIVITI_STARTTIME:
query.orderByHistoricTaskInstanceStartTime();
break;
case ActivitiConstants.ACTIVITI_ENDTIME:
query.orderByHistoricTaskInstanceEndTime();
break;
case ActivitiConstants.ACTIVITI_OWNER:
query.orderByTaskOwner();
break;
case ActivitiConstants.ACTIVITI_DUEDATE:
query.orderByTaskDueDate();
break;
case ActivitiConstants.ACTIVITI_DELETEREASON:
query.orderByDeleteReason();
break;
case ActivitiConstants.ACTIVITI_TASKDEFINITIONKEY:
query.orderByTaskDefinitionKey();
break;
default:
throw new NotSupportedException(
"Sort key: " + key.getField().toString().substring(1) + " is not valid");
}
query = key.isAscendingOrder() ? query.asc() : query.desc();
}
}
}
}