org.bonitasoft.engine.api.impl.ProcessAPIImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.bonitasoft.engine.api.impl.ProcessAPIImpl.java

Source

/**
 * Copyright (C) 2015 BonitaSoft S.A.
 * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble
 * This library is free software; you can redistribute it and/or modify it under the terms
 * of the GNU Lesser General Public License as published by the Free Software Foundation
 * version 2.1 of the License.
 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Lesser General Public License for more details.
 * You should have received a copy of the GNU Lesser General Public License along with this
 * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301, USA.
 **/
package org.bonitasoft.engine.api.impl;

import static java.util.Collections.singletonMap;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.zip.ZipOutputStream;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.DirectoryFileFilter;
import org.bonitasoft.engine.actor.mapping.ActorMappingService;
import org.bonitasoft.engine.actor.mapping.SActorNotFoundException;
import org.bonitasoft.engine.actor.mapping.model.SActor;
import org.bonitasoft.engine.actor.mapping.model.SActorMember;
import org.bonitasoft.engine.actor.mapping.model.SActorUpdateBuilder;
import org.bonitasoft.engine.actor.mapping.model.SActorUpdateBuilderFactory;
import org.bonitasoft.engine.api.DocumentAPI;
import org.bonitasoft.engine.api.ProcessAPI;
import org.bonitasoft.engine.api.impl.form.FormMappingDeployer;
import org.bonitasoft.engine.api.impl.resolver.ProcessDependencyResolver;
import org.bonitasoft.engine.api.impl.transaction.CustomTransactions;
import org.bonitasoft.engine.api.impl.transaction.activity.GetArchivedActivityInstance;
import org.bonitasoft.engine.api.impl.transaction.activity.GetArchivedActivityInstances;
import org.bonitasoft.engine.api.impl.transaction.activity.GetContractOfUserTaskInstance;
import org.bonitasoft.engine.api.impl.transaction.activity.GetNumberOfActivityInstance;
import org.bonitasoft.engine.api.impl.transaction.actor.ExportActorMapping;
import org.bonitasoft.engine.api.impl.transaction.actor.GetActor;
import org.bonitasoft.engine.api.impl.transaction.actor.GetActorsByActorIds;
import org.bonitasoft.engine.api.impl.transaction.actor.GetNumberOfActorMembers;
import org.bonitasoft.engine.api.impl.transaction.actor.GetNumberOfActors;
import org.bonitasoft.engine.api.impl.transaction.actor.GetNumberOfGroupsOfActor;
import org.bonitasoft.engine.api.impl.transaction.actor.GetNumberOfMembershipsOfActor;
import org.bonitasoft.engine.api.impl.transaction.actor.GetNumberOfRolesOfActor;
import org.bonitasoft.engine.api.impl.transaction.actor.GetNumberOfUsersOfActor;
import org.bonitasoft.engine.api.impl.transaction.actor.ImportActorMapping;
import org.bonitasoft.engine.api.impl.transaction.actor.RemoveActorMember;
import org.bonitasoft.engine.api.impl.transaction.category.CreateCategory;
import org.bonitasoft.engine.api.impl.transaction.category.DeleteSCategory;
import org.bonitasoft.engine.api.impl.transaction.category.GetCategories;
import org.bonitasoft.engine.api.impl.transaction.category.GetCategory;
import org.bonitasoft.engine.api.impl.transaction.category.GetNumberOfCategories;
import org.bonitasoft.engine.api.impl.transaction.category.GetNumberOfCategoriesOfProcess;
import org.bonitasoft.engine.api.impl.transaction.category.RemoveCategoriesFromProcessDefinition;
import org.bonitasoft.engine.api.impl.transaction.category.RemoveProcessDefinitionsOfCategory;
import org.bonitasoft.engine.api.impl.transaction.category.UpdateCategory;
import org.bonitasoft.engine.api.impl.transaction.comment.AddComment;
import org.bonitasoft.engine.api.impl.transaction.connector.GetConnectorImplementation;
import org.bonitasoft.engine.api.impl.transaction.connector.GetConnectorImplementations;
import org.bonitasoft.engine.api.impl.transaction.connector.GetNumberOfConnectorImplementations;
import org.bonitasoft.engine.api.impl.transaction.event.GetEventInstances;
import org.bonitasoft.engine.api.impl.transaction.expression.EvaluateExpressionsDefinitionLevel;
import org.bonitasoft.engine.api.impl.transaction.expression.EvaluateExpressionsInstanceLevel;
import org.bonitasoft.engine.api.impl.transaction.expression.EvaluateExpressionsInstanceLevelAndArchived;
import org.bonitasoft.engine.api.impl.transaction.flownode.ExecuteFlowNode;
import org.bonitasoft.engine.api.impl.transaction.flownode.GetFlowNodeInstance;
import org.bonitasoft.engine.api.impl.transaction.flownode.SetExpectedEndDate;
import org.bonitasoft.engine.api.impl.transaction.identity.GetSUser;
import org.bonitasoft.engine.api.impl.transaction.process.AddProcessDefinitionToCategory;
import org.bonitasoft.engine.api.impl.transaction.process.DeleteArchivedProcessInstances;
import org.bonitasoft.engine.api.impl.transaction.process.EnableProcess;
import org.bonitasoft.engine.api.impl.transaction.process.GetArchivedProcessInstanceList;
import org.bonitasoft.engine.api.impl.transaction.process.GetLastArchivedProcessInstance;
import org.bonitasoft.engine.api.impl.transaction.process.GetLatestProcessDefinitionId;
import org.bonitasoft.engine.api.impl.transaction.process.GetNumberOfProcessDeploymentInfos;
import org.bonitasoft.engine.api.impl.transaction.process.GetNumberOfProcessDeploymentInfosUnrelatedToCategory;
import org.bonitasoft.engine.api.impl.transaction.process.GetNumberOfProcessInstance;
import org.bonitasoft.engine.api.impl.transaction.process.GetProcessDefinition;
import org.bonitasoft.engine.api.impl.transaction.process.GetProcessDefinitionDeployInfo;
import org.bonitasoft.engine.api.impl.transaction.process.GetProcessDefinitionDeployInfos;
import org.bonitasoft.engine.api.impl.transaction.process.GetProcessDefinitionDeployInfosWithActorOnlyForGroup;
import org.bonitasoft.engine.api.impl.transaction.process.GetProcessDefinitionDeployInfosWithActorOnlyForGroups;
import org.bonitasoft.engine.api.impl.transaction.process.GetProcessDefinitionDeployInfosWithActorOnlyForRole;
import org.bonitasoft.engine.api.impl.transaction.process.GetProcessDefinitionDeployInfosWithActorOnlyForRoles;
import org.bonitasoft.engine.api.impl.transaction.process.GetProcessDefinitionDeployInfosWithActorOnlyForUser;
import org.bonitasoft.engine.api.impl.transaction.process.GetProcessDefinitionDeployInfosWithActorOnlyForUsers;
import org.bonitasoft.engine.api.impl.transaction.process.GetProcessDefinitionIDByNameAndVersion;
import org.bonitasoft.engine.api.impl.transaction.process.SetProcessInstanceState;
import org.bonitasoft.engine.api.impl.transaction.process.UpdateProcessDeploymentInfo;
import org.bonitasoft.engine.api.impl.transaction.task.AssignOrUnassignUserTask;
import org.bonitasoft.engine.api.impl.transaction.task.GetAssignedTasks;
import org.bonitasoft.engine.api.impl.transaction.task.GetHumanTaskInstance;
import org.bonitasoft.engine.api.impl.transaction.task.GetNumberOfAssignedUserTaskInstances;
import org.bonitasoft.engine.api.impl.transaction.task.GetNumberOfOpenTasksForUsers;
import org.bonitasoft.engine.api.impl.transaction.task.SetTaskPriority;
import org.bonitasoft.engine.archive.ArchiveService;
import org.bonitasoft.engine.bpm.actor.ActorCriterion;
import org.bonitasoft.engine.bpm.actor.ActorInstance;
import org.bonitasoft.engine.bpm.actor.ActorMappingExportException;
import org.bonitasoft.engine.bpm.actor.ActorMappingImportException;
import org.bonitasoft.engine.bpm.actor.ActorMember;
import org.bonitasoft.engine.bpm.actor.ActorNotFoundException;
import org.bonitasoft.engine.bpm.actor.ActorUpdater;
import org.bonitasoft.engine.bpm.actor.ActorUpdater.ActorField;
import org.bonitasoft.engine.bpm.bar.BusinessArchive;
import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;
import org.bonitasoft.engine.bpm.bar.BusinessArchiveFactory;
import org.bonitasoft.engine.bpm.bar.InvalidBusinessArchiveFormatException;
import org.bonitasoft.engine.bpm.bar.ProcessDefinitionBARContribution;
import org.bonitasoft.engine.bpm.category.Category;
import org.bonitasoft.engine.bpm.category.CategoryCriterion;
import org.bonitasoft.engine.bpm.category.CategoryNotFoundException;
import org.bonitasoft.engine.bpm.category.CategoryUpdater;
import org.bonitasoft.engine.bpm.category.CategoryUpdater.CategoryField;
import org.bonitasoft.engine.bpm.comment.ArchivedComment;
import org.bonitasoft.engine.bpm.comment.Comment;
import org.bonitasoft.engine.bpm.connector.ArchivedConnectorInstance;
import org.bonitasoft.engine.bpm.connector.ConnectorCriterion;
import org.bonitasoft.engine.bpm.connector.ConnectorExecutionException;
import org.bonitasoft.engine.bpm.connector.ConnectorImplementationDescriptor;
import org.bonitasoft.engine.bpm.connector.ConnectorInstance;
import org.bonitasoft.engine.bpm.connector.ConnectorNotFoundException;
import org.bonitasoft.engine.bpm.contract.ContractDefinition;
import org.bonitasoft.engine.bpm.contract.ContractViolationException;
import org.bonitasoft.engine.bpm.contract.validation.ContractValidator;
import org.bonitasoft.engine.bpm.contract.validation.ContractValidatorFactory;
import org.bonitasoft.engine.bpm.data.ArchivedDataInstance;
import org.bonitasoft.engine.bpm.data.ArchivedDataNotFoundException;
import org.bonitasoft.engine.bpm.data.DataDefinition;
import org.bonitasoft.engine.bpm.data.DataInstance;
import org.bonitasoft.engine.bpm.data.DataNotFoundException;
import org.bonitasoft.engine.bpm.document.ArchivedDocument;
import org.bonitasoft.engine.bpm.document.ArchivedDocumentNotFoundException;
import org.bonitasoft.engine.bpm.document.Document;
import org.bonitasoft.engine.bpm.document.DocumentAttachmentException;
import org.bonitasoft.engine.bpm.document.DocumentCriterion;
import org.bonitasoft.engine.bpm.document.DocumentException;
import org.bonitasoft.engine.bpm.document.DocumentNotFoundException;
import org.bonitasoft.engine.bpm.document.DocumentValue;
import org.bonitasoft.engine.bpm.flownode.ActivityDefinitionNotFoundException;
import org.bonitasoft.engine.bpm.flownode.ActivityExecutionException;
import org.bonitasoft.engine.bpm.flownode.ActivityInstance;
import org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion;
import org.bonitasoft.engine.bpm.flownode.ActivityInstanceNotFoundException;
import org.bonitasoft.engine.bpm.flownode.ActivityStates;
import org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance;
import org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstance;
import org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstanceNotFoundException;
import org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstance;
import org.bonitasoft.engine.bpm.flownode.EventCriterion;
import org.bonitasoft.engine.bpm.flownode.EventInstance;
import org.bonitasoft.engine.bpm.flownode.FlowNodeExecutionException;
import org.bonitasoft.engine.bpm.flownode.FlowNodeInstance;
import org.bonitasoft.engine.bpm.flownode.FlowNodeInstanceNotFoundException;
import org.bonitasoft.engine.bpm.flownode.FlowNodeType;
import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;
import org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor;
import org.bonitasoft.engine.bpm.flownode.SendEventException;
import org.bonitasoft.engine.bpm.flownode.TaskPriority;
import org.bonitasoft.engine.bpm.flownode.TimerEventTriggerInstance;
import org.bonitasoft.engine.bpm.flownode.TimerEventTriggerInstanceNotFoundException;
import org.bonitasoft.engine.bpm.flownode.UserTaskNotFoundException;
import org.bonitasoft.engine.bpm.parameter.ParameterCriterion;
import org.bonitasoft.engine.bpm.parameter.ParameterInstance;
import org.bonitasoft.engine.bpm.process.ArchivedProcessInstance;
import org.bonitasoft.engine.bpm.process.ArchivedProcessInstanceNotFoundException;
import org.bonitasoft.engine.bpm.process.ArchivedProcessInstancesSearchDescriptor;
import org.bonitasoft.engine.bpm.process.DesignProcessDefinition;
import org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException;
import org.bonitasoft.engine.bpm.process.Problem;
import org.bonitasoft.engine.bpm.process.ProcessActivationException;
import org.bonitasoft.engine.bpm.process.ProcessDefinition;
import org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException;
import org.bonitasoft.engine.bpm.process.ProcessDeployException;
import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;
import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion;
import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoUpdater;
import org.bonitasoft.engine.bpm.process.ProcessEnablementException;
import org.bonitasoft.engine.bpm.process.ProcessExecutionException;
import org.bonitasoft.engine.bpm.process.ProcessExportException;
import org.bonitasoft.engine.bpm.process.ProcessInstance;
import org.bonitasoft.engine.bpm.process.ProcessInstanceCriterion;
import org.bonitasoft.engine.bpm.process.ProcessInstanceNotFoundException;
import org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor;
import org.bonitasoft.engine.bpm.process.ProcessInstanceState;
import org.bonitasoft.engine.bpm.supervisor.ProcessSupervisor;
import org.bonitasoft.engine.builder.BuilderFactory;
import org.bonitasoft.engine.classloader.ClassLoaderService;
import org.bonitasoft.engine.classloader.SClassLoaderException;
import org.bonitasoft.engine.commons.StringUtils;
import org.bonitasoft.engine.commons.exceptions.SBonitaException;
import org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException;
import org.bonitasoft.engine.commons.transaction.TransactionContent;
import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;
import org.bonitasoft.engine.commons.transaction.TransactionExecutor;
import org.bonitasoft.engine.core.category.CategoryService;
import org.bonitasoft.engine.core.category.exception.SCategoryAlreadyExistsException;
import org.bonitasoft.engine.core.category.exception.SCategoryInProcessAlreadyExistsException;
import org.bonitasoft.engine.core.category.exception.SCategoryNotFoundException;
import org.bonitasoft.engine.core.category.model.SCategory;
import org.bonitasoft.engine.core.category.model.SProcessCategoryMapping;
import org.bonitasoft.engine.core.category.model.builder.SCategoryBuilderFactory;
import org.bonitasoft.engine.core.category.model.builder.SCategoryUpdateBuilder;
import org.bonitasoft.engine.core.category.model.builder.SCategoryUpdateBuilderFactory;
import org.bonitasoft.engine.core.category.model.builder.SProcessCategoryMappingBuilderFactory;
import org.bonitasoft.engine.core.connector.ConnectorInstanceService;
import org.bonitasoft.engine.core.connector.ConnectorResult;
import org.bonitasoft.engine.core.connector.ConnectorService;
import org.bonitasoft.engine.core.connector.exception.SConnectorException;
import org.bonitasoft.engine.core.connector.parser.SConnectorImplementationDescriptor;
import org.bonitasoft.engine.core.contract.data.ContractDataService;
import org.bonitasoft.engine.core.contract.data.SContractDataNotFoundException;
import org.bonitasoft.engine.core.data.instance.TransientDataService;
import org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService;
import org.bonitasoft.engine.core.expression.control.model.SExpressionContext;
import org.bonitasoft.engine.core.filter.FilterResult;
import org.bonitasoft.engine.core.filter.UserFilterService;
import org.bonitasoft.engine.core.filter.exception.SUserFilterExecutionException;
import org.bonitasoft.engine.core.operation.OperationService;
import org.bonitasoft.engine.core.operation.model.SOperation;
import org.bonitasoft.engine.core.process.comment.api.SCommentNotFoundException;
import org.bonitasoft.engine.core.process.comment.api.SCommentService;
import org.bonitasoft.engine.core.process.comment.model.SComment;
import org.bonitasoft.engine.core.process.comment.model.archive.SAComment;
import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;
import org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException;
import org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionReadException;
import org.bonitasoft.engine.core.process.definition.exception.SProcessDeletionException;
import org.bonitasoft.engine.core.process.definition.model.SActivityDefinition;
import org.bonitasoft.engine.core.process.definition.model.SActorDefinition;
import org.bonitasoft.engine.core.process.definition.model.SContractDefinition;
import org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;
import org.bonitasoft.engine.core.process.definition.model.SFlowNodeDefinition;
import org.bonitasoft.engine.core.process.definition.model.SHumanTaskDefinition;
import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;
import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;
import org.bonitasoft.engine.core.process.definition.model.SSubProcessDefinition;
import org.bonitasoft.engine.core.process.definition.model.SUserFilterDefinition;
import org.bonitasoft.engine.core.process.definition.model.SUserTaskDefinition;
import org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionBuilderFactory;
import org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionDeployInfoBuilderFactory;
import org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SThrowMessageEventTriggerDefinitionBuilder;
import org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SThrowMessageEventTriggerDefinitionBuilderFactory;
import org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SThrowSignalEventTriggerDefinitionBuilderFactory;
import org.bonitasoft.engine.core.process.definition.model.event.SBoundaryEventDefinition;
import org.bonitasoft.engine.core.process.definition.model.event.SCatchEventDefinition;
import org.bonitasoft.engine.core.process.definition.model.event.SIntermediateCatchEventDefinition;
import org.bonitasoft.engine.core.process.definition.model.event.SStartEventDefinition;
import org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowMessageEventTriggerDefinition;
import org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowSignalEventTriggerDefinition;
import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;
import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;
import org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;
import org.bonitasoft.engine.core.process.instance.api.exceptions.SAProcessInstanceNotFoundException;
import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityInstanceNotFoundException;
import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityModificationException;
import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityReadException;
import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException;
import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException;
import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceHierarchicalDeletionException;
import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceNotFoundException;
import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceReadException;
import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;
import org.bonitasoft.engine.core.process.instance.model.SActivityInstance;
import org.bonitasoft.engine.core.process.instance.model.SFlowElementsContainerType;
import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;
import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstanceStateCounter;
import org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance;
import org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping;
import org.bonitasoft.engine.core.process.instance.model.SProcessInstance;
import org.bonitasoft.engine.core.process.instance.model.SStateCategory;
import org.bonitasoft.engine.core.process.instance.model.STaskPriority;
import org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance;
import org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance;
import org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance;
import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAProcessInstanceBuilderFactory;
import org.bonitasoft.engine.core.process.instance.model.builder.SAutomaticTaskInstanceBuilderFactory;
import org.bonitasoft.engine.core.process.instance.model.builder.SPendingActivityMappingBuilderFactory;
import org.bonitasoft.engine.core.process.instance.model.builder.SProcessInstanceBuilderFactory;
import org.bonitasoft.engine.core.process.instance.model.builder.event.trigger.STimerEventTriggerInstanceBuilder;
import org.bonitasoft.engine.core.process.instance.model.event.SCatchEventInstance;
import org.bonitasoft.engine.core.process.instance.model.event.SEventInstance;
import org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance;
import org.bonitasoft.engine.data.definition.model.SDataDefinition;
import org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilder;
import org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilderFactory;
import org.bonitasoft.engine.data.instance.api.DataInstanceContainer;
import org.bonitasoft.engine.data.instance.api.DataInstanceService;
import org.bonitasoft.engine.data.instance.api.ParentContainerResolver;
import org.bonitasoft.engine.data.instance.exception.SDataInstanceException;
import org.bonitasoft.engine.data.instance.model.SDataInstance;
import org.bonitasoft.engine.data.instance.model.archive.SADataInstance;
import org.bonitasoft.engine.dependency.DependencyService;
import org.bonitasoft.engine.dependency.model.ScopeType;
import org.bonitasoft.engine.exception.AlreadyExistsException;
import org.bonitasoft.engine.exception.BonitaException;
import org.bonitasoft.engine.exception.BonitaHomeNotSetException;
import org.bonitasoft.engine.exception.BonitaRuntimeException;
import org.bonitasoft.engine.exception.ContractDataNotFoundException;
import org.bonitasoft.engine.exception.CreationException;
import org.bonitasoft.engine.exception.DeletionException;
import org.bonitasoft.engine.exception.ExecutionException;
import org.bonitasoft.engine.exception.NotFoundException;
import org.bonitasoft.engine.exception.NotSerializableException;
import org.bonitasoft.engine.exception.ProcessInstanceHierarchicalDeletionException;
import org.bonitasoft.engine.exception.RetrieveException;
import org.bonitasoft.engine.exception.SearchException;
import org.bonitasoft.engine.exception.UpdateException;
import org.bonitasoft.engine.execution.FlowNodeExecutor;
import org.bonitasoft.engine.execution.ProcessExecutor;
import org.bonitasoft.engine.execution.SUnreleasableTaskException;
import org.bonitasoft.engine.execution.StateBehaviors;
import org.bonitasoft.engine.execution.TransactionalProcessInstanceInterruptor;
import org.bonitasoft.engine.execution.WaitingEventsInterrupter;
import org.bonitasoft.engine.execution.event.EventsHandler;
import org.bonitasoft.engine.execution.job.JobNameBuilder;
import org.bonitasoft.engine.execution.state.FlowNodeStateManager;
import org.bonitasoft.engine.expression.ContainerState;
import org.bonitasoft.engine.expression.Expression;
import org.bonitasoft.engine.expression.ExpressionBuilder;
import org.bonitasoft.engine.expression.ExpressionEvaluationException;
import org.bonitasoft.engine.expression.ExpressionType;
import org.bonitasoft.engine.expression.InvalidExpressionException;
import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;
import org.bonitasoft.engine.expression.exception.SInvalidExpressionException;
import org.bonitasoft.engine.expression.model.SExpression;
import org.bonitasoft.engine.home.BonitaHomeServer;
import org.bonitasoft.engine.identity.IdentityService;
import org.bonitasoft.engine.identity.SUserNotFoundException;
import org.bonitasoft.engine.identity.User;
import org.bonitasoft.engine.identity.UserNotFoundException;
import org.bonitasoft.engine.identity.model.SUser;
import org.bonitasoft.engine.io.IOUtil;
import org.bonitasoft.engine.job.FailedJob;
import org.bonitasoft.engine.lock.BonitaLock;
import org.bonitasoft.engine.lock.LockService;
import org.bonitasoft.engine.lock.SLockException;
import org.bonitasoft.engine.log.technical.TechnicalLogSeverity;
import org.bonitasoft.engine.log.technical.TechnicalLoggerService;
import org.bonitasoft.engine.operation.LeftOperand;
import org.bonitasoft.engine.operation.Operation;
import org.bonitasoft.engine.operation.OperationBuilder;
import org.bonitasoft.engine.persistence.FilterOption;
import org.bonitasoft.engine.persistence.OrderAndField;
import org.bonitasoft.engine.persistence.OrderByOption;
import org.bonitasoft.engine.persistence.OrderByType;
import org.bonitasoft.engine.persistence.QueryOptions;
import org.bonitasoft.engine.persistence.ReadPersistenceService;
import org.bonitasoft.engine.persistence.SBonitaReadException;
import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;
import org.bonitasoft.engine.scheduler.JobService;
import org.bonitasoft.engine.scheduler.SchedulerService;
import org.bonitasoft.engine.scheduler.builder.SJobParameterBuilderFactory;
import org.bonitasoft.engine.scheduler.exception.SSchedulerException;
import org.bonitasoft.engine.scheduler.model.SFailedJob;
import org.bonitasoft.engine.scheduler.model.SJobParameter;
import org.bonitasoft.engine.search.Order;
import org.bonitasoft.engine.search.SearchOptions;
import org.bonitasoft.engine.search.SearchOptionsBuilder;
import org.bonitasoft.engine.search.SearchResult;
import org.bonitasoft.engine.search.activity.SearchActivityInstances;
import org.bonitasoft.engine.search.activity.SearchArchivedActivityInstances;
import org.bonitasoft.engine.search.comment.SearchArchivedComments;
import org.bonitasoft.engine.search.comment.SearchComments;
import org.bonitasoft.engine.search.comment.SearchCommentsInvolvingUser;
import org.bonitasoft.engine.search.comment.SearchCommentsManagedBy;
import org.bonitasoft.engine.search.connector.SearchArchivedConnectorInstance;
import org.bonitasoft.engine.search.connector.SearchConnectorInstances;
import org.bonitasoft.engine.search.descriptor.SearchEntitiesDescriptor;
import org.bonitasoft.engine.search.descriptor.SearchHumanTaskInstanceDescriptor;
import org.bonitasoft.engine.search.descriptor.SearchProcessDefinitionsDescriptor;
import org.bonitasoft.engine.search.descriptor.SearchProcessSupervisorDescriptor;
import org.bonitasoft.engine.search.descriptor.SearchUserDescriptor;
import org.bonitasoft.engine.search.events.trigger.SearchTimerEventTriggerInstances;
import org.bonitasoft.engine.search.flownode.SearchArchivedFlowNodeInstances;
import org.bonitasoft.engine.search.flownode.SearchFlowNodeInstances;
import org.bonitasoft.engine.search.identity.SearchUsersWhoCanExecutePendingHumanTaskDeploymentInfo;
import org.bonitasoft.engine.search.identity.SearchUsersWhoCanStartProcessDeploymentInfo;
import org.bonitasoft.engine.search.impl.SearchResultImpl;
import org.bonitasoft.engine.search.process.SearchArchivedProcessInstances;
import org.bonitasoft.engine.search.process.SearchArchivedProcessInstancesInvolvingUser;
import org.bonitasoft.engine.search.process.SearchArchivedProcessInstancesSupervisedBy;
import org.bonitasoft.engine.search.process.SearchArchivedProcessInstancesWithoutSubProcess;
import org.bonitasoft.engine.search.process.SearchFailedProcessInstances;
import org.bonitasoft.engine.search.process.SearchFailedProcessInstancesSupervisedBy;
import org.bonitasoft.engine.search.process.SearchOpenProcessInstancesInvolvingUser;
import org.bonitasoft.engine.search.process.SearchOpenProcessInstancesInvolvingUsersManagedBy;
import org.bonitasoft.engine.search.process.SearchOpenProcessInstancesSupervisedBy;
import org.bonitasoft.engine.search.process.SearchProcessDeploymentInfos;
import org.bonitasoft.engine.search.process.SearchProcessDeploymentInfosCanBeStartedBy;
import org.bonitasoft.engine.search.process.SearchProcessDeploymentInfosCanBeStartedByUsersManagedBy;
import org.bonitasoft.engine.search.process.SearchProcessDeploymentInfosStartedBy;
import org.bonitasoft.engine.search.process.SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasks;
import org.bonitasoft.engine.search.process.SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor;
import org.bonitasoft.engine.search.process.SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy;
import org.bonitasoft.engine.search.process.SearchProcessInstances;
import org.bonitasoft.engine.search.process.SearchUncategorizedProcessDeploymentInfos;
import org.bonitasoft.engine.search.process.SearchUncategorizedProcessDeploymentInfosCanBeStartedBy;
import org.bonitasoft.engine.search.process.SearchUncategorizedProcessDeploymentInfosSupervisedBy;
import org.bonitasoft.engine.search.supervisor.SearchArchivedHumanTasksSupervisedBy;
import org.bonitasoft.engine.search.supervisor.SearchAssignedTasksSupervisedBy;
import org.bonitasoft.engine.search.supervisor.SearchProcessDeploymentInfosSupervised;
import org.bonitasoft.engine.search.supervisor.SearchSupervisors;
import org.bonitasoft.engine.search.task.SearchArchivedTasks;
import org.bonitasoft.engine.search.task.SearchArchivedTasksManagedBy;
import org.bonitasoft.engine.search.task.SearchAssignedAndPendingHumanTasks;
import org.bonitasoft.engine.search.task.SearchAssignedAndPendingHumanTasksFor;
import org.bonitasoft.engine.search.task.SearchAssignedTaskManagedBy;
import org.bonitasoft.engine.search.task.SearchHumanTaskInstances;
import org.bonitasoft.engine.search.task.SearchPendingTasksForUser;
import org.bonitasoft.engine.search.task.SearchPendingTasksManagedBy;
import org.bonitasoft.engine.search.task.SearchPendingTasksSupervisedBy;
import org.bonitasoft.engine.service.ModelConvertor;
import org.bonitasoft.engine.service.TenantServiceAccessor;
import org.bonitasoft.engine.supervisor.mapping.SSupervisorDeletionException;
import org.bonitasoft.engine.supervisor.mapping.SSupervisorNotFoundException;
import org.bonitasoft.engine.supervisor.mapping.SupervisorMappingService;
import org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor;
import org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisorBuilder;
import org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisorBuilderFactory;
import org.bonitasoft.engine.transaction.UserTransactionService;
import org.bonitasoft.engine.xml.Parser;
import org.bonitasoft.engine.xml.XMLWriter;

/**
 * @author Baptiste Mesta
 * @author Matthieu Chaffotte
 * @author Yanyan Liu
 * @author Elias Ricken de Medeiros
 * @author Zhao Na
 * @author Zhang Bole
 * @author Emmanuel Duchastenier
 * @author Celine Souchet
 * @author Arthur Freycon
 */
public class ProcessAPIImpl implements ProcessAPI {

    private static final int BATCH_SIZE = 100;

    private static final String CONTAINER_TYPE_PROCESS_INSTANCE = "PROCESS_INSTANCE";

    private static final String CONTAINER_TYPE_ACTIVITY_INSTANCE = "ACTIVITY_INSTANCE";

    private final ProcessManagementAPIImplDelegate processManagementAPIImplDelegate;

    private final DocumentAPI documentAPI;

    public ProcessAPIImpl() {
        this(new ProcessManagementAPIImplDelegate(), new DocumentAPIImpl());
    }

    public ProcessAPIImpl(final ProcessManagementAPIImplDelegate processManagementAPIDelegate,
            final DocumentAPI documentAPI) {
        processManagementAPIImplDelegate = processManagementAPIDelegate;
        this.documentAPI = documentAPI;
    }

    protected ProcessManagementAPIImplDelegate instantiateProcessManagementAPIDelegate() {
        return new ProcessManagementAPIImplDelegate();
    }

    @Override
    public SearchResult<HumanTaskInstance> searchHumanTaskInstances(final SearchOptions searchOptions)
            throws SearchException {
        try {
            return searchHumanTasksTransaction(searchOptions);
        } catch (final SBonitaException e) {
            throw new SearchException(e);
        }
    }

    private SearchResult<HumanTaskInstance> searchHumanTasksTransaction(final SearchOptions searchOptions)
            throws SBonitaException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        final FlowNodeStateManager flowNodeStateManager = tenantAccessor.getFlowNodeStateManager();
        final SearchHumanTaskInstances searchHumanTasksTransaction = new SearchHumanTaskInstances(
                activityInstanceService, flowNodeStateManager,
                searchEntitiesDescriptor.getSearchHumanTaskInstanceDescriptor(), searchOptions);
        searchHumanTasksTransaction.execute();
        return searchHumanTasksTransaction.getResult();
    }

    @Override
    public void deleteProcessDefinition(final long processDefinitionId) throws DeletionException {
        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 1);
        builder.filter(ProcessInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDefinitionId);
        final SearchOptions searchOptions = builder.done();

        try {
            final boolean hasOpenProcessInstances = searchProcessInstances(getTenantAccessor(), searchOptions)
                    .getCount() > 0;
            checkIfItIsPossibleToDeleteProcessInstance(processDefinitionId, hasOpenProcessInstances);
            final boolean hasArchivedProcessInstances = searchArchivedProcessInstances(searchOptions)
                    .getCount() > 0;
            checkIfItIsPossibleToDeleteProcessInstance(processDefinitionId, hasArchivedProcessInstances);

            processManagementAPIImplDelegate.deleteProcessDefinition(processDefinitionId);
        } catch (final Exception e) {
            throw new DeletionException(e);
        }
    }

    private void checkIfItIsPossibleToDeleteProcessInstance(final long processDefinitionId,
            final boolean canThrowException) throws DeletionException {
        if (canThrowException) {
            throw new DeletionException("Some active process instances are still found, process #"
                    + processDefinitionId + " can't be deleted.");
        }
    }

    @Override
    public void deleteProcessDefinitions(final List<Long> processDefinitionIds) throws DeletionException {
        for (final Long processDefinitionId : processDefinitionIds) {
            deleteProcessDefinition(processDefinitionId);
        }
    }

    @Override
    @CustomTransactions
    @Deprecated
    public void deleteProcess(final long processDefinitionId) throws DeletionException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        // multiple tx here because we must lock instances when deleting them
        // but if the second tx crash we can relaunch delete process without issues
        final UserTransactionService userTransactionService = tenantAccessor.getUserTransactionService();
        try {
            userTransactionService.executeInTransaction(new Callable<Void>() {

                @Override
                public Void call() throws Exception {
                    deleteProcessInstancesFromProcessDefinition(processDefinitionId, tenantAccessor);
                    try {
                        processManagementAPIImplDelegate.deleteProcessDefinition(processDefinitionId);
                    } catch (final BonitaHomeNotSetException e) {
                        throw new SProcessDeletionException(e, processDefinitionId);
                    } catch (final IOException e) {
                        throw new SProcessDeletionException(e, processDefinitionId);
                    }
                    return null;
                }
            });

        } catch (final SProcessInstanceHierarchicalDeletionException e) {
            throw new ProcessInstanceHierarchicalDeletionException(e.getMessage(), e.getProcessInstanceId());
        } catch (final Exception e) {
            throw new DeletionException(e);
        }
    }

    private void deleteProcessInstancesFromProcessDefinition(final long processDefinitionId,
            final TenantServiceAccessor tenantAccessor)
            throws SBonitaException, SProcessInstanceHierarchicalDeletionException {
        List<ProcessInstance> processInstances;
        final int maxResults = 1000;
        do {
            processInstances = searchProcessInstancesFromProcessDefinition(tenantAccessor, processDefinitionId, 0,
                    maxResults);
            if (processInstances.size() > 0) {
                deleteProcessInstancesInsideLocks(tenantAccessor, true, processInstances,
                        tenantAccessor.getTenantId());
            }
        } while (!processInstances.isEmpty());
    }

    private void deleteProcessInstancesInsideLocks(final TenantServiceAccessor tenantAccessor,
            final boolean ignoreProcessInstanceNotFound, final List<ProcessInstance> processInstances,
            final long tenantId) throws SBonitaException, SProcessInstanceHierarchicalDeletionException {
        final List<Long> processInstanceIds = new ArrayList<Long>(processInstances.size());
        for (final ProcessInstance processInstance : processInstances) {
            processInstanceIds.add(processInstance.getId());
        }
        deleteProcessInstancesInsideLocksFromIds(tenantAccessor, ignoreProcessInstanceNotFound, processInstanceIds,
                tenantId);
    }

    private void deleteProcessInstancesInsideLocksFromIds(final TenantServiceAccessor tenantAccessor,
            final boolean ignoreProcessInstanceNotFound, final List<Long> processInstanceIds, final long tenantId)
            throws SBonitaException, SProcessInstanceHierarchicalDeletionException {
        final LockService lockService = tenantAccessor.getLockService();
        final String objectType = SFlowElementsContainerType.PROCESS.name();
        final List<Long> lockedProcesses = new ArrayList<Long>();
        List<BonitaLock> locks = null;
        try {
            locks = createLocks(lockService, objectType, lockedProcesses, processInstanceIds, tenantId);
            deleteProcessInstancesInTransaction(tenantAccessor, ignoreProcessInstanceNotFound, processInstanceIds);
        } finally {
            releaseLocks(tenantAccessor, lockService, locks, tenantId);
        }
    }

    private void releaseLocks(final TenantServiceAccessor tenantAccessor, final LockService lockService,
            final List<BonitaLock> locks, final long tenantId) {
        if (locks == null) {
            return;
        }
        for (final BonitaLock lock : locks) {
            try {
                lockService.unlock(lock, tenantId);
            } catch (final SLockException e) {
                log(tenantAccessor, e);
            }
        }
    }

    private ArrayList<BonitaLock> createLocks(final LockService lockService, final String objectType,
            final List<Long> lockedProcesses, final List<Long> processInstanceIds, final long tenantId)
            throws SLockException {
        final ArrayList<BonitaLock> locks = new ArrayList<BonitaLock>(processInstanceIds.size());
        for (final Long processInstanceId : processInstanceIds) {
            final BonitaLock lock = lockService.lock(processInstanceId, objectType, tenantId);
            locks.add(lock);
            lockedProcesses.add(processInstanceId);
        }
        return locks;
    }

    private void deleteProcessInstancesInTransaction(final TenantServiceAccessor tenantAccessor,
            final boolean ignoreProcessInstanceNotFound, final List<Long> processInstanceIds)
            throws SBonitaException, SProcessInstanceHierarchicalDeletionException {
        final ProcessInstanceService processInstanceService = tenantAccessor.getProcessInstanceService();
        final TechnicalLoggerService logger = tenantAccessor.getTechnicalLoggerService();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        deleteProcessInstances(processInstanceService, logger, ignoreProcessInstanceNotFound,
                activityInstanceService, processInstanceIds);
    }

    private void deleteProcessInstances(final ProcessInstanceService processInstanceService,
            final TechnicalLoggerService logger, final boolean ignoreProcessInstanceNotFound,
            final ActivityInstanceService activityInstanceService, final List<Long> processInstanceIds)
            throws SBonitaException, SProcessInstanceHierarchicalDeletionException {
        for (final Long processInstanceId : processInstanceIds) {
            try {
                deleteProcessInstance(processInstanceService, processInstanceId, activityInstanceService);
            } catch (final SProcessInstanceNotFoundException e) {
                if (ignoreProcessInstanceNotFound) {
                    if (logger.isLoggable(this.getClass(), TechnicalLogSeverity.DEBUG)) {
                        logger.log(this.getClass(), TechnicalLogSeverity.DEBUG,
                                e.getMessage() + ". It has probably completed.");
                    }
                } else {
                    throw e;
                }
            }
        }
    }

    private void deleteProcessInstance(final ProcessInstanceService processInstanceService,
            final Long processInstanceId, final ActivityInstanceService activityInstanceService)
            throws SBonitaException, SProcessInstanceHierarchicalDeletionException {
        final SProcessInstance sProcessInstance = processInstanceService.getProcessInstance(processInstanceId);
        final long callerId = sProcessInstance.getCallerId();
        if (callerId > 0) {
            try {
                final SFlowNodeInstance flowNodeInstance = activityInstanceService.getFlowNodeInstance(callerId);
                final long rootProcessInstanceId = flowNodeInstance.getRootProcessInstanceId();
                final SProcessInstanceHierarchicalDeletionException exception = new SProcessInstanceHierarchicalDeletionException(
                        "Unable to delete the process instance, because the parent is still active.",
                        rootProcessInstanceId);
                exception.setProcessInstanceIdOnContext(processInstanceId);
                exception.setRootProcessInstanceIdOnContext(rootProcessInstanceId);
                exception.setFlowNodeDefinitionIdOnContext(flowNodeInstance.getFlowNodeDefinitionId());
                exception.setFlowNodeInstanceIdOnContext(flowNodeInstance.getId());
                exception.setFlowNodeNameOnContext(flowNodeInstance.getName());
                exception.setProcessDefinitionIdOnContext(flowNodeInstance.getProcessDefinitionId());
                throw exception;
            } catch (final SFlowNodeNotFoundException e) {
                // ok the activity that called this process do not exists anymore
            }
        }
        deleteJobsOnProcessInstance(sProcessInstance);
        processInstanceService.deleteArchivedProcessInstanceElements(processInstanceId,
                sProcessInstance.getProcessDefinitionId());
        processInstanceService.deleteArchivedProcessInstancesOfProcessInstance(processInstanceId);
        processInstanceService.deleteProcessInstance(sProcessInstance);
    }

    private List<ProcessInstance> searchProcessInstancesFromProcessDefinition(
            final TenantServiceAccessor tenantAccessor, final long processDefinitionId, final int startIndex,
            final int maxResults) throws SBonitaException {
        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(startIndex, maxResults);
        searchOptionsBuilder.filter(ProcessInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDefinitionId);
        // Order by caller id ASC because we need to have parent process deleted before their sub processes
        searchOptionsBuilder.sort(ProcessInstanceSearchDescriptor.CALLER_ID, Order.ASC);
        return searchProcessInstances(tenantAccessor, searchOptionsBuilder.done()).getResult();
    }

    @Override
    @CustomTransactions
    @Deprecated
    public void deleteProcesses(final List<Long> processDefinitionIds) throws DeletionException {
        for (final Long processDefinitionId : processDefinitionIds) {
            deleteProcess(processDefinitionId);
        }
    }

    @Override
    public ProcessDefinition deployAndEnableProcess(final DesignProcessDefinition designProcessDefinition)
            throws ProcessDeployException, ProcessEnablementException, AlreadyExistsException,
            InvalidProcessDefinitionException {
        BusinessArchive businessArchive;
        try {
            businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()
                    .setProcessDefinition(designProcessDefinition).done();
        } catch (final InvalidBusinessArchiveFormatException ibafe) {
            throw new InvalidProcessDefinitionException(ibafe.getMessage());
        }
        return deployAndEnableProcess(businessArchive);
    }

    @Override
    public ProcessDefinition deployAndEnableProcess(final BusinessArchive businessArchive)
            throws ProcessDeployException, ProcessEnablementException, AlreadyExistsException {
        final ProcessDefinition processDefinition = deploy(businessArchive);
        try {
            enableProcess(processDefinition.getId());
        } catch (final ProcessDefinitionNotFoundException pdnfe) {
            throw new ProcessEnablementException(pdnfe.getMessage());
        }
        return processDefinition;
    }

    @Override
    public ProcessDefinition deploy(final DesignProcessDefinition designProcessDefinition)
            throws AlreadyExistsException, ProcessDeployException {
        try {
            final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()
                    .setProcessDefinition(designProcessDefinition).done();
            return deploy(businessArchive);
        } catch (final InvalidBusinessArchiveFormatException ibafe) {
            throw new ProcessDeployException(ibafe);
        }
    }

    @Override
    public ProcessDefinition deploy(final BusinessArchive businessArchive)
            throws ProcessDeployException, AlreadyExistsException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();
        final DependencyService dependencyService = tenantAccessor.getDependencyService();
        final DesignProcessDefinition designProcessDefinition = businessArchive.getProcessDefinition();

        // create the runtime process definition
        final SProcessDefinition sProcessDefinition = BuilderFactory.get(SProcessDefinitionBuilderFactory.class)
                .createNewInstance(designProcessDefinition).done();

        try {
            try {
                processDefinitionService.getProcessDefinitionId(designProcessDefinition.getName(),
                        designProcessDefinition.getVersion());
                throw new AlreadyExistsException("The process " + designProcessDefinition.getName() + " in version "
                        + designProcessDefinition.getVersion() + " already exists.");
            } catch (final SProcessDefinitionNotFoundException e) {
                // ok
            }
            processDefinitionService.store(sProcessDefinition, designProcessDefinition.getDisplayName(),
                    designProcessDefinition.getDisplayDescription());
            unzipBar(businessArchive, sProcessDefinition, tenantAccessor.getTenantId());// TODO first unzip in temp folder
            processManagementAPIImplDelegate.createProcessPageDeployer().deployProcessPages(businessArchive,
                    sProcessDefinition.getId(), SessionInfos.getUserIdFromSession());
            new FormMappingDeployer(getTenantAccessor().getFormMappingService()).deployFormMappings(businessArchive,
                    sProcessDefinition.getId());
            final boolean isResolved = tenantAccessor.getDependencyResolver().resolveDependencies(businessArchive,
                    tenantAccessor, sProcessDefinition);
            if (isResolved) {
                tenantAccessor.getDependencyResolver().resolveAndCreateDependencies(businessArchive,
                        processDefinitionService, dependencyService, sProcessDefinition);
            }
        } catch (final BonitaHomeNotSetException e) {
            throw new ProcessDeployException(e);
        } catch (final IOException e) {
            throw new ProcessDeployException(e);
        } catch (final SBonitaException e) {
            throw new ProcessDeployException(e);
        }

        final ProcessDefinition processDefinition = ModelConvertor.toProcessDefinition(sProcessDefinition);
        final TechnicalLoggerService logger = tenantAccessor.getTechnicalLoggerService();
        if (logger.isLoggable(this.getClass(), TechnicalLogSeverity.INFO)) {
            logger.log(this.getClass(), TechnicalLogSeverity.INFO,
                    "The user <" + SessionInfos.getUserNameFromSession() + "> has installed process <"
                            + sProcessDefinition.getName() + "> in version <" + sProcessDefinition.getVersion()
                            + "> with id <" + sProcessDefinition.getId() + ">");
        }
        return processDefinition;
    }

    @Override
    public void importActorMapping(final long pDefinitionId, final byte[] actorMappingXML)
            throws ActorMappingImportException {
        if (actorMappingXML != null) {
            final String actorMapping = new String(actorMappingXML, Charset.forName("UTF-8"));
            importActorMapping(pDefinitionId, actorMapping);
        }
    }

    @Override
    // TODO delete files after use/if an exception occurs
    public byte[] exportBarProcessContentUnderHome(final long processDefinitionId) throws ProcessExportException {
        String processesFolder;
        try {
            final long tenantId = getTenantAccessor().getTenantId();
            processesFolder = BonitaHomeServer.getInstance().getProcessesFolder(tenantId);
        } catch (final BonitaHomeNotSetException e) {
            throw new BonitaRuntimeException(e);
        }
        final File file = new File(processesFolder);
        if (!file.exists()) {
            file.mkdirs();
        }
        final File processFolder = new File(file, String.valueOf(processDefinitionId));

        // export actormapping
        try {

            final File actormappF = new File(processFolder.getPath(), "actorMapping.xml");
            if (!actormappF.exists()) {
                actormappF.createNewFile();
            }
            String xmlcontent = "";
            try {
                xmlcontent = exportActorMapping(processDefinitionId);
            } catch (final ActorMappingExportException e) {
                throw new ProcessExportException(e);
            }
            IOUtil.writeContentToFile(xmlcontent, actormappF);

            final ByteArrayOutputStream baos = new ByteArrayOutputStream();
            final ZipOutputStream zos = new ZipOutputStream(baos);
            try {
                IOUtil.zipDir(processFolder.getPath(), zos, processFolder.getPath());
                return baos.toByteArray();
            } finally {
                zos.close();
                baos.close();
            }
        } catch (final IOException e) {
            throw new ProcessExportException(e);
        }
    }

    protected void unzipBar(final BusinessArchive businessArchive, final SProcessDefinition sProcessDefinition,
            final long tenantId) throws BonitaHomeNotSetException, IOException {
        final File processFolder = getProcessFolder(sProcessDefinition.getId(), tenantId);
        BusinessArchiveFactory.writeBusinessArchiveToFolder(businessArchive, processFolder);
    }

    private File getProcessFolder(final long processDefinitionId, final long tenantId)
            throws BonitaHomeNotSetException {
        final String processesFolder = BonitaHomeServer.getInstance().getProcessesFolder(tenantId);
        final File file = new File(processesFolder);
        if (!file.exists()) {
            file.mkdirs();
        }
        return new File(file, String.valueOf(processDefinitionId));
    }

    @Override
    @CustomTransactions
    @Deprecated
    public void disableAndDelete(final long processDefinitionId)
            throws ProcessDefinitionNotFoundException, ProcessActivationException, DeletionException {
        final TransactionExecutor transactionExecutor = getTenantAccessor().getTransactionExecutor();
        try {
            transactionExecutor.execute(new TransactionContent() {

                @Override
                public void execute() throws SBonitaException {
                    processManagementAPIImplDelegate.disableProcess(processDefinitionId);
                }
            });
        } catch (final SProcessDefinitionNotFoundException e) {
            throw new ProcessDefinitionNotFoundException(e);
        } catch (final SBonitaException e) {
            throw new ProcessActivationException(e);
        }
        deleteProcess(processDefinitionId);
    }

    @Override
    public void disableAndDeleteProcessDefinition(final long processDefinitionId)
            throws ProcessDefinitionNotFoundException, ProcessActivationException, DeletionException {
        disableProcess(processDefinitionId);
        deleteProcessDefinition(processDefinitionId);
    }

    @Override
    public void disableProcess(final long processDefinitionId)
            throws ProcessDefinitionNotFoundException, ProcessActivationException {
        try {
            processManagementAPIImplDelegate.disableProcess(processDefinitionId);
        } catch (final SProcessDefinitionNotFoundException e) {
            throw new ProcessDefinitionNotFoundException(e);
        } catch (final SBonitaException e) {
            throw new ProcessActivationException(e);
        }
    }

    @Override
    public void enableProcess(final long processDefinitionId)
            throws ProcessDefinitionNotFoundException, ProcessEnablementException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();
        final EventsHandler eventsHandler = tenantAccessor.getEventsHandler();
        try {
            final EnableProcess enableProcess = new EnableProcess(processDefinitionService, processDefinitionId,
                    eventsHandler, tenantAccessor.getTechnicalLoggerService(),
                    SessionInfos.getUserNameFromSession());
            enableProcess.execute();
        } catch (final SProcessDefinitionNotFoundException e) {
            throw new ProcessDefinitionNotFoundException(e);
        } catch (final SBonitaException sbe) {
            throw new ProcessEnablementException(sbe);
        } catch (final Exception e) {
            throw new ProcessEnablementException(e);
        }
    }

    @CustomTransactions
    @Override
    public void executeFlowNode(final long flownodeInstanceId) throws FlowNodeExecutionException {
        executeFlowNode(0, flownodeInstanceId, true);
    }

    @CustomTransactions
    @Override
    public void executeFlowNode(final long userId, final long flownodeInstanceId)
            throws FlowNodeExecutionException {
        try {
            executeFlowNode(userId, flownodeInstanceId, true, new HashMap<String, Serializable>());
        } catch (final ContractViolationException e) {
            throw new FlowNodeExecutionException(e);
        } catch (final SBonitaException e) {
            throw new FlowNodeExecutionException(e);
        }
    }

    protected void executeFlowNode(final long userId, final long flownodeInstanceId,
            final boolean wrapInTransaction) throws FlowNodeExecutionException {
        try {
            executeFlowNode(userId, flownodeInstanceId, wrapInTransaction, new HashMap<String, Serializable>());
        } catch (final ContractViolationException e) {
            throw new FlowNodeExecutionException(e);
        } catch (final SBonitaException e) {
            throw new FlowNodeExecutionException(e);
        }
    }

    private void executeTransactionContent(final TenantServiceAccessor tenantAccessor,
            final TransactionContent transactionContent, final boolean wrapInTransaction) throws SBonitaException {
        if (wrapInTransaction) {
            final TransactionExecutor transactionExecutor = tenantAccessor.getTransactionExecutor();
            transactionExecutor.execute(transactionContent);
        } else {
            transactionContent.execute();
        }
    }

    @Override
    public List<ActivityInstance> getActivities(final long processInstanceId, final int startIndex,
            final int maxResults) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        final FlowNodeStateManager flowNodeStateManager = tenantAccessor.getFlowNodeStateManager();
        try {
            return ModelConvertor.toActivityInstances(
                    activityInstanceService.getActivityInstances(processInstanceId, startIndex, maxResults),
                    flowNodeStateManager);
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
    }

    @Override
    public long getNumberOfProcessDeploymentInfos() {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();
        final TransactionContentWithResult<Long> transactionContentWithResult = new GetNumberOfProcessDeploymentInfos(
                processDefinitionService);
        try {
            transactionContentWithResult.execute();
            return transactionContentWithResult.getResult();
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
    }

    @Override
    public ProcessDefinition getProcessDefinition(final long processDefinitionId)
            throws ProcessDefinitionNotFoundException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();
        try {
            final SProcessDefinition sProcessDefinition = processDefinitionService
                    .getProcessDefinition(processDefinitionId);
            return ModelConvertor.toProcessDefinition(sProcessDefinition);
        } catch (final SProcessDefinitionNotFoundException e) {
            throw new ProcessDefinitionNotFoundException(e);
        } catch (final SProcessDefinitionReadException e) {
            throw new RetrieveException(e);
        }
    }

    @Override
    public DesignProcessDefinition getDesignProcessDefinition(final long processDefinitionId)
            throws ProcessDefinitionNotFoundException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        try {
            final File processFolder = getProcessFolder(processDefinitionId, tenantAccessor.getTenantId());
            final File processDesignFile = new File(processFolder,
                    ProcessDefinitionBARContribution.PROCESS_DEFINITION_XML);
            final ProcessDefinitionBARContribution processDefinitionBARContribution = new ProcessDefinitionBARContribution();
            return processDefinitionBARContribution.deserializeProcessDefinition(processDesignFile);
        } catch (final BonitaHomeNotSetException e) {
            throw new ProcessDefinitionNotFoundException(e);
        } catch (final InvalidBusinessArchiveFormatException e) {
            throw new ProcessDefinitionNotFoundException(e);
        } catch (final IOException e) {
            throw new ProcessDefinitionNotFoundException(processDefinitionId, e);
        }
    }

    @Override
    public ProcessDeploymentInfo getProcessDeploymentInfo(final long processDefinitionId)
            throws ProcessDefinitionNotFoundException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();
        try {
            final TransactionContentWithResult<SProcessDefinitionDeployInfo> transactionContentWithResult = new GetProcessDefinitionDeployInfo(
                    processDefinitionId, processDefinitionService);
            transactionContentWithResult.execute();
            return ModelConvertor.toProcessDeploymentInfo(transactionContentWithResult.getResult());
        } catch (final SProcessDefinitionNotFoundException e) {
            throw new ProcessDefinitionNotFoundException(e);
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
    }

    private void log(final TenantServiceAccessor tenantAccessor, final Exception e) {
        final TechnicalLoggerService logger = tenantAccessor.getTechnicalLoggerService();
        if (logger.isLoggable(getClass(), TechnicalLogSeverity.ERROR)) {
            logger.log(this.getClass(), TechnicalLogSeverity.ERROR, e);
        }
    }

    @Override
    public ProcessInstance getProcessInstance(final long processInstanceId)
            throws ProcessInstanceNotFoundException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();
        try {
            final SProcessInstance sProcessInstance = getSProcessInstance(processInstanceId);
            return ModelConvertor
                    .toProcessInstances(Collections.singletonList(sProcessInstance), processDefinitionService)
                    .get(0);
        } catch (final SProcessInstanceNotFoundException notFound) {
            throw new ProcessInstanceNotFoundException(notFound);
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
    }

    protected SProcessInstance getSProcessInstance(final long processInstanceId)
            throws SProcessInstanceNotFoundException, SProcessInstanceReadException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessInstanceService processInstanceService = tenantAccessor.getProcessInstanceService();
        return processInstanceService.getProcessInstance(processInstanceId);
    }

    @Override
    public List<ArchivedProcessInstance> getArchivedProcessInstances(final long processInstanceId,
            final int startIndex, final int maxResults) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final ProcessInstanceService processInstanceService = tenantAccessor.getProcessInstanceService();
        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();
        final GetArchivedProcessInstanceList getProcessInstanceList = new GetArchivedProcessInstanceList(
                processInstanceService, tenantAccessor.getProcessDefinitionService(), searchEntitiesDescriptor,
                processInstanceId, startIndex, maxResults);
        try {
            getProcessInstanceList.execute();
        } catch (final SBonitaException e) {
            log(tenantAccessor, e);
            throw new RetrieveException(e);
        }
        return getProcessInstanceList.getResult();
    }

    @Override
    public ArchivedProcessInstance getArchivedProcessInstance(final long id)
            throws ArchivedProcessInstanceNotFoundException, RetrieveException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessInstanceService processInstanceService = tenantAccessor.getProcessInstanceService();
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();
        try {
            final SAProcessInstance archivedProcessInstance = processInstanceService.getArchivedProcessInstance(id);
            if (archivedProcessInstance == null) {
                throw new ArchivedProcessInstanceNotFoundException(id);
            }
            final SProcessDefinition sProcessDefinition = processDefinitionService
                    .getProcessDefinition(archivedProcessInstance.getProcessDefinitionId());
            return toArchivedProcessInstance(archivedProcessInstance, sProcessDefinition);
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
    }

    /**
     * internal use for mocking purpose
     */
    protected ArchivedProcessInstance toArchivedProcessInstance(final SAProcessInstance archivedProcessInstance,
            final SProcessDefinition sProcessDefinition) {
        return ModelConvertor.toArchivedProcessInstance(archivedProcessInstance, sProcessDefinition);
    }

    @Override
    public ArchivedProcessInstance getFinalArchivedProcessInstance(final long processInstanceId)
            throws ArchivedProcessInstanceNotFoundException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessInstanceService processInstanceService = tenantAccessor.getProcessInstanceService();

        final GetLastArchivedProcessInstance getProcessInstance = new GetLastArchivedProcessInstance(
                processInstanceService, tenantAccessor.getProcessDefinitionService(), processInstanceId,
                tenantAccessor.getSearchEntitiesDescriptor());
        try {
            getProcessInstance.execute();
        } catch (final SProcessInstanceNotFoundException e) {
            log(tenantAccessor, e);
            throw new ArchivedProcessInstanceNotFoundException(e);
        } catch (final SBonitaException e) {
            log(tenantAccessor, e);
            throw new RetrieveException(e);
        }
        return getProcessInstance.getResult();
    }

    @Override
    public ProcessInstance startProcess(final long processDefinitionId)
            throws ProcessActivationException, ProcessExecutionException {
        try {
            return startProcess(getUserId(), processDefinitionId);
        } catch (final ProcessDefinitionNotFoundException e) {
            throw new ProcessExecutionException(e);
        }
    }

    @Override
    public ProcessInstance startProcess(final long userId, final long processDefinitionId)
            throws ProcessDefinitionNotFoundException, ProcessExecutionException, ProcessActivationException {
        return startProcess(userId, processDefinitionId, null, null);
    }

    @Override
    public int getNumberOfActors(final long processDefinitionId) throws ProcessDefinitionNotFoundException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();

        final GetNumberOfActors getNumberofActors = new GetNumberOfActors(processDefinitionService,
                processDefinitionId);
        try {
            getNumberofActors.execute();
        } catch (final SBonitaException e) {
            throw new ProcessDefinitionNotFoundException(e);
        }
        return getNumberofActors.getResult();
    }

    @Override
    public List<ActorInstance> getActors(final long processDefinitionId, final int startIndex, final int maxResults,
            final ActorCriterion sort) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ActorMappingService actorMappingService = tenantAccessor.getActorMappingService();
        try {
            OrderByType order;
            if (sort == null) {
                order = OrderByType.ASC;
            } else {
                switch (sort) {
                case NAME_ASC:
                    order = OrderByType.ASC;
                    break;
                default:
                    order = OrderByType.DESC;
                    break;
                }
            }
            final QueryOptions queryOptions = new QueryOptions(startIndex, maxResults, SActor.class, "name", order);
            final List<SActor> actors = actorMappingService.getActors(processDefinitionId, queryOptions);
            return ModelConvertor.toActors(actors);
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
    }

    @Override
    public List<ActorMember> getActorMembers(final long actorId, final int startIndex, final int maxResults) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ActorMappingService actorMappingService = tenantAccessor.getActorMappingService();

        try {
            final List<SActorMember> actorMembers = actorMappingService.getActorMembers(actorId, startIndex,
                    maxResults);
            return ModelConvertor.toActorMembers(actorMembers);
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }

    }

    @Override
    public long getNumberOfActorMembers(final long actorId) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final ActorMappingService actorMappingService = tenantAccessor.getActorMappingService();
        final GetNumberOfActorMembers numberOfActorMembers = new GetNumberOfActorMembers(actorMappingService,
                actorId);
        try {
            numberOfActorMembers.execute();
            return numberOfActorMembers.getResult();
        } catch (final SBonitaException sbe) {
            return 0; // FIXME throw retrieve exception
        }
    }

    @Override
    public long getNumberOfUsersOfActor(final long actorId) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final ActorMappingService actorMappingService = tenantAccessor.getActorMappingService();
        final GetNumberOfUsersOfActor numberOfUsersOfActor = new GetNumberOfUsersOfActor(actorMappingService,
                actorId);
        numberOfUsersOfActor.execute();
        return numberOfUsersOfActor.getResult();
    }

    @Override
    public long getNumberOfRolesOfActor(final long actorId) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final ActorMappingService actorMappingService = tenantAccessor.getActorMappingService();
        final GetNumberOfRolesOfActor numberOfRolesOfActor = new GetNumberOfRolesOfActor(actorMappingService,
                actorId);
        numberOfRolesOfActor.execute();
        return numberOfRolesOfActor.getResult();
    }

    @Override
    public long getNumberOfGroupsOfActor(final long actorId) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final ActorMappingService actorMappingService = tenantAccessor.getActorMappingService();
        final GetNumberOfGroupsOfActor numberOfGroupsOfActor = new GetNumberOfGroupsOfActor(actorMappingService,
                actorId);
        numberOfGroupsOfActor.execute();
        return numberOfGroupsOfActor.getResult();
    }

    @Override
    public long getNumberOfMembershipsOfActor(final long actorId) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final ActorMappingService actorMappingService = tenantAccessor.getActorMappingService();
        final GetNumberOfMembershipsOfActor getNumber = new GetNumberOfMembershipsOfActor(actorMappingService,
                actorId);
        getNumber.execute();
        return getNumber.getResult();
    }

    @Override
    public ActorInstance updateActor(final long actorId, final ActorUpdater descriptor)
            throws ActorNotFoundException, UpdateException {
        if (descriptor == null || descriptor.getFields().isEmpty()) {
            throw new UpdateException("The update descriptor does not contain field updates");
        }
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final ActorMappingService actorMappingService = tenantAccessor.getActorMappingService();
        final SActorUpdateBuilder actorUpdateBuilder = BuilderFactory.get(SActorUpdateBuilderFactory.class)
                .createNewInstance();
        final Map<ActorField, Serializable> fields = descriptor.getFields();
        for (final Entry<ActorField, Serializable> field : fields.entrySet()) {
            switch (field.getKey()) {
            case DISPLAY_NAME:
                actorUpdateBuilder.updateDisplayName((String) field.getValue());
                break;
            case DESCRIPTION:
                actorUpdateBuilder.updateDescription((String) field.getValue());
                break;
            default:
                break;
            }
        }
        final EntityUpdateDescriptor updateDescriptor = actorUpdateBuilder.done();
        SActor updateActor;
        try {
            updateActor = actorMappingService.updateActor(actorId, updateDescriptor);
            return ModelConvertor.toActorInstance(updateActor);
        } catch (final SActorNotFoundException e) {
            throw new ActorNotFoundException(e);
        } catch (final SBonitaException e) {
            throw new UpdateException(e);
        }
    }

    @Override
    public ActorMember addUserToActor(final long actorId, final long userId)
            throws CreationException, AlreadyExistsException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ActorMappingService actorMappingService = tenantAccessor.getActorMappingService();

        try {
            // Check if the mapping already to throw a specific exception
            checkIfActorMappingForUserAlreadyExists(actorId, userId);

            final SActorMember actorMember = actorMappingService.addUserToActor(actorId, userId);
            final long processDefinitionId = actorMappingService.getActor(actorId).getScopeId();
            final ActorMember clientActorMember = ModelConvertor.toActorMember(actorMember);
            tenantAccessor.getDependencyResolver().resolveDependencies(processDefinitionId, tenantAccessor);
            return clientActorMember;
        } catch (final SBonitaException sbe) {
            throw new CreationException(sbe);
        }
    }

    private void checkIfActorMappingForUserAlreadyExists(final long actorId, final long userId)
            throws AlreadyExistsException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ActorMappingService actorMappingService = tenantAccessor.getActorMappingService();

        try {
            final SActorMember sActorMember = actorMappingService.getActorMember(actorId, userId, -1, -1);
            if (sActorMember != null) {
                throw new AlreadyExistsException("The mapping already exists for the actor id = <" + actorId
                        + ">, the user id = <" + userId + ">");
            }
        } catch (final SBonitaReadException e) {
            // Do nothing
        }
    }

    @Override
    public ActorMember addUserToActor(final String actorName, final ProcessDefinition processDefinition,
            final long userId) throws CreationException, AlreadyExistsException, ActorNotFoundException {
        final List<ActorInstance> actors = getActors(processDefinition.getId(), 0, Integer.MAX_VALUE,
                ActorCriterion.NAME_ASC);
        for (final ActorInstance ai : actors) {
            if (actorName.equals(ai.getName())) {
                return addUserToActor(ai.getId(), userId);
            }
        }
        throw new ActorNotFoundException(
                "Actor " + actorName + " not found in process definition " + processDefinition.getName());
    }

    @Override
    public ActorMember addGroupToActor(final long actorId, final long groupId)
            throws CreationException, AlreadyExistsException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ActorMappingService actorMappingService = tenantAccessor.getActorMappingService();
        try {
            // Check if the mapping already to throw a specific exception
            checkIfActorMappingForGroupAlreadyExists(actorId, groupId);

            final SActorMember actorMember = actorMappingService.addGroupToActor(actorId, groupId);
            final long processDefinitionId = actorMappingService.getActor(actorId).getScopeId();
            final ActorMember clientActorMember = ModelConvertor.toActorMember(actorMember);
            tenantAccessor.getDependencyResolver().resolveDependencies(processDefinitionId, tenantAccessor);
            return clientActorMember;
        } catch (final SBonitaException e) {
            throw new CreationException(e);
        }
    }

    private void checkIfActorMappingForGroupAlreadyExists(final long actorId, final long groupId)
            throws AlreadyExistsException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ActorMappingService actorMappingService = tenantAccessor.getActorMappingService();

        try {
            final SActorMember sActorMember = actorMappingService.getActorMember(actorId, -1, groupId, -1);
            if (sActorMember != null) {
                throw new AlreadyExistsException("The mapping already exists for the actor id = <" + actorId
                        + ">, the group id = <" + groupId + ">");
            }
        } catch (final SBonitaReadException e) {
            // Do nothing
        }
    }

    @Override
    public ActorMember addGroupToActor(final String actorName, final long groupId,
            final ProcessDefinition processDefinition)
            throws CreationException, AlreadyExistsException, ActorNotFoundException {
        final List<ActorInstance> actors = getActors(processDefinition.getId(), 0, Integer.MAX_VALUE,
                ActorCriterion.NAME_ASC);
        for (final ActorInstance actorInstance : actors) {
            if (actorName.equals(actorInstance.getName())) {
                return addGroupToActor(actorInstance.getId(), groupId);
            }
        }
        throw new ActorNotFoundException(
                "Actor " + actorName + " not found in process definition " + processDefinition.getName());
    }

    @Override
    public ActorMember addRoleToActor(final long actorId, final long roleId) throws CreationException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ActorMappingService actorMappingService = tenantAccessor.getActorMappingService();

        try {
            // Check if the mapping already to throw a specific exception
            checkIfActorMappingForRoleAlreadyExists(actorId, roleId);

            final SActorMember actorMember = actorMappingService.addRoleToActor(actorId, roleId);
            final long processDefinitionId = actorMappingService.getActor(actorId).getScopeId();
            final ActorMember clientActorMember = ModelConvertor.toActorMember(actorMember);
            tenantAccessor.getDependencyResolver().resolveDependencies(processDefinitionId, tenantAccessor);
            return clientActorMember;
        } catch (final SBonitaException sbe) {
            throw new CreationException(sbe);
        }
    }

    private void checkIfActorMappingForRoleAlreadyExists(final long actorId, final long roleId)
            throws AlreadyExistsException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ActorMappingService actorMappingService = tenantAccessor.getActorMappingService();

        try {
            final SActorMember sActorMember = actorMappingService.getActorMember(actorId, -1, -1, roleId);
            if (sActorMember != null) {
                throw new AlreadyExistsException("The mapping already exists for the actor id = <" + actorId
                        + ">, the role id = <" + roleId + ">");
            }
        } catch (final SBonitaReadException e) {
            // Do nothing
        }
    }

    @Override
    public ActorMember addRoleToActor(final String actorName, final ProcessDefinition processDefinition,
            final long roleId) throws ActorNotFoundException, CreationException {
        final List<ActorInstance> actors = getActors(processDefinition.getId(), 0, Integer.MAX_VALUE,
                ActorCriterion.NAME_ASC);
        for (final ActorInstance ai : actors) {
            if (actorName.equals(ai.getName())) {
                return addRoleToActor(ai.getId(), roleId);
            }
        }
        throw new ActorNotFoundException(
                "Actor " + actorName + " not found in process definition " + processDefinition.getName());
    }

    @Override
    public ActorMember addRoleAndGroupToActor(final String actorName, final ProcessDefinition processDefinition,
            final long roleId, final long groupId) throws ActorNotFoundException, CreationException {
        final List<ActorInstance> actors = getActors(processDefinition.getId(), 0, Integer.MAX_VALUE,
                ActorCriterion.NAME_ASC);
        for (final ActorInstance ai : actors) {
            if (actorName.equals(ai.getName())) {
                return addRoleAndGroupToActor(ai.getId(), roleId, groupId);
            }
        }
        throw new ActorNotFoundException(
                "Actor " + actorName + " not found in process definition " + processDefinition.getName());
    }

    @Override
    public ActorMember addRoleAndGroupToActor(final long actorId, final long roleId, final long groupId)
            throws CreationException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ActorMappingService actorMappingService = tenantAccessor.getActorMappingService();

        try {
            checkIfActorMappingForMembershipAlreadyExists(actorId, roleId, groupId);

            final SActorMember actorMember = actorMappingService.addRoleAndGroupToActor(actorId, roleId, groupId);
            final long processDefinitionId = actorMappingService.getActor(actorId).getScopeId();
            final ActorMember clientActorMember = ModelConvertor.toActorMember(actorMember);
            tenantAccessor.getDependencyResolver().resolveDependencies(processDefinitionId, tenantAccessor);
            return clientActorMember;
        } catch (final SBonitaException sbe) {
            throw new CreationException(sbe);
        }
    }

    private void checkIfActorMappingForMembershipAlreadyExists(final long actorId, final long roleId,
            final long groupId) throws AlreadyExistsException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ActorMappingService actorMappingService = tenantAccessor.getActorMappingService();

        try {
            final SActorMember sActorMember = actorMappingService.getActorMember(actorId, -1, groupId, roleId);
            if (sActorMember != null) {
                throw new AlreadyExistsException("The mapping already exists for the actor id = <" + actorId
                        + ">, the role id = <" + roleId + ">, the group id = <" + groupId + ">");
            }
        } catch (final SBonitaReadException e) {
            // Do nothing
        }
    }

    @Override
    public void removeActorMember(final long actorMemberId) throws DeletionException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final ActorMappingService actorMappingService = tenantAccessor.getActorMappingService();
        final RemoveActorMember removeActorMember = new RemoveActorMember(actorMappingService, actorMemberId);
        // FIXME remove an actor member when process is running!
        try {
            removeActorMember.execute();
            final SActorMember actorMember = removeActorMember.getResult();
            final long processDefinitionId = getActor(actorMember.getActorId()).getProcessDefinitionId();
            tenantAccessor.getDependencyResolver().resolveDependencies(processDefinitionId, tenantAccessor);
        } catch (final SBonitaException sbe) {
            throw new DeletionException(sbe);
        } catch (final ActorNotFoundException e) {
            throw new DeletionException(e);
        }
    }

    @Override
    public ActorInstance getActor(final long actorId) throws ActorNotFoundException {
        try {
            final TenantServiceAccessor tenantAccessor = getTenantAccessor();
            final ActorMappingService actorMappingService = tenantAccessor.getActorMappingService();

            final GetActor getActor = new GetActor(actorMappingService, actorId);
            getActor.execute();
            return ModelConvertor.toActorInstance(getActor.getResult());
        } catch (final SBonitaException e) {
            throw new ActorNotFoundException(e);
        }
    }

    @Override
    public ActivityInstance getActivityInstance(final long activityInstanceId)
            throws ActivityInstanceNotFoundException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final FlowNodeStateManager flowNodeStateManager = tenantAccessor.getFlowNodeStateManager();
        final SActivityInstance sActivityInstance;
        try {
            sActivityInstance = getSActivityInstance(activityInstanceId);
        } catch (final SActivityInstanceNotFoundException e) {
            throw new ActivityInstanceNotFoundException(activityInstanceId);
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
        return ModelConvertor.toActivityInstance(sActivityInstance, flowNodeStateManager);
    }

    protected SActivityInstance getSActivityInstance(final long activityInstanceId)
            throws SActivityInstanceNotFoundException, SActivityReadException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        return activityInstanceService.getActivityInstance(activityInstanceId);
    }

    @Override
    public FlowNodeInstance getFlowNodeInstance(final long flowNodeInstanceId)
            throws FlowNodeInstanceNotFoundException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        final FlowNodeStateManager flowNodeStateManager = tenantAccessor.getFlowNodeStateManager();
        try {
            final SFlowNodeInstance flowNodeInstance = activityInstanceService
                    .getFlowNodeInstance(flowNodeInstanceId);
            return ModelConvertor.toFlowNodeInstance(flowNodeInstance, flowNodeStateManager);
        } catch (final SFlowNodeNotFoundException e) {
            throw new FlowNodeInstanceNotFoundException(e);
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
    }

    @Override
    public List<HumanTaskInstance> getAssignedHumanTaskInstances(final long userId, final int startIndex,
            final int maxResults, final ActivityInstanceCriterion pagingCriterion) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final FlowNodeStateManager flowNodeStateManager = tenantAccessor.getFlowNodeStateManager();
        final OrderAndField orderAndField = OrderAndFields.getOrderAndFieldForActivityInstance(pagingCriterion);

        final ActivityInstanceService instanceService = tenantAccessor.getActivityInstanceService();
        try {
            final GetAssignedTasks getAssignedTasks = new GetAssignedTasks(instanceService, userId, startIndex,
                    maxResults, orderAndField.getField(), orderAndField.getOrder());
            getAssignedTasks.execute();
            final List<SHumanTaskInstance> assignedTasks = getAssignedTasks.getResult();
            return ModelConvertor.toHumanTaskInstances(assignedTasks, flowNodeStateManager);
        } catch (final SUserNotFoundException e) {
            return Collections.emptyList();
        } catch (final SBonitaException e) {
            return Collections.emptyList();
        }
    }

    @Override
    public long getNumberOfPendingHumanTaskInstances(final long userId) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ActorMappingService actorMappingService = tenantAccessor.getActorMappingService();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();

        final ProcessDefinitionService processDefService = tenantAccessor.getProcessDefinitionService();
        try {
            final Set<Long> actorIds = getActorsForUser(userId, actorMappingService, processDefService);
            if (actorIds.isEmpty()) {
                return 0L;
            }
            return activityInstanceService.getNumberOfPendingTasksForUser(userId, QueryOptions.countQueryOptions());
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
    }

    @Override
    public List<HumanTaskInstance> getPendingHumanTaskInstances(final long userId, final int startIndex,
            final int maxResults, final ActivityInstanceCriterion pagingCriterion) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ActorMappingService actorMappingService = tenantAccessor.getActorMappingService();
        final FlowNodeStateManager flowNodeStateManager = tenantAccessor.getFlowNodeStateManager();
        final OrderAndField orderAndField = OrderAndFields.getOrderAndFieldForActivityInstance(pagingCriterion);

        final ProcessDefinitionService definitionService = tenantAccessor.getProcessDefinitionService();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        try {
            final Set<Long> actorIds = getActorsForUser(userId, actorMappingService, definitionService);
            final List<SHumanTaskInstance> pendingTasks = activityInstanceService.getPendingTasks(userId, actorIds,
                    startIndex, maxResults, orderAndField.getField(), orderAndField.getOrder());
            return ModelConvertor.toHumanTaskInstances(pendingTasks, flowNodeStateManager);
        } catch (final SBonitaException e) {
            return Collections.emptyList();
        }
    }

    private Set<Long> getActorsForUser(final long userId, final ActorMappingService actorMappingService,
            final ProcessDefinitionService definitionService)
            throws SBonitaReadException, SProcessDefinitionReadException {
        final Set<Long> actorIds = new HashSet<Long>();
        final List<Long> processDefIds = definitionService.getProcessDefinitionIds(0, Integer.MAX_VALUE);
        if (!processDefIds.isEmpty()) {
            final Set<Long> processDefinitionIds = new HashSet<Long>(processDefIds);
            final List<SActor> actors = actorMappingService.getActors(processDefinitionIds, userId);
            for (final SActor sActor : actors) {
                actorIds.add(sActor.getId());
            }
        }
        return actorIds;
    }

    @Override
    public ArchivedActivityInstance getArchivedActivityInstance(final long activityInstanceId)
            throws ActivityInstanceNotFoundException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        final FlowNodeStateManager flowNodeStateManager = tenantAccessor.getFlowNodeStateManager();
        final GetArchivedActivityInstance getActivityInstance = new GetArchivedActivityInstance(
                activityInstanceService, activityInstanceId);
        try {
            getActivityInstance.execute();
        } catch (final SActivityInstanceNotFoundException e) {
            throw new ActivityInstanceNotFoundException(activityInstanceId, e);
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
        return ModelConvertor.toArchivedActivityInstance(getActivityInstance.getResult(), flowNodeStateManager);
    }

    @Override
    public ArchivedFlowNodeInstance getArchivedFlowNodeInstance(final long archivedFlowNodeInstanceId)
            throws ArchivedFlowNodeInstanceNotFoundException, RetrieveException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        final FlowNodeStateManager flowNodeStateManager = tenantAccessor.getFlowNodeStateManager();

        try {
            final SAFlowNodeInstance archivedFlowNodeInstance = activityInstanceService
                    .getArchivedFlowNodeInstance(archivedFlowNodeInstanceId);
            return ModelConvertor.toArchivedFlowNodeInstance(archivedFlowNodeInstance, flowNodeStateManager);
        } catch (final SFlowNodeNotFoundException e) {
            throw new ArchivedFlowNodeInstanceNotFoundException(archivedFlowNodeInstanceId);
        } catch (final SFlowNodeReadException e) {
            throw new RetrieveException(e);
        }
    }

    @Override
    public List<ProcessInstance> getProcessInstances(final int startIndex, final int maxResults,
            final ProcessInstanceCriterion criterion) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final OrderAndField orderAndField = OrderAndFields.getOrderAndFieldForProcessInstance(criterion);
        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(startIndex, maxResults);
        searchOptionsBuilder.sort(orderAndField.getField(), Order.valueOf(orderAndField.getOrder().name()));
        List<ProcessInstance> result;
        try {
            result = searchProcessInstances(tenantAccessor, searchOptionsBuilder.done()).getResult();
        } catch (final SBonitaException e) {
            result = Collections.emptyList();
        }
        return result;
    }

    @Override
    public long getNumberOfProcessInstances() {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();
        final ProcessInstanceService processInstanceService = tenantAccessor.getProcessInstanceService();
        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();

        try {
            final TransactionContentWithResult<Long> transactionContent = new GetNumberOfProcessInstance(
                    processInstanceService, processDefinitionService, searchEntitiesDescriptor);
            transactionContent.execute();
            return transactionContent.getResult();
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
    }

    protected SearchResult<ProcessInstance> searchProcessInstances(final TenantServiceAccessor tenantAccessor,
            final SearchOptions searchOptions) throws SBonitaException {
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();
        final ProcessInstanceService processInstanceService = tenantAccessor.getProcessInstanceService();
        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();

        final SearchProcessInstances searchProcessInstances = new SearchProcessInstances(processInstanceService,
                searchEntitiesDescriptor.getSearchProcessInstanceDescriptor(), searchOptions,
                processDefinitionService);
        searchProcessInstances.execute();
        return searchProcessInstances.getResult();
    }

    @Override
    public List<ArchivedProcessInstance> getArchivedProcessInstances(final int startIndex, final int maxResults,
            final ProcessInstanceCriterion criterion) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final OrderAndField orderAndField = OrderAndFields.getOrderAndFieldForProcessInstance(criterion);

        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(startIndex, maxResults);
        searchOptionsBuilder.sort(orderAndField.getField(), Order.valueOf(orderAndField.getOrder().name()));
        searchOptionsBuilder.filter(ArchivedProcessInstancesSearchDescriptor.STATE_ID,
                ProcessInstanceState.COMPLETED.getId());
        searchOptionsBuilder.filter(ArchivedProcessInstancesSearchDescriptor.CALLER_ID, -1);
        final SearchArchivedProcessInstances searchArchivedProcessInstances = searchArchivedProcessInstances(
                tenantAccessor, searchOptionsBuilder.done());
        try {
            searchArchivedProcessInstances.execute();
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }

        return searchArchivedProcessInstances.getResult().getResult();
    }

    private SearchArchivedProcessInstances searchArchivedProcessInstances(
            final TenantServiceAccessor tenantAccessor, final SearchOptions searchOptions)
            throws RetrieveException {
        final ProcessInstanceService processInstanceService = tenantAccessor.getProcessInstanceService();
        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();
        return new SearchArchivedProcessInstances(processInstanceService,
                tenantAccessor.getProcessDefinitionService(),
                searchEntitiesDescriptor.getSearchArchivedProcessInstanceDescriptor(), searchOptions);
    }

    @Override
    public long getNumberOfArchivedProcessInstances() {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessInstanceService processInstanceService = tenantAccessor.getProcessInstanceService();
        try {
            final SAProcessInstanceBuilderFactory saProcessInstanceBuilder = BuilderFactory
                    .get(SAProcessInstanceBuilderFactory.class);
            final List<FilterOption> filterOptions = new ArrayList<FilterOption>(2);
            filterOptions.add(new FilterOption(SAProcessInstance.class, saProcessInstanceBuilder.getStateIdKey(),
                    ProcessInstanceState.COMPLETED.getId()));
            filterOptions
                    .add(new FilterOption(SAProcessInstance.class, saProcessInstanceBuilder.getCallerIdKey(), -1));
            final QueryOptions queryOptions = new QueryOptions(0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS,
                    Collections.<OrderByOption>emptyList(), filterOptions, null);
            return processInstanceService.getNumberOfArchivedProcessInstances(queryOptions);
        } catch (final SBonitaException e) {
            log(tenantAccessor, e);
            throw new RetrieveException(e);
        }
    }

    @Override
    public SearchResult<ArchivedProcessInstance> searchArchivedProcessInstances(final SearchOptions searchOptions)
            throws RetrieveException, SearchException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessInstanceService processInstanceService = tenantAccessor.getProcessInstanceService();
        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();
        final SearchArchivedProcessInstancesWithoutSubProcess searchArchivedProcessInstances = new SearchArchivedProcessInstancesWithoutSubProcess(
                processInstanceService, tenantAccessor.getProcessDefinitionService(),
                searchEntitiesDescriptor.getSearchArchivedProcessInstanceDescriptor(), searchOptions);
        try {
            searchArchivedProcessInstances.execute();
        } catch (final SBonitaException e) {
            throw new SearchException(e);
        }
        return searchArchivedProcessInstances.getResult();
    }

    @Override
    public SearchResult<ArchivedProcessInstance> searchArchivedProcessInstancesInAllStates(
            final SearchOptions searchOptions) throws SearchException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessInstanceService processInstanceService = tenantAccessor.getProcessInstanceService();
        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();
        final SearchArchivedProcessInstances searchArchivedProcessInstances = new SearchArchivedProcessInstances(
                processInstanceService, tenantAccessor.getProcessDefinitionService(),
                searchEntitiesDescriptor.getSearchArchivedProcessInstanceDescriptor(), searchOptions);
        try {
            searchArchivedProcessInstances.execute();
        } catch (final SBonitaException e) {
            throw new SearchException(e);
        }
        return searchArchivedProcessInstances.getResult();
    }

    @Override
    public List<ActivityInstance> getOpenActivityInstances(final long processInstanceId, final int startIndex,
            final int maxResults, final ActivityInstanceCriterion criterion) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        final FlowNodeStateManager flowNodeStateManager = tenantAccessor.getFlowNodeStateManager();

        try {
            final int totalNumber = activityInstanceService.getNumberOfOpenActivityInstances(processInstanceId);
            // If there are no instances, return an empty list:
            if (totalNumber == 0) {
                return Collections.<ActivityInstance>emptyList();
            }
            final OrderAndField orderAndField = OrderAndFields.getOrderAndFieldForActivityInstance(criterion);
            return ModelConvertor
                    .toActivityInstances(
                            activityInstanceService.getOpenActivityInstances(processInstanceId, startIndex,
                                    maxResults, orderAndField.getField(), orderAndField.getOrder()),
                            flowNodeStateManager);
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
    }

    @Override
    public List<ArchivedActivityInstance> getArchivedActivityInstances(final long processInstanceId,
            final int startIndex, final int maxResults, final ActivityInstanceCriterion criterion) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final List<ArchivedActivityInstance> archivedActivityInstances = getArchivedActivityInstances(
                processInstanceId, startIndex, maxResults, criterion, tenantAccessor);
        return archivedActivityInstances;
    }

    private List<ArchivedActivityInstance> getArchivedActivityInstances(final long processInstanceId,
            final int pageIndex, final int numberPerPage, final ActivityInstanceCriterion pagingCriterion,
            final TenantServiceAccessor tenantAccessor) throws RetrieveException {

        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        final FlowNodeStateManager flowNodeStateManager = tenantAccessor.getFlowNodeStateManager();
        final OrderAndField orderAndField = OrderAndFields.getOrderAndFieldForActivityInstance(pagingCriterion);
        final GetArchivedActivityInstances getActivityInstances = new GetArchivedActivityInstances(
                activityInstanceService, processInstanceId, pageIndex, numberPerPage, orderAndField.getField(),
                orderAndField.getOrder());
        try {
            getActivityInstances.execute();
        } catch (final SBonitaException e) {
            log(tenantAccessor, e);
            throw new RetrieveException(e);
        }
        return ModelConvertor.toArchivedActivityInstances(getActivityInstances.getResult(), flowNodeStateManager);
    }

    @Override
    public int getNumberOfOpenedActivityInstances(final long processInstanceId) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();

        final TransactionContentWithResult<Integer> transactionContentWithResult = new GetNumberOfActivityInstance(
                processInstanceId, activityInstanceService);
        try {
            transactionContentWithResult.execute();
            return transactionContentWithResult.getResult();
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
    }

    @Override
    public Category createCategory(final String name, final String description)
            throws AlreadyExistsException, CreationException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final CategoryService categoryService = tenantAccessor.getCategoryService();

        try {
            final CreateCategory createCategory = new CreateCategory(name, description, categoryService);
            createCategory.execute();
            return ModelConvertor.toCategory(createCategory.getResult());
        } catch (final SCategoryAlreadyExistsException scaee) {
            throw new AlreadyExistsException(scaee);
        } catch (final SBonitaException e) {
            throw new CreationException("Category create exception!", e);
        }
    }

    @Override
    public Category getCategory(final long categoryId) throws CategoryNotFoundException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final CategoryService categoryService = tenantAccessor.getCategoryService();
        try {
            final GetCategory getCategory = new GetCategory(categoryService, categoryId);
            getCategory.execute();
            final SCategory sCategory = getCategory.getResult();
            return ModelConvertor.toCategory(sCategory);
        } catch (final SBonitaException sbe) {
            throw new CategoryNotFoundException(sbe);
        }
    }

    @Override
    public long getNumberOfCategories() {
        final CategoryService categoryService = getTenantAccessor().getCategoryService();
        try {
            final GetNumberOfCategories getNumberOfCategories = new GetNumberOfCategories(categoryService);
            getNumberOfCategories.execute();
            return getNumberOfCategories.getResult();
        } catch (final SBonitaException e) {
            return 0;
        }
    }

    @Override
    public List<Category> getCategories(final int startIndex, final int maxResults,
            final CategoryCriterion sortCriterion) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final SCategoryBuilderFactory fact = BuilderFactory.get(SCategoryBuilderFactory.class);

        final CategoryService categoryService = tenantAccessor.getCategoryService();
        String field = null;
        OrderByType order = null;
        switch (sortCriterion) {
        case NAME_ASC:
            field = fact.getNameKey();
            order = OrderByType.ASC;
            break;
        case NAME_DESC:
            field = fact.getNameKey();
            order = OrderByType.DESC;
            break;
        default:
            throw new IllegalStateException();
        }
        try {
            final GetCategories getCategories = new GetCategories(startIndex, maxResults, field, categoryService,
                    order);
            getCategories.execute();
            return ModelConvertor.toCategories(getCategories.getResult());
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
    }

    @Override
    public void addCategoriesToProcess(final long processDefinitionId, final List<Long> categoryIds)
            throws AlreadyExistsException, CreationException {
        try {
            final CategoryService categoryService = getTenantAccessor().getCategoryService();
            final ProcessDefinitionService processDefinitionService = getTenantAccessor()
                    .getProcessDefinitionService();
            for (final Long categoryId : categoryIds) {
                new AddProcessDefinitionToCategory(categoryId, processDefinitionId, categoryService,
                        processDefinitionService).execute();
            }
        } catch (final SCategoryInProcessAlreadyExistsException scipaee) {
            throw new AlreadyExistsException(scipaee);
        } catch (final SBonitaException sbe) {
            throw new CreationException(sbe);
        }
    }

    @Override
    public void removeCategoriesFromProcess(final long processDefinitionId, final List<Long> categoryIds)
            throws DeletionException {
        try {
            final CategoryService categoryService = getTenantAccessor().getCategoryService();
            final TransactionContent transactionContent = new RemoveCategoriesFromProcessDefinition(
                    processDefinitionId, categoryIds, categoryService);
            transactionContent.execute();
        } catch (final SBonitaException sbe) {
            throw new DeletionException(sbe);
        }
    }

    @Override
    public void addProcessDefinitionToCategory(final long categoryId, final long processDefinitionId)
            throws AlreadyExistsException, CreationException {
        try {
            final CategoryService categoryService = getTenantAccessor().getCategoryService();
            final ProcessDefinitionService processDefinitionService = getTenantAccessor()
                    .getProcessDefinitionService();
            final TransactionContent transactionContent = new AddProcessDefinitionToCategory(categoryId,
                    processDefinitionId, categoryService, processDefinitionService);
            transactionContent.execute();
        } catch (final SProcessDefinitionNotFoundException e) {
            throw new CreationException(e);
        } catch (final SCategoryNotFoundException scnfe) {
            throw new CreationException(scnfe);
        } catch (final SCategoryInProcessAlreadyExistsException cipaee) {
            throw new AlreadyExistsException(cipaee);
        } catch (final SBonitaException sbe) {
            throw new CreationException(sbe);
        }
    }

    @Override
    public void addProcessDefinitionsToCategory(final long categoryId, final List<Long> processDefinitionIds)
            throws AlreadyExistsException, CreationException {
        try {
            final CategoryService categoryService = getTenantAccessor().getCategoryService();
            final ProcessDefinitionService processDefinitionService = getTenantAccessor()
                    .getProcessDefinitionService();
            for (final Long processDefinitionId : processDefinitionIds) {
                new AddProcessDefinitionToCategory(categoryId, processDefinitionId, categoryService,
                        processDefinitionService).execute();
            }
        } catch (final SProcessDefinitionNotFoundException e) {
            throw new CreationException(e);
        } catch (final SCategoryNotFoundException scnfe) {
            throw new CreationException(scnfe);
        } catch (final SCategoryInProcessAlreadyExistsException cipaee) {
            throw new AlreadyExistsException(cipaee);
        } catch (final SBonitaException sbe) {
            throw new CreationException(sbe);
        }
    }

    @Override
    public long getNumberOfCategories(final long processDefinitionId) {
        try {
            final TenantServiceAccessor tenantAccessor = getTenantAccessor();

            final CategoryService categoryService = tenantAccessor.getCategoryService();
            final GetNumberOfCategoriesOfProcess getNumberOfCategoriesOfProcess = new GetNumberOfCategoriesOfProcess(
                    categoryService, processDefinitionId);
            getNumberOfCategoriesOfProcess.execute();
            return getNumberOfCategoriesOfProcess.getResult();
        } catch (final SBonitaException sbe) {
            throw new RetrieveException(sbe);
        }
    }

    @Override
    public long getNumberOfProcessDefinitionsOfCategory(final long categoryId) {
        try {
            final CategoryService categoryService = getTenantAccessor().getCategoryService();
            return categoryService.getNumberOfProcessDeploymentInfosOfCategory(categoryId);
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
    }

    @Override
    public List<ProcessDeploymentInfo> getProcessDeploymentInfosOfCategory(final long categoryId,
            final int startIndex, final int maxResults, final ProcessDeploymentInfoCriterion sortCriterion) {
        if (sortCriterion == null) {
            throw new IllegalArgumentException("You must to have a criterion to sort your result.");
        }
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();
        final OrderByType order = buildOrderByType(sortCriterion.getOrder());
        final String field = sortCriterion.getField();
        try {
            final QueryOptions queryOptions = new QueryOptions(startIndex, maxResults,
                    SProcessDefinitionDeployInfo.class, field, order);
            final List<SProcessDefinitionDeployInfo> sProcessDefinitionDeployInfos = processDefinitionService
                    .searchProcessDeploymentInfosOfCategory(categoryId, queryOptions);
            return ModelConvertor.toProcessDeploymentInfo(sProcessDefinitionDeployInfos);
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
    }

    @Override
    public List<Category> getCategoriesOfProcessDefinition(final long processDefinitionId, final int startIndex,
            final int maxResults, final CategoryCriterion sortingCriterion) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final CategoryService categoryService = tenantAccessor.getCategoryService();
        try {
            final OrderByType order = buildOrderByType(sortingCriterion.getOrder());
            return ModelConvertor.toCategories(categoryService.getCategoriesOfProcessDefinition(processDefinitionId,
                    startIndex, maxResults, order));
        } catch (final SBonitaException sbe) {
            throw new RetrieveException(sbe);
        }
    }

    private OrderByType buildOrderByType(final Order order) {
        return OrderByType.valueOf(order.name());
    }

    @Override
    public List<Category> getCategoriesUnrelatedToProcessDefinition(final long processDefinitionId,
            final int startIndex, final int maxResults, final CategoryCriterion sortingCriterion) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final CategoryService categoryService = tenantAccessor.getCategoryService();
        try {
            final OrderByType order = buildOrderByType(sortingCriterion.getOrder());
            return ModelConvertor.toCategories(categoryService
                    .getCategoriesUnrelatedToProcessDefinition(processDefinitionId, startIndex, maxResults, order));
        } catch (final SBonitaException sbe) {
            throw new RetrieveException(sbe);
        }
    }

    @Override
    public void updateCategory(final long categoryId, final CategoryUpdater updater)
            throws CategoryNotFoundException, UpdateException {
        if (updater == null || updater.getFields().isEmpty()) {
            throw new UpdateException("The update descriptor does not contain field updates");
        }
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final CategoryService categoryService = tenantAccessor.getCategoryService();

        try {
            final SCategoryUpdateBuilderFactory fact = BuilderFactory.get(SCategoryUpdateBuilderFactory.class);
            final EntityUpdateDescriptor updateDescriptor = getCategoryUpdateDescriptor(fact.createNewInstance(),
                    updater);
            final UpdateCategory updateCategory = new UpdateCategory(categoryService, categoryId, updateDescriptor);
            updateCategory.execute();
        } catch (final SCategoryNotFoundException scnfe) {
            throw new CategoryNotFoundException(scnfe);
        } catch (final SBonitaException sbe) {
            throw new UpdateException(sbe);
        }
    }

    private EntityUpdateDescriptor getCategoryUpdateDescriptor(final SCategoryUpdateBuilder descriptorBuilder,
            final CategoryUpdater updater) {
        final Map<CategoryField, Serializable> fields = updater.getFields();
        final String name = (String) fields.get(CategoryField.NAME);
        if (name != null) {
            descriptorBuilder.updateName(name);
        }
        final String description = (String) fields.get(CategoryField.DESCRIPTION);
        if (description != null) {
            descriptorBuilder.updateDescription(description);
        }
        return descriptorBuilder.done();
    }

    @Override
    public void deleteCategory(final long categoryId) throws DeletionException {
        if (categoryId <= 0) {
            throw new DeletionException("Category id can not be less than 0!");
        }
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final CategoryService categoryService = tenantAccessor.getCategoryService();

        final DeleteSCategory deleteSCategory = new DeleteSCategory(categoryService, categoryId);
        try {
            deleteSCategory.execute();
        } catch (final SBonitaException sbe) {
            throw new DeletionException(sbe);
        }
    }

    @Override
    public long getNumberOfUncategorizedProcessDefinitions() {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();
        final CategoryService categoryService = tenantAccessor.getCategoryService();
        try {
            final List<Long> processDefinitionIds = processDefinitionService.getProcessDefinitionIds(0,
                    Integer.MAX_VALUE);
            long number;
            if (processDefinitionIds.isEmpty()) {
                number = 0;
            } else {
                number = processDefinitionIds.size()
                        - categoryService.getNumberOfCategorizedProcessIds(processDefinitionIds);
            }
            return number;
        } catch (final SBonitaException e) {
            throw new BonitaRuntimeException(e);// TODO refactor exceptions
        }
    }

    @Override
    public List<ProcessDeploymentInfo> getUncategorizedProcessDeploymentInfos(final int startIndex,
            final int maxResults, final ProcessDeploymentInfoCriterion sortCriterion) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();
        try {
            final QueryOptions queryOptions = new QueryOptions(startIndex, maxResults,
                    SProcessDefinitionDeployInfo.class, sortCriterion.getField(),
                    buildOrderByType(sortCriterion.getOrder()));
            final List<SProcessDefinitionDeployInfo> processDefinitionDeployInfos = processDefinitionService
                    .searchUncategorizedProcessDeploymentInfos(queryOptions);
            return ModelConvertor.toProcessDeploymentInfo(processDefinitionDeployInfos);
        } catch (final SBonitaException sbe) {
            throw new RetrieveException(sbe);
        }
    }

    @Override
    public long getNumberOfProcessDeploymentInfosUnrelatedToCategory(final long categoryId) {
        try {
            final TenantServiceAccessor tenantAccessor = getTenantAccessor();
            final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();

            final GetNumberOfProcessDeploymentInfosUnrelatedToCategory transactionContentWithResult = new GetNumberOfProcessDeploymentInfosUnrelatedToCategory(
                    categoryId, processDefinitionService);
            transactionContentWithResult.execute();
            return transactionContentWithResult.getResult();
        } catch (final SBonitaException sbe) {
            throw new RetrieveException(sbe);
        }
    }

    @Override
    public List<ProcessDeploymentInfo> getProcessDeploymentInfosUnrelatedToCategory(final long categoryId,
            final int startIndex, final int maxResults, final ProcessDeploymentInfoCriterion sortingCriterion) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();

        try {
            return ModelConvertor.toProcessDeploymentInfo(
                    processDefinitionService.getProcessDeploymentInfosUnrelatedToCategory(categoryId, startIndex,
                            maxResults, sortingCriterion));
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
    }

    @Deprecated
    @Override
    public void removeAllCategoriesFromProcessDefinition(final long processDefinitionId) throws DeletionException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final CategoryService categoryService = tenantAccessor.getCategoryService();
        final TransactionContent transactionContent = new RemoveProcessDefinitionsOfCategory(processDefinitionId,
                categoryService);
        try {
            transactionContent.execute();
        } catch (final SBonitaException sbe) {
            throw new DeletionException(sbe);
        }
    }

    @Deprecated
    @Override
    public void removeAllProcessDefinitionsFromCategory(final long categoryId) throws DeletionException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final CategoryService categoryService = tenantAccessor.getCategoryService();

        final RemoveProcessDefinitionsOfCategory remove = new RemoveProcessDefinitionsOfCategory(categoryService,
                categoryId);
        try {
            remove.execute();
        } catch (final SBonitaException sbe) {
            throw new DeletionException(sbe);
        }
    }

    @Override
    public long removeCategoriesFromProcessDefinition(final long processDefinitionId, final int startIndex,
            final int maxResults) throws DeletionException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final CategoryService categoryService = tenantAccessor.getCategoryService();
        final SProcessCategoryMappingBuilderFactory fact = BuilderFactory
                .get(SProcessCategoryMappingBuilderFactory.class);

        try {
            final FilterOption filterOption = new FilterOption(SProcessCategoryMapping.class,
                    fact.getProcessIdKey(), processDefinitionId);
            final OrderByOption order = new OrderByOption(SProcessCategoryMapping.class, fact.getIdKey(),
                    OrderByType.ASC);
            final QueryOptions queryOptions = new QueryOptions(startIndex, maxResults,
                    Collections.singletonList(order), Collections.singletonList(filterOption), null);
            final List<SProcessCategoryMapping> processCategoryMappings = categoryService
                    .searchProcessCategoryMappings(queryOptions);
            return categoryService.deleteProcessCategoryMappings(processCategoryMappings);
        } catch (final SBonitaException e) {
            throw new DeletionException(e);
        }
    }

    @Override
    public long removeProcessDefinitionsFromCategory(final long categoryId, final int startIndex,
            final int maxResults) throws DeletionException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final CategoryService categoryService = tenantAccessor.getCategoryService();
        final SProcessCategoryMappingBuilderFactory fact = BuilderFactory
                .get(SProcessCategoryMappingBuilderFactory.class);

        try {
            final FilterOption filterOption = new FilterOption(SProcessCategoryMapping.class,
                    fact.getCategoryIdKey(), categoryId);
            final OrderByOption order = new OrderByOption(SProcessCategoryMapping.class, fact.getIdKey(),
                    OrderByType.ASC);
            final QueryOptions queryOptions = new QueryOptions(startIndex, maxResults,
                    Collections.singletonList(order), Collections.singletonList(filterOption), null);
            final List<SProcessCategoryMapping> processCategoryMappings = categoryService
                    .searchProcessCategoryMappings(queryOptions);
            return categoryService.deleteProcessCategoryMappings(processCategoryMappings);
        } catch (final SBonitaException e) {
            throw new DeletionException(e);
        }
    }

    @Override
    public List<EventInstance> getEventInstances(final long rootContainerId, final int startIndex,
            final int maxResults, final EventCriterion criterion) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final EventInstanceService eventInstanceService = tenantAccessor.getEventInstanceService();
        final FlowNodeStateManager flowNodeStateManager = tenantAccessor.getFlowNodeStateManager();
        final OrderAndField orderAndField = OrderAndFields.getOrderAndFieldForEvent(criterion);
        final GetEventInstances getEventInstances = new GetEventInstances(eventInstanceService, rootContainerId,
                startIndex, maxResults, orderAndField.getField(), orderAndField.getOrder());
        try {
            getEventInstances.execute();
            final List<SEventInstance> result = getEventInstances.getResult();
            return ModelConvertor.toEventInstances(result, flowNodeStateManager);
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
    }

    @Override
    public void assignUserTask(final long userTaskId, final long userId) throws UpdateException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        try {
            final AssignOrUnassignUserTask assignUserTask = new AssignOrUnassignUserTask(userId, userTaskId,
                    activityInstanceService, tenantAccessor.getFlowNodeStateManager().getStateBehaviors());
            assignUserTask.execute();
        } catch (final SUserNotFoundException sunfe) {
            throw new UpdateException(sunfe);
        } catch (final SActivityInstanceNotFoundException sainfe) {
            throw new UpdateException(sainfe);
        } catch (final SBonitaException sbe) {
            throw new UpdateException(sbe);
        }
    }

    @Override
    public void updateActorsOfUserTask(final long userTaskId) throws UpdateException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        try {
            final SHumanTaskInstance humanTaskInstance = getSHumanTaskInstance(userTaskId);
            final long processDefinitionId = humanTaskInstance.getLogicalGroup(0);
            final SProcessDefinition processDefinition = tenantAccessor.getProcessDefinitionService()
                    .getProcessDefinition(processDefinitionId);
            final SHumanTaskDefinition humanTaskDefinition = (SHumanTaskDefinition) processDefinition
                    .getProcessContainer().getFlowNode(humanTaskInstance.getFlowNodeDefinitionId());
            final long humanTaskInstanceId = humanTaskInstance.getId();
            if (humanTaskDefinition != null) {
                final SUserFilterDefinition sUserFilterDefinition = humanTaskDefinition.getSUserFilterDefinition();
                if (sUserFilterDefinition != null) {
                    cleanPendingMappingsAndUnassignHumanTask(userTaskId, humanTaskInstance);

                    final FilterResult result = executeFilter(processDefinitionId, humanTaskInstanceId,
                            humanTaskDefinition.getActorName(), sUserFilterDefinition);
                    final List<Long> userIds = result.getResult();
                    if (userIds == null || userIds.isEmpty() || userIds.contains(0L) || userIds.contains(-1L)) {
                        throw new UpdateException("no user id returned by the user filter " + sUserFilterDefinition
                                + " on activity " + humanTaskDefinition.getName());
                    }
                    createPendingMappingsAndAssignHumanTask(humanTaskInstanceId, result);
                }
            }
            final TechnicalLoggerService technicalLoggerService = tenantAccessor.getTechnicalLoggerService();
            if (technicalLoggerService.isLoggable(ProcessAPIImpl.class, TechnicalLogSeverity.INFO)) {
                final StringBuilder builder = new StringBuilder("User '");
                builder.append(SessionInfos.getUserNameFromSession())
                        .append("' has re-executed assignation on activity ").append(humanTaskInstanceId);
                builder.append(" of process instance ").append(humanTaskInstance.getLogicalGroup(1))
                        .append(" of process named '").append(processDefinition.getName()).append("' in version ")
                        .append(processDefinition.getVersion());
                technicalLoggerService.log(ProcessAPIImpl.class, TechnicalLogSeverity.INFO, builder.toString());
            }
        } catch (final SBonitaException sbe) {
            throw new UpdateException(sbe);
        }
    }

    private void createPendingMappingsAndAssignHumanTask(final long humanTaskInstanceId, final FilterResult result)
            throws SBonitaException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final List<Long> userIds = result.getResult();
        for (final Long userId : userIds) {
            createPendingMappingForUser(humanTaskInstanceId, userId);
        }
        if (userIds.size() == 1 && result.shouldAutoAssignTaskIfSingleResult()) {
            tenantAccessor.getActivityInstanceService().assignHumanTask(humanTaskInstanceId, userIds.get(0));
        }
    }

    private FilterResult executeFilter(final long processDefinitionId, final long humanTaskInstanceId,
            final String actorName, final SUserFilterDefinition sUserFilterDefinition)
            throws SUserFilterExecutionException, SClassLoaderException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ClassLoaderService classLoaderService = tenantAccessor.getClassLoaderService();
        final UserFilterService userFilterService = tenantAccessor.getUserFilterService();
        final ClassLoader processClassloader = classLoaderService.getLocalClassLoader(ScopeType.PROCESS.name(),
                processDefinitionId);
        final SExpressionContext expressionContext = new SExpressionContext(humanTaskInstanceId,
                DataInstanceContainer.ACTIVITY_INSTANCE.name(), processDefinitionId);
        return userFilterService.executeFilter(processDefinitionId, sUserFilterDefinition,
                sUserFilterDefinition.getInputs(), processClassloader, expressionContext, actorName);
    }

    private void cleanPendingMappingsAndUnassignHumanTask(final long userTaskId,
            final SHumanTaskInstance humanTaskInstance)
            throws SFlowNodeNotFoundException, SFlowNodeReadException, SActivityModificationException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        // release
        if (humanTaskInstance.getAssigneeId() > 0) {
            activityInstanceService.assignHumanTask(userTaskId, 0);
        }
        activityInstanceService.deletePendingMappings(humanTaskInstance.getId());
    }

    private SHumanTaskInstance getSHumanTaskInstance(final long userTaskId)
            throws SFlowNodeNotFoundException, SFlowNodeReadException, UpdateException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final SFlowNodeInstance flowNodeInstance = tenantAccessor.getActivityInstanceService()
                .getFlowNodeInstance(userTaskId);
        if (!(flowNodeInstance instanceof SHumanTaskInstance)) {
            throw new UpdateException("The identifier does not refer to a human task");
        }
        return (SHumanTaskInstance) flowNodeInstance;
    }

    private void createPendingMappingForUser(final long humanTaskInstanceId, final Long userId)
            throws SBonitaException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        final SPendingActivityMappingBuilderFactory sPendingActivityMappingBuilderFactory = BuilderFactory
                .get(SPendingActivityMappingBuilderFactory.class);
        final SPendingActivityMapping mapping = sPendingActivityMappingBuilderFactory
                .createNewInstanceForUser(humanTaskInstanceId, userId).done();
        activityInstanceService.addPendingActivityMappings(mapping);
    }

    @Override
    public List<DataDefinition> getActivityDataDefinitions(final long processDefinitionId,
            final String activityName, final int startIndex, final int maxResults)
            throws ActivityDefinitionNotFoundException, ProcessDefinitionNotFoundException {
        List<DataDefinition> subDataDefinitionList = Collections.emptyList();
        List<SDataDefinition> sdataDefinitionList = Collections.emptyList();
        final TenantServiceAccessor tenantAccessor;
        tenantAccessor = getTenantAccessor();

        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();
        final GetProcessDefinition getProcessDefinition = new GetProcessDefinition(processDefinitionId,
                processDefinitionService);
        try {
            getProcessDefinition.execute();
        } catch (final SProcessDefinitionNotFoundException e) {
            throw new ProcessDefinitionNotFoundException(e);
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
        final SProcessDefinition sProcessDefinition = getProcessDefinition.getResult();
        if (sProcessDefinition != null) {
            boolean activityFound = false;
            final SFlowElementContainerDefinition processContainer = sProcessDefinition.getProcessContainer();
            final Set<SActivityDefinition> activityDefList = processContainer.getActivities();
            for (final SActivityDefinition sActivityDefinition : activityDefList) {
                if (activityName.equals(sActivityDefinition.getName())) {
                    sdataDefinitionList = sActivityDefinition.getSDataDefinitions();
                    activityFound = true;
                    break;
                }
            }
            if (!activityFound) {
                throw new ActivityDefinitionNotFoundException(activityName);
            }
            final List<DataDefinition> dataDefinitionList = ModelConvertor.toDataDefinitions(sdataDefinitionList);
            if (startIndex >= dataDefinitionList.size()) {
                return Collections.emptyList();
            }
            final int toIndex = Math.min(dataDefinitionList.size(), startIndex + maxResults);
            subDataDefinitionList = new ArrayList<DataDefinition>(dataDefinitionList.subList(startIndex, toIndex));
        }
        return subDataDefinitionList;
    }

    @Override
    public List<DataDefinition> getProcessDataDefinitions(final long processDefinitionId, final int startIndex,
            final int maxResults) throws ProcessDefinitionNotFoundException {
        List<DataDefinition> subDataDefinitionList = Collections.emptyList();
        final TenantServiceAccessor tenantAccessor;
        tenantAccessor = getTenantAccessor();

        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();
        final GetProcessDefinition getProcessDefinition = new GetProcessDefinition(processDefinitionId,
                processDefinitionService);
        try {
            getProcessDefinition.execute();
        } catch (final SProcessDefinitionNotFoundException e) {
            throw new ProcessDefinitionNotFoundException(e);
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }

        final SProcessDefinition sProcessDefinition = getProcessDefinition.getResult();
        if (sProcessDefinition != null) {
            final SFlowElementContainerDefinition processContainer = sProcessDefinition.getProcessContainer();
            final List<SDataDefinition> sdataDefinitionList = processContainer.getDataDefinitions();
            final List<DataDefinition> dataDefinitionList = ModelConvertor.toDataDefinitions(sdataDefinitionList);
            if (startIndex >= dataDefinitionList.size()) {
                return Collections.emptyList();
            }
            final int toIndex = Math.min(dataDefinitionList.size(), startIndex + maxResults);
            subDataDefinitionList = new ArrayList<DataDefinition>(dataDefinitionList.subList(startIndex, toIndex));
        }
        return subDataDefinitionList;
    }

    @Override
    public HumanTaskInstance getHumanTaskInstance(final long activityInstanceId)
            throws ActivityInstanceNotFoundException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        final FlowNodeStateManager flowNodeStateManager = tenantAccessor.getFlowNodeStateManager();
        final GetHumanTaskInstance getHumanTaskInstance = new GetHumanTaskInstance(activityInstanceService,
                activityInstanceId);
        try {
            getHumanTaskInstance.execute();
        } catch (final SActivityInstanceNotFoundException e) {
            throw new ActivityInstanceNotFoundException(activityInstanceId, e);
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
        return ModelConvertor.toHumanTaskInstance(getHumanTaskInstance.getResult(), flowNodeStateManager);
    }

    @Override
    public long getNumberOfAssignedHumanTaskInstances(final long userId) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();

        try {
            final TransactionContentWithResult<Long> transactionContent = new GetNumberOfAssignedUserTaskInstances(
                    userId, activityInstanceService);
            transactionContent.execute();
            return transactionContent.getResult();
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
    }

    @Override
    public Map<Long, Long> getNumberOfOpenTasks(final List<Long> userIds) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();

        try {
            final GetNumberOfOpenTasksForUsers transactionContent = new GetNumberOfOpenTasksForUsers(userIds,
                    activityInstanceService);
            transactionContent.execute();
            return transactionContent.getResult();
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
    }

    @Override
    public Map<String, byte[]> getProcessResources(final long processDefinitionId, final String filenamesPattern)
            throws RetrieveException {
        String processesFolder;
        try {
            processesFolder = BonitaHomeServer.getInstance().getProcessesFolder(getTenantAccessor().getTenantId());
        } catch (final BonitaHomeNotSetException e) {
            throw new RetrieveException("Problem accessing basic Bonita Home server resources", e);
        }
        final String sep = File.separator;
        processesFolder = StringUtils.uniformizePathPattern(processesFolder);
        if (!processesFolder.endsWith(sep)) {
            processesFolder = processesFolder + sep;
        }
        processesFolder = processesFolder + processDefinitionId + sep;
        final File processDirectory = new File(processesFolder);
        final Collection<File> files = FileUtils.listFiles(processDirectory,
                new DeepRegexFileFilter(processDirectory, filenamesPattern), DirectoryFileFilter.DIRECTORY);
        final Map<String, byte[]> res = new HashMap<String, byte[]>(files.size());
        try {
            for (final File f : files) {
                res.put(generateRelativeResourcePath(processDirectory, f), IOUtil.getAllContentFrom(f));
            }
        } catch (final IOException e) {
            throw new RetrieveException("Problem accessing resources " + filenamesPattern
                    + " for processDefinitionId: " + processDefinitionId, e);
        }
        return res;
    }

    protected String generateRelativeResourcePath(final File processDirectory, final File f) {
        String path = StringUtils
                .uniformizePathPattern(f.getAbsolutePath().replace(processDirectory.getAbsolutePath(), ""));
        // remove first slash, if any:
        if (path.startsWith("/")) {
            path = path.substring(1);
        }
        return path;
    }

    @Override
    public long getLatestProcessDefinitionId(final String processName) throws ProcessDefinitionNotFoundException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();

        final TransactionContentWithResult<Long> transactionContent = new GetLatestProcessDefinitionId(
                processDefinitionService, processName);
        try {
            transactionContent.execute();
        } catch (final SBonitaException e) {
            throw new ProcessDefinitionNotFoundException(e);
        }
        return transactionContent.getResult();
    }

    @Override
    public List<DataInstance> getProcessDataInstances(final long processInstanceId, final int startIndex,
            final int maxResults) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final DataInstanceService dataInstanceService = tenantAccessor.getDataInstanceService();
        final ClassLoaderService classLoaderService = tenantAccessor.getClassLoaderService();
        final ProcessInstanceService processInstanceService = tenantAccessor.getProcessInstanceService();
        final ParentContainerResolver parentContainerResolver = tenantAccessor.getParentContainerResolver();
        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        try {
            final long processDefinitionId = processInstanceService.getProcessInstance(processInstanceId)
                    .getProcessDefinitionId();
            final ClassLoader processClassLoader = classLoaderService.getLocalClassLoader(ScopeType.PROCESS.name(),
                    processDefinitionId);
            Thread.currentThread().setContextClassLoader(processClassLoader);
            final List<SDataInstance> dataInstances = dataInstanceService.getDataInstances(processInstanceId,
                    DataInstanceContainer.PROCESS_INSTANCE.name(), parentContainerResolver, startIndex, maxResults);
            return convertModelToDataInstances(dataInstances);
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        } finally {
            Thread.currentThread().setContextClassLoader(contextClassLoader);
        }
    }

    @Override
    public DataInstance getProcessDataInstance(final String dataName, final long processInstanceId)
            throws DataNotFoundException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final DataInstanceService dataInstanceService = tenantAccessor.getDataInstanceService();
        final ClassLoaderService classLoaderService = tenantAccessor.getClassLoaderService();
        final ProcessInstanceService processInstanceService = tenantAccessor.getProcessInstanceService();
        final ParentContainerResolver parentContainerResolver = tenantAccessor.getParentContainerResolver();
        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        try {
            final long processDefinitionId = processInstanceService.getProcessInstance(processInstanceId)
                    .getProcessDefinitionId();
            final ClassLoader processClassLoader = classLoaderService.getLocalClassLoader(ScopeType.PROCESS.name(),
                    processDefinitionId);
            Thread.currentThread().setContextClassLoader(processClassLoader);
            final SDataInstance sDataInstance = dataInstanceService.getDataInstance(dataName, processInstanceId,
                    DataInstanceContainer.PROCESS_INSTANCE.toString(), parentContainerResolver);
            return convertModeltoDataInstance(sDataInstance);
        } catch (final SBonitaException e) {
            throw new DataNotFoundException(e);
        } finally {
            Thread.currentThread().setContextClassLoader(contextClassLoader);
        }
    }

    @Override
    public void updateProcessDataInstance(final String dataName, final long processInstanceId,
            final Serializable dataValue) throws UpdateException {
        updateProcessDataInstances(processInstanceId, singletonMap(dataName, dataValue));
    }

    @Override
    public void updateProcessDataInstances(final long processInstanceId,
            final Map<String, Serializable> dataNameValues) throws UpdateException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final DataInstanceService dataInstanceService = tenantAccessor.getDataInstanceService();
        final ParentContainerResolver parentContainerResolver = tenantAccessor.getParentContainerResolver();
        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        try {
            final ClassLoader processClassLoader = getProcessInstanceClassloader(tenantAccessor, processInstanceId);
            Thread.currentThread().setContextClassLoader(processClassLoader);
            final List<String> dataNames = new ArrayList<String>(dataNameValues.keySet());
            final List<SDataInstance> sDataInstances = dataInstanceService.getDataInstances(dataNames,
                    processInstanceId, DataInstanceContainer.PROCESS_INSTANCE.toString(), parentContainerResolver);
            updateDataInstances(sDataInstances, dataNameValues);
        } catch (final SBonitaException e) {
            throw new UpdateException(e);
        } catch (final ClassNotFoundException e) {
            throw new UpdateException(e);
        } finally {
            Thread.currentThread().setContextClassLoader(contextClassLoader);
        }
    }

    private void updateDataInstances(final List<SDataInstance> sDataInstances,
            final Map<String, Serializable> dataNameValues)
            throws ClassNotFoundException, UpdateException, SDataInstanceException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final DataInstanceService dataInstanceService = tenantAccessor.getDataInstanceService();
        for (final SDataInstance sDataInstance : sDataInstances) {
            final Serializable dataValue = dataNameValues.get(sDataInstance.getName());
            updateDataInstance(dataInstanceService, sDataInstance, dataValue);
        }
    }

    private void updateDataInstance(final DataInstanceService dataInstanceService,
            final SDataInstance sDataInstance, final Serializable dataNewValue)
            throws UpdateException, SDataInstanceException {
        verifyTypeOfNewDataValue(sDataInstance, dataNewValue);

        final EntityUpdateDescriptor entityUpdateDescriptor = buildEntityUpdateDescriptorForData(dataNewValue);
        dataInstanceService.updateDataInstance(sDataInstance, entityUpdateDescriptor);
    }

    private void verifyTypeOfNewDataValue(final SDataInstance sDataInstance, final Serializable dataValue)
            throws UpdateException {
        final String dataClassName = sDataInstance.getClassName();
        Class<?> dataClass;
        try {
            dataClass = Class.forName(dataClassName);
        } catch (final ClassNotFoundException e) {
            throw new UpdateException(e);
        }
        if (!dataClass.isInstance(dataValue)) {
            final UpdateException e = new UpdateException("The type of new value [" + dataValue.getClass().getName()
                    + "] is not compatible with the type of the data.");
            e.setDataName(sDataInstance.getName());
            e.setDataClassName(dataClassName);
            throw e;
        }
    }

    private EntityUpdateDescriptor buildEntityUpdateDescriptorForData(final Serializable dataValue) {
        final EntityUpdateDescriptor entityUpdateDescriptor = new EntityUpdateDescriptor();
        entityUpdateDescriptor.addField("value", dataValue);
        return entityUpdateDescriptor;
    }

    /**
     * @param tenantAccessor
     * @param processInstanceId
     * @return
     * @throws SProcessInstanceNotFoundException
     * @throws SProcessInstanceReadException
     * @throws SClassLoaderException
     */
    protected ClassLoader getProcessInstanceClassloader(final TenantServiceAccessor tenantAccessor,
            final long processInstanceId)
            throws SProcessInstanceNotFoundException, SProcessInstanceReadException, SClassLoaderException {
        final ClassLoaderService classLoaderService = tenantAccessor.getClassLoaderService();
        final ProcessInstanceService processInstanceService = tenantAccessor.getProcessInstanceService();
        final long processDefinitionId = processInstanceService.getProcessInstance(processInstanceId)
                .getProcessDefinitionId();
        return classLoaderService.getLocalClassLoader(ScopeType.PROCESS.name(), processDefinitionId);
    }

    @Override
    public List<DataInstance> getActivityDataInstances(final long activityInstanceId, final int startIndex,
            final int maxResults) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final DataInstanceService dataInstanceService = tenantAccessor.getDataInstanceService();
        final ClassLoaderService classLoaderService = tenantAccessor.getClassLoaderService();
        final ParentContainerResolver parentContainerResolver = tenantAccessor.getParentContainerResolver();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        final int processDefinitionIndex = BuilderFactory.get(SAutomaticTaskInstanceBuilderFactory.class)
                .getProcessDefinitionIndex();
        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        try {
            final long parentProcessInstanceId = activityInstanceService.getFlowNodeInstance(activityInstanceId)
                    .getLogicalGroup(processDefinitionIndex);
            final ClassLoader processClassLoader = classLoaderService.getLocalClassLoader(ScopeType.PROCESS.name(),
                    parentProcessInstanceId);
            Thread.currentThread().setContextClassLoader(processClassLoader);
            final List<SDataInstance> dataInstances = dataInstanceService.getDataInstances(activityInstanceId,
                    DataInstanceContainer.ACTIVITY_INSTANCE.name(), parentContainerResolver, startIndex,
                    maxResults);
            return convertModelToDataInstances(dataInstances);
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        } finally {
            Thread.currentThread().setContextClassLoader(contextClassLoader);
        }
    }

    @Override
    public DataInstance getActivityDataInstance(final String dataName, final long activityInstanceId)
            throws DataNotFoundException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final DataInstanceService dataInstanceService = tenantAccessor.getDataInstanceService();
        final ClassLoaderService classLoaderService = tenantAccessor.getClassLoaderService();
        final ParentContainerResolver parentContainerResolver = tenantAccessor.getParentContainerResolver();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        final int processDefinitionIndex = BuilderFactory.get(SAutomaticTaskInstanceBuilderFactory.class)
                .getProcessDefinitionIndex();
        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        try {
            final SFlowNodeInstance flowNodeInstance = activityInstanceService
                    .getFlowNodeInstance(activityInstanceId);
            SDataInstance data;
            final long parentProcessInstanceId = flowNodeInstance.getLogicalGroup(processDefinitionIndex);
            final ClassLoader processClassLoader = classLoaderService.getLocalClassLoader(ScopeType.PROCESS.name(),
                    parentProcessInstanceId);
            Thread.currentThread().setContextClassLoader(processClassLoader);
            data = dataInstanceService.getDataInstance(dataName, activityInstanceId,
                    DataInstanceContainer.ACTIVITY_INSTANCE.toString(), parentContainerResolver);
            return convertModeltoDataInstance(data);
        } catch (final SBonitaException e) {
            throw new DataNotFoundException(e);
        } finally {
            Thread.currentThread().setContextClassLoader(contextClassLoader);
        }
    }

    @Override
    public void updateActivityDataInstance(final String dataName, final long activityInstanceId,
            final Serializable dataValue) throws UpdateException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final DataInstanceService dataInstanceService = tenantAccessor.getDataInstanceService();
        final ClassLoaderService classLoaderService = tenantAccessor.getClassLoaderService();
        final ParentContainerResolver parentContainerResolver = tenantAccessor.getParentContainerResolver();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        final int processDefinitionIndex = BuilderFactory.get(SAutomaticTaskInstanceBuilderFactory.class)
                .getProcessDefinitionIndex();
        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        try {
            final SFlowNodeInstance flowNodeInstance = activityInstanceService
                    .getFlowNodeInstance(activityInstanceId);
            final long parentProcessInstanceId = flowNodeInstance.getLogicalGroup(processDefinitionIndex);
            final ClassLoader processClassLoader = classLoaderService.getLocalClassLoader(ScopeType.PROCESS.name(),
                    parentProcessInstanceId);
            Thread.currentThread().setContextClassLoader(processClassLoader);
            final SDataInstance sDataInstance = dataInstanceService.getDataInstance(dataName, activityInstanceId,
                    DataInstanceContainer.ACTIVITY_INSTANCE.toString(), parentContainerResolver);
            updateDataInstance(dataInstanceService, sDataInstance, dataValue);
        } catch (final SBonitaException e) {
            throw new UpdateException(e);
        } finally {
            Thread.currentThread().setContextClassLoader(contextClassLoader);
        }
    }

    @Override
    public DataInstance getActivityTransientDataInstance(final String dataName, final long activityInstanceId)
            throws DataNotFoundException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final TransientDataService transientDataService = tenantAccessor.getTransientDataService();
        final ClassLoaderService classLoaderService = tenantAccessor.getClassLoaderService();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        final int processDefinitionIndex = BuilderFactory.get(SAutomaticTaskInstanceBuilderFactory.class)
                .getProcessDefinitionIndex();
        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        try {
            final SFlowNodeInstance flowNodeInstance = activityInstanceService
                    .getFlowNodeInstance(activityInstanceId);
            SDataInstance data;
            final long parentProcessInstanceId = flowNodeInstance.getLogicalGroup(processDefinitionIndex);
            final ClassLoader processClassLoader = classLoaderService.getLocalClassLoader(ScopeType.PROCESS.name(),
                    parentProcessInstanceId);
            Thread.currentThread().setContextClassLoader(processClassLoader);
            data = transientDataService.getDataInstance(dataName, activityInstanceId,
                    DataInstanceContainer.ACTIVITY_INSTANCE.toString());
            return convertModeltoDataInstance(data);
        } catch (final SBonitaException e) {
            throw new DataNotFoundException(e);
        } finally {
            Thread.currentThread().setContextClassLoader(contextClassLoader);
        }
    }

    /**
     * isolate static call for mocking reasons
     */
    protected DataInstance convertModeltoDataInstance(final SDataInstance data) {
        return ModelConvertor.toDataInstance(data);
    }

    @Override
    public List<DataInstance> getActivityTransientDataInstances(final long activityInstanceId, final int startIndex,
            final int maxResults) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final TransientDataService transientDataService = tenantAccessor.getTransientDataService();
        final ClassLoaderService classLoaderService = tenantAccessor.getClassLoaderService();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        final int processDefinitionIndex = BuilderFactory.get(SAutomaticTaskInstanceBuilderFactory.class)
                .getProcessDefinitionIndex();
        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        try {
            final long parentProcessInstanceId = activityInstanceService.getFlowNodeInstance(activityInstanceId)
                    .getLogicalGroup(processDefinitionIndex);
            final ClassLoader processClassLoader = classLoaderService.getLocalClassLoader(ScopeType.PROCESS.name(),
                    parentProcessInstanceId);
            Thread.currentThread().setContextClassLoader(processClassLoader);
            final List<SDataInstance> dataInstances = transientDataService.getDataInstances(activityInstanceId,
                    DataInstanceContainer.ACTIVITY_INSTANCE.name(), startIndex, maxResults);
            return convertModelToDataInstances(dataInstances);
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        } finally {
            Thread.currentThread().setContextClassLoader(contextClassLoader);
        }
    }

    /**
     * isolate static call for mocking reasons
     */
    protected List<DataInstance> convertModelToDataInstances(final List<SDataInstance> dataInstances) {
        return ModelConvertor.toDataInstances(dataInstances);
    }

    @Override
    public void updateActivityTransientDataInstance(final String dataName, final long activityInstanceId,
            final Serializable dataValue) throws UpdateException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final TransientDataService transientDataService = tenantAccessor.getTransientDataService();
        final ClassLoaderService classLoaderService = tenantAccessor.getClassLoaderService();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        final int processDefinitionIndex = BuilderFactory.get(SAutomaticTaskInstanceBuilderFactory.class)
                .getProcessDefinitionIndex();
        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        try {
            final SFlowNodeInstance flowNodeInstance = activityInstanceService
                    .getFlowNodeInstance(activityInstanceId);
            final long parentProcessInstanceId = flowNodeInstance.getLogicalGroup(processDefinitionIndex);
            final ClassLoader processClassLoader = classLoaderService.getLocalClassLoader(ScopeType.PROCESS.name(),
                    parentProcessInstanceId);
            Thread.currentThread().setContextClassLoader(processClassLoader);
            updateTransientData(dataName, activityInstanceId, dataValue, transientDataService);
        } catch (final SBonitaException e) {
            throw new UpdateException(e);
        } finally {
            Thread.currentThread().setContextClassLoader(contextClassLoader);
        }

    }

    protected void updateTransientData(final String dataName, final long activityInstanceId,
            final Serializable dataValue, final TransientDataService transientDataInstanceService)
            throws SDataInstanceException, UpdateException {
        final SDataInstance sDataInstance = transientDataInstanceService.getDataInstance(dataName,
                activityInstanceId, DataInstanceContainer.ACTIVITY_INSTANCE.toString());
        verifyTypeOfNewDataValue(sDataInstance, dataValue);
        final EntityUpdateDescriptor entityUpdateDescriptor = buildEntityUpdateDescriptorForData(dataValue);
        transientDataInstanceService.updateDataInstance(sDataInstance, entityUpdateDescriptor);
    }

    @Override
    public void importActorMapping(final long processDefinitionId, final String xmlContent)
            throws ActorMappingImportException {
        if (xmlContent != null) {
            final TenantServiceAccessor tenantAccessor = getTenantAccessor();
            final ActorMappingService actorMappingService = tenantAccessor.getActorMappingService();
            final IdentityService identityService = tenantAccessor.getIdentityService();
            final Parser parser = tenantAccessor.getActorMappingParser();
            try {
                new ImportActorMapping(actorMappingService, identityService, parser, processDefinitionId,
                        xmlContent).execute();
                tenantAccessor.getDependencyResolver().resolveDependencies(processDefinitionId, tenantAccessor);
            } catch (final SBonitaException sbe) {
                throw new ActorMappingImportException(sbe);
            }
        }
    }

    @Override
    public String exportActorMapping(final long processDefinitionId) throws ActorMappingExportException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ActorMappingService actorMappingService = tenantAccessor.getActorMappingService();
        final IdentityService identityService = tenantAccessor.getIdentityService();
        final XMLWriter writer = tenantAccessor.getXMLWriter();
        try {
            final ExportActorMapping exportActorMapping = new ExportActorMapping(actorMappingService,
                    identityService, writer, processDefinitionId);
            exportActorMapping.execute();
            return exportActorMapping.getResult();
        } catch (final SBonitaException sbe) {
            throw new ActorMappingExportException(sbe);
        }
    }

    @Override
    public boolean isInvolvedInProcessInstance(final long userId, final long processInstanceId)
            throws ProcessInstanceNotFoundException {
        return new ProcessInvolvementAPIImpl(this).isInvolvedInProcessInstance(userId, processInstanceId);
    }

    public boolean isInvolvedInHumanTaskInstance(long userId, long humanTaskInstanceId)
            throws ActivityInstanceNotFoundException, UserNotFoundException {
        return new ProcessInvolvementAPIImpl(this).isInvolvedInHumanTaskInstance(userId, humanTaskInstanceId);
    }

    @Override
    public boolean isManagerOfUserInvolvedInProcessInstance(final long managerUserId, final long processInstanceId)
            throws ProcessInstanceNotFoundException, BonitaException {
        return new ProcessInvolvementAPIImpl(this).isManagerOfUserInvolvedInProcessInstance(managerUserId,
                processInstanceId);
    }

    @Override
    public long getProcessInstanceIdFromActivityInstanceId(final long activityInstanceId)
            throws ProcessInstanceNotFoundException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        try {
            final SActivityInstance sActivityInstance = getSActivityInstance(activityInstanceId);
            return sActivityInstance.getRootContainerId();
        } catch (final SBonitaException e) {
            log(tenantAccessor, e);
            throw new ProcessInstanceNotFoundException(e.getMessage());
        }
    }

    @Override
    public long getProcessDefinitionIdFromActivityInstanceId(final long activityInstanceId)
            throws ProcessDefinitionNotFoundException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessInstanceService processInstanceService = tenantAccessor.getProcessInstanceService();
        try {
            final SActivityInstance sActivityInstance = getSActivityInstance(activityInstanceId);
            return processInstanceService.getProcessInstance(sActivityInstance.getParentProcessInstanceId())
                    .getProcessDefinitionId();
        } catch (final SBonitaException e) {
            throw new ProcessDefinitionNotFoundException(e);
        }
    }

    @Override
    public long getProcessDefinitionIdFromProcessInstanceId(final long processInstanceId)
            throws ProcessDefinitionNotFoundException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();
        try {
            final SProcessInstance sProcessInstance = getSProcessInstance(processInstanceId);
            final ProcessInstance processInstance = ModelConvertor
                    .toProcessInstances(Collections.singletonList(sProcessInstance), processDefinitionService)
                    .get(0);
            return processInstance.getProcessDefinitionId();
        } catch (final SBonitaException e) {
            log(tenantAccessor, e);
            throw new ProcessDefinitionNotFoundException(e.getMessage());
        }
    }

    @Override
    public Date getActivityReachedStateDate(final long activityInstanceId, final String stateName) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();

        final int stateId = ModelConvertor.getServerActivityStateId(stateName);
        final GetArchivedActivityInstance getArchivedActivityInstance = new GetArchivedActivityInstance(
                activityInstanceId, stateId, activityInstanceService);
        try {
            getArchivedActivityInstance.execute();
            final long reachedDate = getArchivedActivityInstance.getResult().getReachedStateDate();
            return new Date(reachedDate);
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
    }

    @Override
    public Set<String> getSupportedStates(final FlowNodeType nodeType) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final FlowNodeStateManager flowNodeStateManager = tenantAccessor.getFlowNodeStateManager();
        return flowNodeStateManager.getSupportedState(nodeType);
    }

    @Override
    public void updateActivityInstanceVariables(final long activityInstanceId,
            final Map<String, Serializable> variables) throws UpdateException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final DataInstanceService dataInstanceService = tenantAccessor.getDataInstanceService();
        final ClassLoaderService classLoaderService = tenantAccessor.getClassLoaderService();
        final ParentContainerResolver parentContainerResolver = tenantAccessor.getParentContainerResolver();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        final int processDefinitionIndex = BuilderFactory.get(SAutomaticTaskInstanceBuilderFactory.class)
                .getProcessDefinitionIndex();
        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        try {
            final long parentProcessInstanceId = activityInstanceService.getFlowNodeInstance(activityInstanceId)
                    .getLogicalGroup(processDefinitionIndex);
            final ClassLoader processClassLoader = classLoaderService.getLocalClassLoader(ScopeType.PROCESS.name(),
                    parentProcessInstanceId);
            Thread.currentThread().setContextClassLoader(processClassLoader);
            final List<SDataInstance> dataInstances = dataInstanceService.getDataInstances(
                    new ArrayList<String>(variables.keySet()), activityInstanceId,
                    DataInstanceContainer.ACTIVITY_INSTANCE.toString(), parentContainerResolver);
            if (dataInstances.size() < variables.size()) {
                throw new UpdateException("Some data does not exists, wanted to update " + variables.keySet()
                        + " but there is only " + dataInstances);
            }
            for (final SDataInstance dataInstance : dataInstances) {
                final Serializable newValue = variables.get(dataInstance.getName());
                final EntityUpdateDescriptor entityUpdateDescriptor = buildEntityUpdateDescriptorForData(newValue);
                dataInstanceService.updateDataInstance(dataInstance, entityUpdateDescriptor);
            }
        } catch (final SBonitaException e) {
            throw new UpdateException(e);
        } finally {
            Thread.currentThread().setContextClassLoader(contextClassLoader);
        }
    }

    @Override
    public void updateActivityInstanceVariables(final List<Operation> operations, final long activityInstanceId,
            final Map<String, Serializable> expressionContexts) throws UpdateException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final OperationService operationService = tenantAccessor.getOperationService();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        final ClassLoaderService classLoaderService = tenantAccessor.getClassLoaderService();
        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        try {
            final SActivityInstance activityInstance = activityInstanceService
                    .getActivityInstance(activityInstanceId);
            final ClassLoader processClassLoader = classLoaderService.getLocalClassLoader(ScopeType.PROCESS.name(),
                    activityInstance.getProcessDefinitionId());
            Thread.currentThread().setContextClassLoader(processClassLoader);
            final List<SOperation> sOperations = convertOperations(operations);
            final SExpressionContext sExpressionContext = new SExpressionContext(activityInstanceId,
                    DataInstanceContainer.ACTIVITY_INSTANCE.toString(), activityInstance.getProcessDefinitionId());
            sExpressionContext.setSerializableInputValues(expressionContexts);

            operationService.execute(sOperations, sExpressionContext);
        } catch (final SBonitaException e) {
            throw new UpdateException(e);
        } finally {
            Thread.currentThread().setContextClassLoader(contextClassLoader);
        }
    }

    protected SOperation convertOperation(final Operation operation) {
        return ModelConvertor.convertOperation(operation);
    }

    protected List<SOperation> convertOperations(final List<Operation> operations) {
        return ModelConvertor.convertOperations(operations);
    }

    @Override
    public long getOneAssignedUserTaskInstanceOfProcessInstance(final long processInstanceId, final long userId)
            throws RetrieveException {
        // FIXME: write specific query that should be more efficient:
        final int assignedUserTaskInstanceNumber = (int) getNumberOfAssignedHumanTaskInstances(userId);
        final List<HumanTaskInstance> userTaskInstances = getAssignedHumanTaskInstances(userId, 0,
                assignedUserTaskInstanceNumber, ActivityInstanceCriterion.DEFAULT);
        for (final HumanTaskInstance userTaskInstance : userTaskInstances) {
            final String stateName = userTaskInstance.getState();
            final long userTaskInstanceId = userTaskInstance.getId();
            if (stateName.equals(ActivityStates.READY_STATE)
                    && userTaskInstance.getParentContainerId() == processInstanceId) {
                return userTaskInstanceId;
            }
        }
        return -1;
    }

    @Override
    public long getOneAssignedUserTaskInstanceOfProcessDefinition(final long processDefinitionId,
            final long userId) {
        final int assignedUserTaskInstanceNumber = (int) getNumberOfAssignedHumanTaskInstances(userId);
        final List<HumanTaskInstance> userTaskInstances = getAssignedHumanTaskInstances(userId, 0,
                assignedUserTaskInstanceNumber, ActivityInstanceCriterion.DEFAULT);
        if (!userTaskInstances.isEmpty()) {
            for (final HumanTaskInstance userTaskInstance : userTaskInstances) {
                final String stateName = userTaskInstance.getState();
                try {
                    final SProcessInstance sProcessInstance = getSProcessInstance(
                            userTaskInstance.getRootContainerId());
                    if (stateName.equals(ActivityStates.READY_STATE)
                            && sProcessInstance.getProcessDefinitionId() == processDefinitionId) {
                        return userTaskInstance.getId();
                    }
                } catch (final SBonitaException e) {
                    throw new RetrieveException(e);
                }
            }
        }
        return -1;
    }

    @Override
    public String getActivityInstanceState(final long activityInstanceId) throws ActivityInstanceNotFoundException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final FlowNodeStateManager flowNodeStateManager = tenantAccessor.getFlowNodeStateManager();
        try {
            final SActivityInstance sActivityInstance = getSActivityInstance(activityInstanceId);
            final ActivityInstance activityInstance = ModelConvertor.toActivityInstance(sActivityInstance,
                    flowNodeStateManager);
            return activityInstance.getState();
        } catch (final SBonitaException e) {
            log(tenantAccessor, e);
            throw new ActivityInstanceNotFoundException(activityInstanceId);
        }
    }

    @Override
    public boolean canExecuteTask(final long activityInstanceId, final long userId)
            throws ActivityInstanceNotFoundException, RetrieveException {
        final HumanTaskInstance userTaskInstance = getHumanTaskInstance(activityInstanceId);
        return userTaskInstance.getState().equalsIgnoreCase(ActivityStates.READY_STATE)
                && userTaskInstance.getAssigneeId() == userId;
    }

    @Override
    public long getProcessDefinitionId(final String name, final String version)
            throws ProcessDefinitionNotFoundException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();

        final TransactionContentWithResult<Long> transactionContent = new GetProcessDefinitionIDByNameAndVersion(
                processDefinitionService, name, version);
        try {
            transactionContent.execute();
        } catch (final SBonitaException e) {
            throw new ProcessDefinitionNotFoundException(e);
        }
        return transactionContent.getResult();
    }

    @Override
    public void releaseUserTask(final long userTaskId) throws ActivityInstanceNotFoundException, UpdateException {
        final TenantServiceAccessor tenantAccessor;
        tenantAccessor = getTenantAccessor();

        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        try {
            final AssignOrUnassignUserTask assignUserTask = new AssignOrUnassignUserTask(0, userTaskId,
                    activityInstanceService, tenantAccessor.getFlowNodeStateManager().getStateBehaviors());
            assignUserTask.execute();
        } catch (final SUnreleasableTaskException e) {
            throw new UpdateException(e);
        } catch (final SActivityInstanceNotFoundException e) {
            throw new ActivityInstanceNotFoundException(e);
        } catch (final SBonitaException e) {
            log(tenantAccessor, e);
            throw new UpdateException(e);
        }
    }

    @Override
    public void updateProcessDeploymentInfo(final long processDefinitionId,
            final ProcessDeploymentInfoUpdater processDeploymentInfoUpdater)
            throws ProcessDefinitionNotFoundException, UpdateException {
        if (processDeploymentInfoUpdater == null || processDeploymentInfoUpdater.getFields().isEmpty()) {
            throw new UpdateException("The update descriptor does not contain field updates");
        }
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();

        final UpdateProcessDeploymentInfo updateProcessDeploymentInfo = new UpdateProcessDeploymentInfo(
                processDefinitionService, processDefinitionId, processDeploymentInfoUpdater);
        try {
            updateProcessDeploymentInfo.execute();
        } catch (final SProcessDefinitionNotFoundException e) {
            throw new ProcessDefinitionNotFoundException(e);
        } catch (final SBonitaException sbe) {
            throw new UpdateException(sbe);
        }
    }

    @Override
    public List<ProcessDeploymentInfo> getStartableProcessDeploymentInfosForActors(final Set<Long> actorIds,
            final int startIndex, final int maxResults, final ProcessDeploymentInfoCriterion sortingCriterion) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();
        final SProcessDefinitionDeployInfoBuilderFactory builder = BuilderFactory
                .get(SProcessDefinitionDeployInfoBuilderFactory.class);

        try {
            final Set<Long> processDefIds = getIdOfStartableProcessDeploymentInfosForActors(actorIds);

            String field = null;
            OrderByType order = null;
            switch (sortingCriterion) {
            case DEFAULT:
                break;
            case LABEL_ASC:
                // field = processDefinitionDeployInfoKyeProvider.get
                // FIXME add label?
                break;
            case LABEL_DESC:
                break;
            case NAME_ASC:
                field = builder.getNameKey();
                order = OrderByType.ASC;
                break;
            case NAME_DESC:
                field = builder.getNameKey();
                order = OrderByType.DESC;
                break;
            case ACTIVATION_STATE_ASC:
                field = builder.getActivationStateKey();
                order = OrderByType.ASC;
                break;
            case ACTIVATION_STATE_DESC:
                field = builder.getActivationStateKey();
                order = OrderByType.DESC;
                break;
            case CONFIGURATION_STATE_ASC:
                field = builder.getConfigurationStateKey();
                order = OrderByType.ASC;
                break;
            case CONFIGURATION_STATE_DESC:
                field = builder.getConfigurationStateKey();
                order = OrderByType.DESC;
                break;
            case VERSION_ASC:
                field = builder.getVersionKey();
                order = OrderByType.ASC;
                break;
            case VERSION_DESC:
                field = builder.getVersionKey();
                order = OrderByType.DESC;
                break;
            default:
                break;
            }

            final List<SProcessDefinitionDeployInfo> processDefinitionDeployInfos = processDefinitionService
                    .getProcessDeploymentInfos(new ArrayList<Long>(processDefIds), startIndex, maxResults, field,
                            order);
            return ModelConvertor.toProcessDeploymentInfo(processDefinitionDeployInfos);
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
    }

    private Set<Long> getIdOfStartableProcessDeploymentInfosForActors(final Set<Long> actorIds)
            throws SActorNotFoundException, SBonitaReadException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ActorMappingService actorMappingService = tenantAccessor.getActorMappingService();

        final List<SActor> actors = actorMappingService.getActors(new ArrayList<Long>(actorIds));
        final Set<Long> processDefIds = new HashSet<Long>(actors.size());
        for (final SActor sActor : actors) {
            if (sActor.isInitiator()) {
                processDefIds.add(sActor.getScopeId());
            }
        }
        return processDefIds;
    }

    @Override
    public boolean isAllowedToStartProcess(final long processDefinitionId, final Set<Long> actorIds) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ActorMappingService actorMappingService = tenantAccessor.getActorMappingService();

        final GetActorsByActorIds getActorsByActorIds = new GetActorsByActorIds(actorMappingService,
                new ArrayList<Long>(actorIds));
        try {
            getActorsByActorIds.execute();
            final List<SActor> actors = getActorsByActorIds.getResult();
            boolean isAllowedToStartProcess = true;
            final Iterator<SActor> iterator = actors.iterator();
            while (isAllowedToStartProcess && iterator.hasNext()) {
                final SActor actor = iterator.next();
                if (actor.getScopeId() != processDefinitionId || !actor.isInitiator()) {
                    isAllowedToStartProcess = false;
                }
            }
            return isAllowedToStartProcess;
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
    }

    @Override
    public ActorInstance getActorInitiator(final long processDefinitionId)
            throws ActorNotFoundException, ProcessDefinitionNotFoundException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();
        final ActorMappingService actorMappingService = tenantAccessor.getActorMappingService();
        ActorInstance actorInstance = null;
        try {
            final SProcessDefinition definition = processDefinitionService
                    .getProcessDefinition(processDefinitionId);
            final SActorDefinition sActorDefinition = definition.getActorInitiator();
            if (sActorDefinition == null) {
                throw new ActorNotFoundException("No actor initiator defined on the process");
            }
            final String name = sActorDefinition.getName();
            final SActor sActor = actorMappingService.getActor(name, processDefinitionId);
            actorInstance = ModelConvertor.toActorInstance(sActor);
        } catch (final SProcessDefinitionNotFoundException e) {
            // no rollback need, we only read
            throw new ProcessDefinitionNotFoundException(e);
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
        return actorInstance;
    }

    @Override
    public int getNumberOfActivityDataDefinitions(final long processDefinitionId, final String activityName)
            throws ProcessDefinitionNotFoundException, ActivityDefinitionNotFoundException {
        List<SDataDefinition> sdataDefinitionList = Collections.emptyList();
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();
        final GetProcessDefinition getProcessDefinition = new GetProcessDefinition(processDefinitionId,
                processDefinitionService);
        try {
            getProcessDefinition.execute();
        } catch (final SProcessDefinitionNotFoundException e) {
            throw new ProcessDefinitionNotFoundException(e);
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
        final SProcessDefinition sProcessDefinition = getProcessDefinition.getResult();
        if (sProcessDefinition != null) {
            boolean found = false;
            final SFlowElementContainerDefinition processContainer = sProcessDefinition.getProcessContainer();
            final Set<SActivityDefinition> activityDefList = processContainer.getActivities();
            for (final SActivityDefinition sActivityDefinition : activityDefList) {
                if (activityName.equals(sActivityDefinition.getName())) {
                    sdataDefinitionList = sActivityDefinition.getSDataDefinitions();
                    found = true;
                    break;
                }
            }
            if (!found) {
                throw new ActivityDefinitionNotFoundException(activityName);
            }
            return sdataDefinitionList.size();
        }
        return 0;
    }

    @Override
    public int getNumberOfProcessDataDefinitions(final long processDefinitionId)
            throws ProcessDefinitionNotFoundException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();

        final GetProcessDefinition getProcessDefinition = new GetProcessDefinition(processDefinitionId,
                processDefinitionService);
        try {
            getProcessDefinition.execute();
        } catch (final SProcessDefinitionNotFoundException e) {
            throw new ProcessDefinitionNotFoundException(e);
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
        final SProcessDefinition sProcessDefinition = getProcessDefinition.getResult();
        final SFlowElementContainerDefinition processContainer = sProcessDefinition.getProcessContainer();
        return processContainer.getDataDefinitions().size();
    }

    @Override
    public ProcessInstance startProcess(final long processDefinitionId,
            final Map<String, Serializable> initialVariables)
            throws ProcessDefinitionNotFoundException, ProcessActivationException, ProcessExecutionException {
        final List<Operation> operations = createSetDataOperation(processDefinitionId, initialVariables);
        return startProcess(processDefinitionId, operations, initialVariables);
    }

    @Override
    public ProcessInstance startProcessWithInputs(final long processDefinitionId,
            final Map<String, Serializable> instantiationInputs) throws ProcessDefinitionNotFoundException,
            ProcessActivationException, ProcessExecutionException, ContractViolationException {
        throwContractViolationExceptionIfProcessContractIsInvalid(instantiationInputs, processDefinitionId);
        return startProcessWithInputs(0, processDefinitionId, instantiationInputs);
    }

    @Override
    public ProcessInstance startProcessWithInputs(final long userId, final long processDefinitionId,
            final Map<String, Serializable> instantiationInputs) throws ProcessDefinitionNotFoundException,
            ProcessActivationException, ProcessExecutionException, ContractViolationException {
        return new ProcessStarter(userId, processDefinitionId, instantiationInputs).start();
    }

    private void throwContractViolationExceptionIfProcessContractIsInvalid(final Map<String, Serializable> inputs,
            long processDefinitionId) throws ContractViolationException, ProcessDefinitionNotFoundException {
        final SContractDefinition contractDefinition;
        try {
            contractDefinition = getTenantAccessor().getProcessDefinitionService()
                    .getProcessDefinition(processDefinitionId).getContract();
        } catch (SProcessDefinitionNotFoundException | SProcessDefinitionReadException e) {
            throw new ProcessDefinitionNotFoundException(processDefinitionId, e);
        }
        final ContractValidator validator = new ContractValidatorFactory()
                .createContractValidator(getTenantAccessor().getTechnicalLoggerService());
        if (!validator.isValid(contractDefinition, inputs)) {
            throw new ContractViolationException("Contract is not valid: ", validator.getComments());
        }
    }

    @Override
    public ProcessInstance startProcess(final long userId, final long processDefinitionId,
            final Map<String, Serializable> initialVariables)
            throws ProcessDefinitionNotFoundException, ProcessActivationException, ProcessExecutionException {
        final List<Operation> operations = createSetDataOperation(processDefinitionId, initialVariables);
        return startProcess(userId, processDefinitionId, operations, initialVariables);
    }

    protected List<Operation> createSetDataOperation(final long processDefinitionId,
            final Map<String, Serializable> initialVariables) throws ProcessExecutionException {
        final ClassLoaderService classLoaderService = getTenantAccessor().getClassLoaderService();
        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        final List<Operation> operations = new ArrayList<Operation>();
        try {
            final ClassLoader localClassLoader = classLoaderService.getLocalClassLoader(ScopeType.PROCESS.name(),
                    processDefinitionId);
            Thread.currentThread().setContextClassLoader(localClassLoader);
            if (initialVariables != null) {
                for (final Entry<String, Serializable> initialVariable : initialVariables.entrySet()) {
                    final String name = initialVariable.getKey();
                    final Serializable value = initialVariable.getValue();
                    final Expression expression = new ExpressionBuilder().createExpression(name, name,
                            value.getClass().getName(), ExpressionType.TYPE_INPUT);
                    final Operation operation = new OperationBuilder().createSetDataOperation(name, expression);
                    operations.add(operation);
                }
            }
        } catch (final SClassLoaderException cle) {
            throw new ProcessExecutionException(cle);
        } catch (final InvalidExpressionException iee) {
            throw new ProcessExecutionException(iee);
        } finally {
            Thread.currentThread().setContextClassLoader(contextClassLoader);
        }
        return operations;
    }

    @Override
    public ProcessInstance startProcess(final long processDefinitionId, final List<Operation> operations,
            final Map<String, Serializable> context)
            throws ProcessExecutionException, ProcessDefinitionNotFoundException, ProcessActivationException {
        try {
            return startProcess(0, processDefinitionId, operations, context);
        } catch (final RetrieveException e) {
            throw new ProcessExecutionException(e);
        }
    }

    @Override
    public ProcessInstance startProcess(final long userId, final long processDefinitionId,
            final List<Operation> operations, final Map<String, Serializable> context)
            throws ProcessDefinitionNotFoundException, ProcessActivationException, ProcessExecutionException {
        final ProcessStarter starter = new ProcessStarter(userId, processDefinitionId, operations, context);
        return starter.start();
    }

    @Override
    public long getNumberOfActivityDataInstances(final long activityInstanceId)
            throws ActivityInstanceNotFoundException {
        try {
            return getNumberOfDataInstancesOfContainer(activityInstanceId, DataInstanceContainer.ACTIVITY_INSTANCE);
        } catch (final SBonitaException e) {
            throw new ActivityInstanceNotFoundException(activityInstanceId);
        }
    }

    private long getNumberOfDataInstancesOfContainer(final long instanceId,
            final DataInstanceContainer containerType) throws SBonitaException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ParentContainerResolver parentContainerResolver = tenantAccessor.getParentContainerResolver();
        final DataInstanceService dataInstanceService = tenantAccessor.getDataInstanceService();
        return dataInstanceService.getNumberOfDataInstances(instanceId, containerType.name(),
                parentContainerResolver);
    }

    @Override
    public long getNumberOfProcessDataInstances(final long processInstanceId)
            throws ProcessInstanceNotFoundException {
        try {
            return getNumberOfDataInstancesOfContainer(processInstanceId, DataInstanceContainer.PROCESS_INSTANCE);
        } catch (final SBonitaException e) {
            throw new ProcessInstanceNotFoundException(e);
        }
    }

    protected Map<String, Serializable> executeOperations(final ConnectorResult connectorResult,
            final List<Operation> operations, final Map<String, Serializable> operationInputValues,
            final SExpressionContext expressionContext, final ClassLoader classLoader,
            final TenantServiceAccessor tenantAccessor) throws SBonitaException {
        final ConnectorService connectorService = tenantAccessor.getConnectorService();
        final OperationService operationService = tenantAccessor.getOperationService();
        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(classLoader);
            final Map<String, Serializable> externalDataValue = new HashMap<String, Serializable>(
                    operations.size());
            // convert the client operation to server operation
            final List<SOperation> sOperations = convertOperations(operations);
            // set input values of expression with connector result + provided input for this operation
            final HashMap<String, Object> inputValues = new HashMap<String, Object>(operationInputValues);
            inputValues.putAll(connectorResult.getResult());
            expressionContext.setInputValues(inputValues);
            // execute
            final Long containerId = expressionContext.getContainerId();
            operationService.execute(sOperations, containerId == null ? -1 : containerId,
                    expressionContext.getContainerType(), expressionContext);
            // return the value of the data if it's an external data
            for (final Operation operation : operations) {
                final LeftOperand leftOperand = operation.getLeftOperand();
                if (LeftOperand.TYPE_EXTERNAL_DATA.equals(leftOperand.getType())) {
                    externalDataValue.put(leftOperand.getName(),
                            (Serializable) expressionContext.getInputValues().get(leftOperand.getName()));
                }
            }
            // we finally disconnect the connector
            connectorService.disconnect(connectorResult);
            return externalDataValue;
        } finally {
            Thread.currentThread().setContextClassLoader(contextClassLoader);
        }
    }

    private Map<String, Serializable> toSerializableMap(final Map<String, Object> map,
            final String connectorDefinitionId, final String connectorDefinitionVersion)
            throws NotSerializableException {
        final HashMap<String, Serializable> resMap = new HashMap<String, Serializable>(map.size());
        for (final Entry<String, Object> entry : map.entrySet()) {
            try {
                resMap.put(entry.getKey(), (Serializable) entry.getValue());
            } catch (final ClassCastException e) {
                throw new NotSerializableException(connectorDefinitionId, connectorDefinitionVersion,
                        entry.getKey(), entry.getValue());
            }
        }
        return resMap;
    }

    @Override
    public Map<String, Serializable> executeConnectorOnProcessDefinition(final String connectorDefinitionId,
            final String connectorDefinitionVersion, final Map<String, Expression> connectorInputParameters,
            final Map<String, Map<String, Serializable>> inputValues, final long processDefinitionId)
            throws ConnectorExecutionException {
        return executeConnectorOnProcessDefinitionWithOrWithoutOperations(connectorDefinitionId,
                connectorDefinitionVersion, connectorInputParameters, inputValues, null, null, processDefinitionId);
    }

    @Override
    public Map<String, Serializable> executeConnectorOnProcessDefinition(final String connectorDefinitionId,
            final String connectorDefinitionVersion, final Map<String, Expression> connectorInputParameters,
            final Map<String, Map<String, Serializable>> inputValues, final List<Operation> operations,
            final Map<String, Serializable> operationInputValues, final long processDefinitionId)
            throws ConnectorExecutionException {
        return executeConnectorOnProcessDefinitionWithOrWithoutOperations(connectorDefinitionId,
                connectorDefinitionVersion, connectorInputParameters, inputValues, operations, operationInputValues,
                processDefinitionId);
    }

    /**
     * execute the connector and return connector output if there is no operation or operation output if there is operation
     *
     * @param operations
     * @param operationInputValues
     */
    private Map<String, Serializable> executeConnectorOnProcessDefinitionWithOrWithoutOperations(
            final String connectorDefinitionId, final String connectorDefinitionVersion,
            final Map<String, Expression> connectorInputParameters,
            final Map<String, Map<String, Serializable>> inputValues, final List<Operation> operations,
            final Map<String, Serializable> operationInputValues, final long processDefinitionId)
            throws ConnectorExecutionException {
        checkConnectorParameters(connectorInputParameters, inputValues);
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();
        final ConnectorService connectorService = tenantAccessor.getConnectorService();
        final ClassLoaderService classLoaderService = tenantAccessor.getClassLoaderService();

        try {
            final ClassLoader classLoader = classLoaderService.getLocalClassLoader(ScopeType.PROCESS.name(),
                    processDefinitionId);

            final Map<String, SExpression> connectorsExps = ModelConvertor
                    .constructExpressions(connectorInputParameters);
            final SExpressionContext expcontext = new SExpressionContext();
            expcontext.setProcessDefinitionId(processDefinitionId);
            expcontext.setContainerState(ContainerState.ACTIVE);
            final SProcessDefinition processDef = processDefinitionService
                    .getProcessDefinition(processDefinitionId);
            if (processDef != null) {
                expcontext.setProcessDefinition(processDef);
            }
            final ConnectorResult connectorResult = connectorService.executeMutipleEvaluation(processDefinitionId,
                    connectorDefinitionId, connectorDefinitionVersion, connectorsExps, inputValues, classLoader,
                    expcontext);
            if (operations != null) {
                // execute operations
                return executeOperations(connectorResult, operations, operationInputValues, expcontext, classLoader,
                        tenantAccessor);
            }
            return getSerializableResultOfConnector(connectorDefinitionVersion, connectorResult, connectorService);
        } catch (final SBonitaException e) {
            throw new ConnectorExecutionException(e);
        } catch (final NotSerializableException e) {
            throw new ConnectorExecutionException(e);
        }
    }

    protected Map<String, Serializable> getSerializableResultOfConnector(final String connectorDefinitionVersion,
            final ConnectorResult connectorResult, final ConnectorService connectorService)
            throws NotSerializableException, SConnectorException {
        connectorService.disconnect(connectorResult);
        return toSerializableMap(connectorResult.getResult(), connectorDefinitionVersion,
                connectorDefinitionVersion);
    }

    protected void checkConnectorParameters(final Map<String, Expression> connectorInputParameters,
            final Map<String, Map<String, Serializable>> inputValues) throws ConnectorExecutionException {
        if (connectorInputParameters.size() != inputValues.size()) {
            throw new ConnectorExecutionException(
                    "The number of input parameters is not consistent with the number of input values. Input parameters: "
                            + connectorInputParameters.size() + ", number of input values: " + inputValues.size());
        }
    }

    @Override
    public void setActivityStateByName(final long activityInstanceId, final String state) throws UpdateException {
        setActivityStateById(activityInstanceId, ModelConvertor.getServerActivityStateId(state));
    }

    @Override
    public void setActivityStateById(final long activityInstanceId, final int stateId) throws UpdateException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final FlowNodeExecutor flowNodeExecutor = tenantAccessor.getFlowNodeExecutor();
        final EventInstanceService eventInstanceService = tenantAccessor.getEventInstanceService();
        final WaitingEventsInterrupter waitingEventsInterrupter = new WaitingEventsInterrupter(eventInstanceService,
                tenantAccessor.getSchedulerService(), tenantAccessor.getTechnicalLoggerService());
        final StateBehaviors stateBehaviors = new StateBehaviors(tenantAccessor.getBPMInstancesCreator(),
                tenantAccessor.getEventsHandler(), tenantAccessor.getActivityInstanceService(),
                tenantAccessor.getUserFilterService(), tenantAccessor.getClassLoaderService(),
                tenantAccessor.getActorMappingService(), tenantAccessor.getConnectorInstanceService(),
                tenantAccessor.getExpressionResolverService(), tenantAccessor.getProcessDefinitionService(),
                tenantAccessor.getDataInstanceService(), tenantAccessor.getOperationService(),
                tenantAccessor.getWorkService(), tenantAccessor.getContainerRegistry(),
                tenantAccessor.getEventInstanceService(), tenantAccessor.getCommentService(),
                tenantAccessor.getIdentityService(), tenantAccessor.getProcessInstanceService(),
                tenantAccessor.getParentContainerResolver(), waitingEventsInterrupter,
                tenantAccessor.getTechnicalLoggerService(), tenantAccessor.getRefBusinessDataService());
        try {
            final SActivityInstance sActivityInstance = getSActivityInstance(activityInstanceId);
            if (sActivityInstance instanceof SHumanTaskInstance) {
                stateBehaviors.interruptSubActivities(sActivityInstance.getId(), SStateCategory.ABORTING);
            }

            // set state
            flowNodeExecutor.setStateByStateId(sActivityInstance.getLogicalGroup(0), sActivityInstance.getId(),
                    stateId);
        } catch (final SBonitaException e) {
            throw new UpdateException(e);
        }
    }

    @Override
    public void setTaskPriority(final long humanTaskInstanceId, final TaskPriority priority)
            throws UpdateException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();

        try {
            final SetTaskPriority transactionContent = new SetTaskPriority(activityInstanceService,
                    humanTaskInstanceId, STaskPriority.valueOf(priority.name()));
            transactionContent.execute();
        } catch (final SBonitaException e) {
            throw new UpdateException(e);
        }
    }

    @Override
    @Deprecated
    public void deleteProcessInstances(final long processDefinitionId) throws DeletionException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        try {
            deleteProcessInstancesFromProcessDefinition(processDefinitionId, tenantAccessor);
            new DeleteArchivedProcessInstances(tenantAccessor, processDefinitionId).execute();
        } catch (final SProcessInstanceHierarchicalDeletionException e) {
            throw new ProcessInstanceHierarchicalDeletionException(e.getMessage(), e.getProcessInstanceId());
        } catch (final SBonitaException e) {
            throw new DeletionException(e);
        }
    }

    private void deleteProcessInstanceInTransaction(final TenantServiceAccessor tenantAccessor,
            final long processInstanceId) throws SBonitaException {
        final UserTransactionService userTransactionService = tenantAccessor.getUserTransactionService();
        final ProcessInstanceService processInstanceService = tenantAccessor.getProcessInstanceService();

        try {
            userTransactionService.executeInTransaction(new Callable<Void>() {

                @Override
                public Void call() throws Exception {
                    final SProcessInstance sProcessInstance = processInstanceService
                            .getProcessInstance(processInstanceId);
                    deleteJobsOnProcessInstance(sProcessInstance);
                    processInstanceService.deleteParentProcessInstanceAndElements(sProcessInstance);
                    return null;
                }

            });
        } catch (final SBonitaException e) {
            throw e;
        } catch (final Exception e) {
            throw new SBonitaRuntimeException("Error while deleting the parent process instance and elements.", e);
        }
    }

    @Override
    @CustomTransactions
    public long deleteProcessInstances(final long processDefinitionId, final int startIndex, final int maxResults)
            throws DeletionException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessInstanceService processInstanceService = tenantAccessor.getProcessInstanceService();
        final UserTransactionService userTxService = tenantAccessor.getUserTransactionService();
        try {
            final Map<SProcessInstance, List<Long>> processInstancesWithChildrenIds = userTxService
                    .executeInTransaction(new Callable<Map<SProcessInstance, List<Long>>>() {

                        @Override
                        public Map<SProcessInstance, List<Long>> call() throws SBonitaReadException {
                            final List<SProcessInstance> sProcessInstances1 = searchProcessInstancesFromProcessDefinition(
                                    processInstanceService, processDefinitionId, startIndex, maxResults);
                            final Map<SProcessInstance, List<Long>> sProcessInstanceListHashMap = new LinkedHashMap<SProcessInstance, List<Long>>(
                                    sProcessInstances1.size());
                            for (final SProcessInstance rootProcessInstance : sProcessInstances1) {
                                List<Long> tmpList;
                                final List<Long> childrenProcessInstanceIds = new ArrayList<Long>();
                                int fromIndex = 0;
                                do {
                                    // from index always will be zero because elements will be deleted
                                    tmpList = processInstanceService
                                            .getArchivedChildrenSourceObjectIdsFromRootProcessInstance(
                                                    rootProcessInstance.getId(), fromIndex, BATCH_SIZE,
                                                    OrderByType.ASC);
                                    childrenProcessInstanceIds.addAll(tmpList);
                                    fromIndex += BATCH_SIZE;
                                } while (tmpList.size() == BATCH_SIZE);
                                sProcessInstanceListHashMap.put(rootProcessInstance, childrenProcessInstanceIds);
                            }
                            return sProcessInstanceListHashMap;
                        }
                    });

            if (processInstancesWithChildrenIds.isEmpty()) {
                return 0;
            }

            final LockService lockService = tenantAccessor.getLockService();
            final String objectType = SFlowElementsContainerType.PROCESS.name();
            List<BonitaLock> locks = null;
            try {
                locks = createLockProcessInstances(lockService, objectType, processInstancesWithChildrenIds,
                        tenantAccessor.getTenantId());
                return userTxService.executeInTransaction(new Callable<Long>() {

                    @Override
                    public Long call() throws Exception {
                        final List<SProcessInstance> sProcessInstances = new ArrayList<SProcessInstance>(
                                processInstancesWithChildrenIds.keySet());
                        deleteJobsOnProcessInstance(processDefinitionId, sProcessInstances);
                        return processInstanceService.deleteParentProcessInstanceAndElements(sProcessInstances);
                    }
                });
            } finally {
                releaseLocks(tenantAccessor, lockService, locks, tenantAccessor.getTenantId());
            }

        } catch (final SProcessInstanceHierarchicalDeletionException e) {
            throw new ProcessInstanceHierarchicalDeletionException(e.getMessage(), e.getProcessInstanceId());
        } catch (final Exception e) {
            throw new DeletionException(e);
        }
    }

    private void deleteJobsOnEventSubProcess(final SProcessDefinition processDefinition,
            final SProcessInstance sProcessInstance) {
        final Set<SSubProcessDefinition> sSubProcessDefinitions = processDefinition.getProcessContainer()
                .getSubProcessDefinitions();
        for (final SSubProcessDefinition sSubProcessDefinition : sSubProcessDefinitions) {
            final List<SStartEventDefinition> startEventsOfSubProcess = sSubProcessDefinition
                    .getSubProcessContainer().getStartEvents();
            deleteJobsOnEventSubProcess(processDefinition, sProcessInstance, sSubProcessDefinition,
                    startEventsOfSubProcess);

            final List<SIntermediateCatchEventDefinition> intermediateCatchEvents = sSubProcessDefinition
                    .getSubProcessContainer().getIntermediateCatchEvents();
            deleteJobsOnEventSubProcess(processDefinition, sProcessInstance, sSubProcessDefinition,
                    intermediateCatchEvents);

            final List<SBoundaryEventDefinition> sEndEventDefinitions = sSubProcessDefinition
                    .getSubProcessContainer().getBoundaryEvents();
            deleteJobsOnEventSubProcess(processDefinition, sProcessInstance, sSubProcessDefinition,
                    sEndEventDefinitions);
        }
    }

    private void deleteJobsOnEventSubProcess(final SProcessDefinition processDefinition,
            final SProcessInstance sProcessInstance, final SSubProcessDefinition sSubProcessDefinition,
            final List<? extends SCatchEventDefinition> sCatchEventDefinitions) {
        final SchedulerService schedulerService = getTenantAccessor().getSchedulerService();
        final TechnicalLoggerService logger = getTenantAccessor().getTechnicalLoggerService();

        for (final SCatchEventDefinition sCatchEventDefinition : sCatchEventDefinitions) {
            try {
                if (!sCatchEventDefinition.getTimerEventTriggerDefinitions().isEmpty()) {
                    final String jobName = JobNameBuilder.getTimerEventJobName(processDefinition.getId(),
                            sCatchEventDefinition, sProcessInstance.getId(), sSubProcessDefinition.getId());
                    final boolean delete = schedulerService.delete(jobName);
                    if (!delete && schedulerService.isExistingJob(jobName)) {
                        if (logger.isLoggable(this.getClass(), TechnicalLogSeverity.WARNING)) {
                            logger.log(this.getClass(), TechnicalLogSeverity.WARNING, "No job found with name '"
                                    + jobName + "' when interrupting timer catch event named '"
                                    + sCatchEventDefinition.getName() + "' on event sub process with the id '"
                                    + sSubProcessDefinition.getId() + "'. It was probably already triggered.");
                        }
                    }
                }
            } catch (final Exception e) {
                if (logger.isLoggable(this.getClass(), TechnicalLogSeverity.WARNING)) {
                    logger.log(this.getClass(), TechnicalLogSeverity.WARNING, e);
                }
            }
        }
    }

    void deleteJobsOnProcessInstance(final SProcessInstance sProcessInstance) throws SBonitaException {
        deleteJobsOnProcessInstance(sProcessInstance.getProcessDefinitionId(),
                Collections.singletonList(sProcessInstance));
    }

    private void deleteJobsOnProcessInstance(final long processDefinitionId,
            final List<SProcessInstance> sProcessInstances) throws SBonitaException {
        final SProcessDefinition processDefinition = getTenantAccessor().getProcessDefinitionService()
                .getProcessDefinition(processDefinitionId);

        for (final SProcessInstance sProcessInstance : sProcessInstances) {
            deleteJobsOnProcessInstance(processDefinition, sProcessInstance);
        }
    }

    private void deleteJobsOnProcessInstance(final SProcessDefinition processDefinition,
            final SProcessInstance sProcessInstance) throws SBonitaReadException {
        final List<SStartEventDefinition> startEventsOfSubProcess = processDefinition.getProcessContainer()
                .getStartEvents();
        deleteJobsOnProcessInstance(processDefinition, sProcessInstance, startEventsOfSubProcess);

        final List<SIntermediateCatchEventDefinition> intermediateCatchEvents = processDefinition
                .getProcessContainer().getIntermediateCatchEvents();
        deleteJobsOnProcessInstance(processDefinition, sProcessInstance, intermediateCatchEvents);

        final List<SBoundaryEventDefinition> sEndEventDefinitions = processDefinition.getProcessContainer()
                .getBoundaryEvents();
        deleteJobsOnProcessInstance(processDefinition, sProcessInstance, sEndEventDefinitions);

        deleteJobsOnEventSubProcess(processDefinition, sProcessInstance);
    }

    private void deleteJobsOnProcessInstance(final SProcessDefinition processDefinition,
            final SProcessInstance sProcessInstance,
            final List<? extends SCatchEventDefinition> sCatchEventDefinitions) throws SBonitaReadException {
        for (final SCatchEventDefinition sCatchEventDefinition : sCatchEventDefinitions) {
            deleteJobsOnFlowNodeInstances(processDefinition, sCatchEventDefinition, sProcessInstance);
        }
    }

    private void deleteJobsOnFlowNodeInstances(final SProcessDefinition processDefinition,
            final SCatchEventDefinition sCatchEventDefinition, final SProcessInstance sProcessInstance)
            throws SBonitaReadException {
        final ActivityInstanceService activityInstanceService = getTenantAccessor().getActivityInstanceService();

        final List<OrderByOption> orderByOptions = Collections
                .singletonList(new OrderByOption(SCatchEventInstance.class, "id", OrderByType.ASC));
        final FilterOption filterOption1 = new FilterOption(SCatchEventInstance.class, "flowNodeDefinitionId",
                sCatchEventDefinition.getId());
        final FilterOption filterOption2 = new FilterOption(SCatchEventInstance.class, "logicalGroup4",
                sProcessInstance.getId());
        QueryOptions queryOptions = new QueryOptions(0, 100, orderByOptions,
                Arrays.asList(filterOption1, filterOption2), null);
        List<SCatchEventInstance> sCatchEventInstances = activityInstanceService
                .searchFlowNodeInstances(SCatchEventInstance.class, queryOptions);
        while (!sCatchEventInstances.isEmpty()) {
            for (final SCatchEventInstance sCatchEventInstance : sCatchEventInstances) {
                deleteJobsOnFlowNodeInstance(processDefinition, sCatchEventDefinition, sCatchEventInstance);
            }
            queryOptions = QueryOptions.getNextPage(queryOptions);
            sCatchEventInstances = activityInstanceService.searchFlowNodeInstances(SCatchEventInstance.class,
                    queryOptions);
        }
    }

    private void deleteJobsOnFlowNodeInstance(final SProcessDefinition processDefinition,
            final SCatchEventDefinition sCatchEventDefinition, final SCatchEventInstance sCatchEventInstance) {
        final SchedulerService schedulerService = getTenantAccessor().getSchedulerService();
        final TechnicalLoggerService logger = getTenantAccessor().getTechnicalLoggerService();

        try {
            if (!sCatchEventDefinition.getTimerEventTriggerDefinitions().isEmpty()) {
                final String jobName = JobNameBuilder.getTimerEventJobName(processDefinition.getId(),
                        sCatchEventDefinition, sCatchEventInstance);
                final boolean delete = schedulerService.delete(jobName);
                if (!delete && schedulerService.isExistingJob(jobName)) {
                    if (logger.isLoggable(this.getClass(), TechnicalLogSeverity.WARNING)) {
                        logger.log(this.getClass(), TechnicalLogSeverity.WARNING,
                                "No job found with name '" + jobName
                                        + "' when interrupting timer catch event named '"
                                        + sCatchEventDefinition.getName() + "' and id '"
                                        + sCatchEventInstance.getId() + "'. It was probably already triggered.");
                    }
                }
            }
        } catch (final Exception e) {
            if (logger.isLoggable(this.getClass(), TechnicalLogSeverity.WARNING)) {
                logger.log(this.getClass(), TechnicalLogSeverity.WARNING, e);
            }
        }
    }

    private List<SProcessInstance> searchProcessInstancesFromProcessDefinition(
            final ProcessInstanceService processInstanceService, final long processDefinitionId,
            final int startIndex, final int maxResults) throws SBonitaReadException {
        final SProcessInstanceBuilderFactory keyProvider = BuilderFactory.get(SProcessInstanceBuilderFactory.class);
        final FilterOption filterOption = new FilterOption(SProcessInstance.class,
                keyProvider.getProcessDefinitionIdKey(), processDefinitionId);
        final OrderByOption order2 = new OrderByOption(SProcessInstance.class, keyProvider.getIdKey(),
                OrderByType.ASC);
        // Order by caller id ASC because we need to have parent process deleted before their sub processes
        final OrderByOption order = new OrderByOption(SProcessInstance.class, keyProvider.getCallerIdKey(),
                OrderByType.ASC);
        final QueryOptions queryOptions = new QueryOptions(startIndex, maxResults, Arrays.asList(order, order2),
                Collections.singletonList(filterOption), null);
        return processInstanceService.searchProcessInstances(queryOptions);
    }

    @Override
    public long deleteArchivedProcessInstances(final long processDefinitionId, final int startIndex,
            final int maxResults) throws DeletionException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessInstanceService processInstanceService = tenantAccessor.getProcessInstanceService();

        try {
            final List<SAProcessInstance> saProcessInstances = searchArchivedProcessInstancesFromProcessDefinition(
                    processInstanceService, processDefinitionId, startIndex, maxResults);
            if (!saProcessInstances.isEmpty()) {
                return processInstanceService.deleteArchivedParentProcessInstancesAndElements(saProcessInstances);
            }
            return 0;
        } catch (final SBonitaException e) {
            throw new DeletionException(e);
        }
    }

    @Override
    public long deleteArchivedProcessInstancesInAllStates(final List<Long> sourceProcessInstanceIds)
            throws DeletionException {
        if (sourceProcessInstanceIds == null || sourceProcessInstanceIds.isEmpty()) {
            throw new IllegalArgumentException(
                    "The identifier of the archived process instances to deleted are missing !!");
        }
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessInstanceService processInstanceService = tenantAccessor.getProcessInstanceService();

        try {
            final List<SAProcessInstance> saProcessInstances = processInstanceService
                    .getArchivedProcessInstancesInAllStates(sourceProcessInstanceIds);
            return processInstanceService.deleteArchivedParentProcessInstancesAndElements(saProcessInstances);
        } catch (final SProcessInstanceHierarchicalDeletionException e) {
            throw new ProcessInstanceHierarchicalDeletionException(e.getMessage(), e.getProcessInstanceId());
        } catch (final SBonitaException e) {
            throw new DeletionException(e);
        }
    }

    @Override
    public long deleteArchivedProcessInstancesInAllStates(final long sourceProcessInstanceId)
            throws DeletionException {
        return deleteArchivedProcessInstancesInAllStates(Collections.singletonList(sourceProcessInstanceId));
    }

    private List<SAProcessInstance> searchArchivedProcessInstancesFromProcessDefinition(
            final ProcessInstanceService processInstanceService, final long processDefinitionId,
            final int startIndex, final int maxResults) throws SBonitaReadException {
        final SAProcessInstanceBuilderFactory keyProvider = BuilderFactory
                .get(SAProcessInstanceBuilderFactory.class);
        final FilterOption filterOption = new FilterOption(SAProcessInstance.class,
                keyProvider.getProcessDefinitionIdKey(), processDefinitionId);
        final OrderByOption order = new OrderByOption(SAProcessInstance.class, keyProvider.getIdKey(),
                OrderByType.ASC);
        // Order by caller id ASC because we need to have parent process deleted before their sub processes
        final OrderByOption order2 = new OrderByOption(SAProcessInstance.class, keyProvider.getCallerIdKey(),
                OrderByType.ASC);
        final QueryOptions queryOptions = new QueryOptions(startIndex, maxResults, Arrays.asList(order, order2),
                Collections.singletonList(filterOption), null);
        return processInstanceService.searchArchivedProcessInstances(queryOptions);
    }

    private List<BonitaLock> createLockProcessInstances(final LockService lockService, final String objectType,
            final Map<SProcessInstance, List<Long>> sProcessInstances, final long tenantId) throws SLockException {
        final List<BonitaLock> locks = new ArrayList<BonitaLock>();
        final HashSet<Long> uniqIds = new HashSet<Long>();
        for (final Entry<SProcessInstance, List<Long>> sProcessInstancewithChildrenIds : sProcessInstances
                .entrySet()) {
            uniqIds.add(sProcessInstancewithChildrenIds.getKey().getId());
            for (final Long childId : sProcessInstancewithChildrenIds.getValue()) {
                uniqIds.add(childId);
            }
        }
        for (final Long id : uniqIds) {
            final BonitaLock childLock = lockService.lock(id, objectType, tenantId);
            locks.add(childLock);
        }
        return locks;
    }

    @Override
    @CustomTransactions
    public void deleteProcessInstance(final long processInstanceId) throws DeletionException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final LockService lockService = tenantAccessor.getLockService();
        final String objectType = SFlowElementsContainerType.PROCESS.name();
        BonitaLock lock = null;
        try {
            lock = lockService.lock(processInstanceId, objectType, tenantAccessor.getTenantId());
            deleteProcessInstanceInTransaction(tenantAccessor, processInstanceId);
        } catch (final SProcessInstanceHierarchicalDeletionException e) {
            throw new ProcessInstanceHierarchicalDeletionException(e.getMessage(), e.getProcessInstanceId());
        } catch (final SProcessInstanceNotFoundException e) {
            throw new DeletionException(e);
        } catch (final SBonitaException e) {
            throw new DeletionException(e);
        } finally {
            if (lock != null) {
                try {
                    lockService.unlock(lock, tenantAccessor.getTenantId());
                } catch (final SLockException e) {
                    throw new DeletionException(
                            "Lock was not released. Object type: " + objectType + ", id: " + processInstanceId, e);
                }
            }
        }
    }

    @Override
    public SearchResult<ProcessInstance> searchOpenProcessInstances(final SearchOptions searchOptions)
            throws SearchException {
        // To select all process instances completed, without subprocess
        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(searchOptions);
        searchOptionsBuilder.differentFrom(ProcessInstanceSearchDescriptor.STATE_ID,
                ProcessInstanceState.COMPLETED.getId());
        searchOptionsBuilder.filter(ProcessInstanceSearchDescriptor.CALLER_ID, -1);
        try {
            return searchProcessInstances(getTenantAccessor(), searchOptionsBuilder.done());
        } catch (final SBonitaException e) {
            throw new SearchException(e);
        }
    }

    @Override
    public SearchResult<ProcessInstance> searchProcessInstances(final SearchOptions searchOptions)
            throws SearchException {
        try {
            return searchProcessInstances(getTenantAccessor(), searchOptions);
        } catch (final SBonitaException e) {
            throw new SearchException(e);
        }
    }

    @Override
    public SearchResult<ProcessInstance> searchFailedProcessInstances(final SearchOptions searchOptions)
            throws SearchException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();
        final ProcessInstanceService processInstanceService = tenantAccessor.getProcessInstanceService();
        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();

        try {
            final SearchFailedProcessInstances searchProcessInstances = new SearchFailedProcessInstances(
                    processInstanceService, searchEntitiesDescriptor.getSearchProcessInstanceDescriptor(),
                    searchOptions, processDefinitionService);
            searchProcessInstances.execute();
            return searchProcessInstances.getResult();
        } catch (final SBonitaException sbe) {
            throw new SearchException(sbe);
        }
    }

    /*
     * (non-Javadoc)
     * @see org.bonitasoft.engine.api.ProcessRuntimeAPI#searchFailedProcessInstancesSupervisedBy(long, org.bonitasoft.engine.search.SearchOptions)
     */
    @Override
    public SearchResult<ProcessInstance> searchFailedProcessInstancesSupervisedBy(final long userId,
            final SearchOptions searchOptions) throws SearchException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final IdentityService identityService = tenantAccessor.getIdentityService();
        final GetSUser getUser = createTxUserGetter(userId, identityService);
        try {
            getUser.execute();
        } catch (final SBonitaException e) {
            return new SearchResultImpl<ProcessInstance>(0, Collections.<ProcessInstance>emptyList());
        }
        final ProcessInstanceService processInstanceService = tenantAccessor.getProcessInstanceService();
        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();
        final SearchFailedProcessInstancesSupervisedBy searchFailedProcessInstances = createSearchFailedProcessInstancesSupervisedBy(
                userId, searchOptions, processInstanceService, searchEntitiesDescriptor, processDefinitionService);
        try {
            searchFailedProcessInstances.execute();
            return searchFailedProcessInstances.getResult();
        } catch (final SBonitaException sbe) {
            throw new SearchException(sbe);
        }
    }

    protected SearchFailedProcessInstancesSupervisedBy createSearchFailedProcessInstancesSupervisedBy(
            final long userId, final SearchOptions searchOptions,
            final ProcessInstanceService processInstanceService,
            final SearchEntitiesDescriptor searchEntitiesDescriptor,
            final ProcessDefinitionService processDefinitionService) {
        return new SearchFailedProcessInstancesSupervisedBy(processInstanceService,
                searchEntitiesDescriptor.getSearchProcessInstanceDescriptor(), userId, searchOptions,
                processDefinitionService);
    }

    protected GetSUser createTxUserGetter(final long userId, final IdentityService identityService) {
        return new GetSUser(identityService, userId);
    }

    @Override
    public SearchResult<ProcessInstance> searchOpenProcessInstancesSupervisedBy(final long userId,
            final SearchOptions searchOptions) throws SearchException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final IdentityService identityService = tenantAccessor.getIdentityService();
        final GetSUser getUser = createTxUserGetter(userId, identityService);
        try {
            getUser.execute();
        } catch (final SBonitaException e) {
            return new SearchResultImpl<ProcessInstance>(0, Collections.<ProcessInstance>emptyList());
        }
        final ProcessInstanceService processInstanceService = tenantAccessor.getProcessInstanceService();
        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();
        final SearchOpenProcessInstancesSupervisedBy searchOpenProcessInstances = new SearchOpenProcessInstancesSupervisedBy(
                processInstanceService, searchEntitiesDescriptor.getSearchProcessInstanceDescriptor(), userId,
                searchOptions, processDefinitionService);
        try {
            searchOpenProcessInstances.execute();
            return searchOpenProcessInstances.getResult();
        } catch (final SBonitaException sbe) {
            throw new SearchException(sbe);
        }
    }

    @Override
    public SearchResult<ProcessDeploymentInfo> searchProcessDeploymentInfosStartedBy(final long userId,
            final SearchOptions searchOptions) throws SearchException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();
        final SearchProcessDefinitionsDescriptor searchDescriptor = searchEntitiesDescriptor
                .getSearchProcessDefinitionsDescriptor();
        final SearchProcessDeploymentInfosStartedBy searcher = new SearchProcessDeploymentInfosStartedBy(
                processDefinitionService, searchDescriptor, userId, searchOptions);
        try {
            searcher.execute();
        } catch (final SBonitaException e) {
            throw new SearchException("Can't get ProcessDeploymentInfo startedBy userid " + userId, e);
        }
        return searcher.getResult();
    }

    @Override
    public SearchResult<ProcessDeploymentInfo> searchProcessDeploymentInfos(final SearchOptions searchOptions)
            throws SearchException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();
        final SearchProcessDefinitionsDescriptor searchDescriptor = searchEntitiesDescriptor
                .getSearchProcessDefinitionsDescriptor();
        final SearchProcessDeploymentInfos transactionSearch = new SearchProcessDeploymentInfos(
                processDefinitionService, searchDescriptor, searchOptions);
        try {
            transactionSearch.execute();
        } catch (final SBonitaException e) {
            throw new SearchException("Can't get processDefinition's executing searchProcessDefinitions()", e);
        }
        return transactionSearch.getResult();
    }

    @Override
    public SearchResult<ProcessDeploymentInfo> searchProcessDeploymentInfosCanBeStartedBy(final long userId,
            final SearchOptions searchOptions) throws RetrieveException, SearchException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();
        final SearchProcessDefinitionsDescriptor searchDescriptor = searchEntitiesDescriptor
                .getSearchProcessDefinitionsDescriptor();
        final SearchProcessDeploymentInfosCanBeStartedBy transactionSearch = new SearchProcessDeploymentInfosCanBeStartedBy(
                processDefinitionService, searchDescriptor, searchOptions, userId);
        try {
            transactionSearch.execute();
        } catch (final SBonitaException e) {
            throw new SearchException("Error while retrieving process definitions: " + e.getMessage(), e);
        }
        return transactionSearch.getResult();
    }

    @Deprecated
    @Override
    public SearchResult<ProcessDeploymentInfo> searchProcessDeploymentInfos(final long userId,
            final SearchOptions searchOptions) throws RetrieveException, SearchException {
        return searchProcessDeploymentInfosCanBeStartedBy(userId, searchOptions);
    }

    @Override
    public SearchResult<ProcessDeploymentInfo> searchProcessDeploymentInfosCanBeStartedByUsersManagedBy(
            final long managerUserId, final SearchOptions searchOptions) throws SearchException {
        final TenantServiceAccessor serviceAccessor = getTenantAccessor();
        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();
        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();
        final SearchProcessDefinitionsDescriptor searchDescriptor = searchEntitiesDescriptor
                .getSearchProcessDefinitionsDescriptor();
        final SearchProcessDeploymentInfosCanBeStartedByUsersManagedBy transactionSearch = new SearchProcessDeploymentInfosCanBeStartedByUsersManagedBy(
                processDefinitionService, searchDescriptor, searchOptions, managerUserId);
        try {
            transactionSearch.execute();
        } catch (final SBonitaException e) {
            throw new SearchException(e);
        }
        return transactionSearch.getResult();
    }

    @Deprecated
    @Override
    public SearchResult<ProcessDeploymentInfo> searchProcessDeploymentInfosUsersManagedByCanStart(
            final long managerUserId, final SearchOptions searchOptions) throws SearchException {
        return searchProcessDeploymentInfosCanBeStartedByUsersManagedBy(managerUserId, searchOptions);
    }

    @Override
    public SearchResult<ProcessDeploymentInfo> searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(
            final long userId, final SearchOptions searchOptions) throws SearchException {
        final TenantServiceAccessor serviceAccessor = getTenantAccessor();
        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();
        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();
        final SearchProcessDefinitionsDescriptor searchDescriptor = searchEntitiesDescriptor
                .getSearchProcessDefinitionsDescriptor();
        final SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor searcher = new SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(
                processDefinitionService, searchDescriptor, searchOptions, userId);
        try {
            searcher.execute();
        } catch (final SBonitaException sbe) {
            throw new SearchException(sbe);
        }
        return searcher.getResult();
    }

    @Override
    public SearchResult<ProcessDeploymentInfo> searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(
            final long supervisorId, final SearchOptions searchOptions) throws SearchException {
        final TenantServiceAccessor serviceAccessor = getTenantAccessor();
        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();
        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();
        final SearchProcessDefinitionsDescriptor searchDescriptor = searchEntitiesDescriptor
                .getSearchProcessDefinitionsDescriptor();
        final SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy searcher = new SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(
                processDefinitionService, searchDescriptor, searchOptions, supervisorId);
        try {
            searcher.execute();
        } catch (final SBonitaException sbe) {
            throw new SearchException(sbe);
        }
        return searcher.getResult();
    }

    @Override
    public SearchResult<ProcessDeploymentInfo> searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks(
            final SearchOptions searchOptions) throws SearchException {
        final TenantServiceAccessor serviceAccessor = getTenantAccessor();
        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();
        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();
        final SearchProcessDefinitionsDescriptor searchDescriptor = searchEntitiesDescriptor
                .getSearchProcessDefinitionsDescriptor();
        final SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasks searcher = new SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasks(
                processDefinitionService, searchDescriptor, searchOptions);
        try {
            searcher.execute();
        } catch (final SBonitaException sbe) {
            throw new SearchException(sbe);
        }
        return searcher.getResult();
    }

    @Override
    public Map<String, Map<String, Long>> getFlownodeStateCounters(final long processInstanceId) {
        final TenantServiceAccessor serviceAccessor = getTenantAccessor();
        final HashMap<String, Map<String, Long>> countersForProcessInstance = new HashMap<String, Map<String, Long>>();
        try {
            // Active flownodes:
            final List<SFlowNodeInstanceStateCounter> flownodes = serviceAccessor.getActivityInstanceService()
                    .getNumberOfFlownodesInAllStates(processInstanceId);
            for (final SFlowNodeInstanceStateCounter nodeCounter : flownodes) {
                final String flownodeName = nodeCounter.getFlownodeName();
                final Map<String, Long> flownodeCounters = getFlownodeCounters(countersForProcessInstance,
                        flownodeName);
                flownodeCounters.put(nodeCounter.getStateName(), nodeCounter.getNumberOf());
                countersForProcessInstance.put(flownodeName, flownodeCounters);
            }
            // Archived flownodes:
            final List<SFlowNodeInstanceStateCounter> archivedFlownodes = serviceAccessor
                    .getActivityInstanceService().getNumberOfArchivedFlownodesInAllStates(processInstanceId);
            for (final SFlowNodeInstanceStateCounter nodeCounter : archivedFlownodes) {
                final String flownodeName = nodeCounter.getFlownodeName();
                final Map<String, Long> flownodeCounters = getFlownodeCounters(countersForProcessInstance,
                        flownodeName);
                flownodeCounters.put(nodeCounter.getStateName(), nodeCounter.getNumberOf());
                countersForProcessInstance.put(flownodeName, flownodeCounters);
            }
        } catch (final SBonitaReadException e) {
            throw new RetrieveException(e);
        }
        return countersForProcessInstance;
    }

    private Map<String, Long> getFlownodeCounters(final HashMap<String, Map<String, Long>> counters,
            final String flownodeName) {
        Map<String, Long> flownodeCounters = counters.get(flownodeName);
        if (flownodeCounters == null) {
            flownodeCounters = new HashMap<String, Long>();
            counters.put(flownodeName, flownodeCounters);
        }
        return flownodeCounters;
    }

    @Override
    public SearchResult<ProcessDeploymentInfo> searchProcessDeploymentInfosSupervisedBy(final long userId,
            final SearchOptions searchOptions) throws SearchException {
        final TenantServiceAccessor serviceAccessor = getTenantAccessor();
        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();
        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();
        final SearchProcessDefinitionsDescriptor searchDescriptor = searchEntitiesDescriptor
                .getSearchProcessDefinitionsDescriptor();
        final SearchProcessDeploymentInfosSupervised searcher = new SearchProcessDeploymentInfosSupervised(
                processDefinitionService, searchDescriptor, searchOptions, userId);
        try {
            searcher.execute();
        } catch (final SBonitaException sbe) {
            throw new SearchException(sbe);
        }
        return searcher.getResult();
    }

    @Override
    public SearchResult<HumanTaskInstance> searchAssignedTasksSupervisedBy(final long supervisorId,
            final SearchOptions searchOptions) throws SearchException {
        final TenantServiceAccessor serviceAccessor = getTenantAccessor();
        final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager();
        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();
        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();
        final SearchAssignedTasksSupervisedBy searchedTasksTransaction = new SearchAssignedTasksSupervisedBy(
                supervisorId, activityInstanceService, flowNodeStateManager,
                searchEntitiesDescriptor.getSearchHumanTaskInstanceDescriptor(), searchOptions);
        try {
            searchedTasksTransaction.execute();
        } catch (final SBonitaException sbe) {
            throw new SearchException(sbe);
        }
        return searchedTasksTransaction.getResult();
    }

    @Override
    public SearchResult<ArchivedHumanTaskInstance> searchArchivedHumanTasksSupervisedBy(final long supervisorId,
            final SearchOptions searchOptions) throws SearchException {
        final TenantServiceAccessor serviceAccessor = getTenantAccessor();
        final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager();
        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();
        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();
        final SearchArchivedHumanTasksSupervisedBy searchedTasksTransaction = new SearchArchivedHumanTasksSupervisedBy(
                supervisorId, activityInstanceService, flowNodeStateManager,
                searchEntitiesDescriptor.getSearchArchivedHumanTaskInstanceDescriptor(), searchOptions);

        try {
            searchedTasksTransaction.execute();
        } catch (final SBonitaException sbe) {
            throw new SearchException(sbe);
        }
        return searchedTasksTransaction.getResult();
    }

    @Override
    public SearchResult<ProcessSupervisor> searchProcessSupervisors(final SearchOptions searchOptions)
            throws SearchException {
        final TenantServiceAccessor serviceAccessor = getTenantAccessor();
        final SupervisorMappingService supervisorService = serviceAccessor.getSupervisorService();
        final SearchProcessSupervisorDescriptor searchDescriptor = new SearchProcessSupervisorDescriptor();
        final SearchSupervisors searchSupervisorsTransaction = new SearchSupervisors(supervisorService,
                searchDescriptor, searchOptions);
        try {
            searchSupervisorsTransaction.execute();
            return searchSupervisorsTransaction.getResult();
        } catch (final SBonitaException e) {
            throw new SearchException(e);
        }
    }

    @Override
    public boolean isUserProcessSupervisor(final long processDefinitionId, final long userId) {
        final TenantServiceAccessor serviceAccessor = getTenantAccessor();
        final SupervisorMappingService supervisorService = serviceAccessor.getSupervisorService();
        try {
            return supervisorService.isProcessSupervisor(processDefinitionId, userId);
        } catch (final SBonitaReadException e) {
            throw new RetrieveException(e);
        }
    }

    @Override
    public void deleteSupervisor(final long supervisorId) throws DeletionException {
        final TenantServiceAccessor serviceAccessor = getTenantAccessor();
        final SupervisorMappingService supervisorService = serviceAccessor.getSupervisorService();
        final TechnicalLoggerService technicalLoggerService = serviceAccessor.getTechnicalLoggerService();
        try {
            supervisorService.deleteProcessSupervisor(supervisorId);
            if (technicalLoggerService.isLoggable(getClass(), TechnicalLogSeverity.INFO)) {
                technicalLoggerService.log(getClass(), TechnicalLogSeverity.INFO,
                        "The process manager has been deleted with id = <" + supervisorId + ">.");
            }
        } catch (final SSupervisorNotFoundException e) {
            throw new DeletionException("The process manager was not found with id = <" + supervisorId + ">");
        } catch (final SSupervisorDeletionException e) {
            throw new DeletionException(e);
        }
    }

    @Override
    public void deleteSupervisor(final Long processDefinitionId, final Long userId, final Long roleId,
            final Long groupId) throws DeletionException {
        final TenantServiceAccessor serviceAccessor = getTenantAccessor();
        final SupervisorMappingService supervisorService = serviceAccessor.getSupervisorService();
        final TechnicalLoggerService technicalLoggerService = serviceAccessor.getTechnicalLoggerService();

        try {
            final List<SProcessSupervisor> sProcessSupervisors = searchSProcessSupervisors(processDefinitionId,
                    userId, groupId, roleId);

            if (!sProcessSupervisors.isEmpty()) {
                // Then, delete it
                supervisorService.deleteProcessSupervisor(sProcessSupervisors.get(0));
                if (technicalLoggerService.isLoggable(getClass(), TechnicalLogSeverity.INFO)) {
                    technicalLoggerService.log(getClass(), TechnicalLogSeverity.INFO,
                            "The process manager has been deleted with process definition id = <"
                                    + processDefinitionId + ">, user id = <" + userId + ">, group id = <" + groupId
                                    + ">, and role id = <" + roleId + ">.");
                }
            } else {
                throw new SSupervisorNotFoundException(userId, roleId, groupId, processDefinitionId);
            }
        } catch (final SBonitaException e) {
            throw new DeletionException(e);
        }
    }

    @Override
    public ProcessSupervisor createProcessSupervisorForUser(final long processDefinitionId, final long userId)
            throws CreationException, AlreadyExistsException {
        final SProcessSupervisorBuilder supervisorBuilder = buildSProcessSupervisor(processDefinitionId);
        supervisorBuilder.setUserId(userId);
        return createSupervisor(supervisorBuilder.done());
    }

    @Override
    public ProcessSupervisor createProcessSupervisorForRole(final long processDefinitionId, final long roleId)
            throws CreationException, AlreadyExistsException {
        final SProcessSupervisorBuilder supervisorBuilder = buildSProcessSupervisor(processDefinitionId);
        supervisorBuilder.setRoleId(roleId);
        return createSupervisor(supervisorBuilder.done());
    }

    @Override
    public ProcessSupervisor createProcessSupervisorForGroup(final long processDefinitionId, final long groupId)
            throws CreationException, AlreadyExistsException {
        final SProcessSupervisorBuilder supervisorBuilder = buildSProcessSupervisor(processDefinitionId);
        supervisorBuilder.setGroupId(groupId);
        return createSupervisor(supervisorBuilder.done());
    }

    @Override
    public ProcessSupervisor createProcessSupervisorForMembership(final long processDefinitionId,
            final long groupId, final long roleId) throws CreationException, AlreadyExistsException {
        final SProcessSupervisorBuilder supervisorBuilder = buildSProcessSupervisor(processDefinitionId);
        supervisorBuilder.setGroupId(groupId);
        supervisorBuilder.setRoleId(roleId);
        return createSupervisor(supervisorBuilder.done());
    }

    private SProcessSupervisorBuilder buildSProcessSupervisor(final long processDefinitionId) {
        final SProcessSupervisorBuilderFactory sProcessSupervisorBuilderFactory = BuilderFactory
                .get(SProcessSupervisorBuilderFactory.class);
        return sProcessSupervisorBuilderFactory.createNewInstance(processDefinitionId);
    }

    private ProcessSupervisor createSupervisor(final SProcessSupervisor sProcessSupervisor)
            throws CreationException, AlreadyExistsException {
        final TenantServiceAccessor tenantServiceAccessor = getTenantAccessor();
        final SupervisorMappingService supervisorService = tenantServiceAccessor.getSupervisorService();
        final TechnicalLoggerService technicalLoggerService = tenantServiceAccessor.getTechnicalLoggerService();

        try {
            checkIfProcessSupervisorAlreadyExists(sProcessSupervisor.getProcessDefId(),
                    sProcessSupervisor.getUserId(), sProcessSupervisor.getGroupId(),
                    sProcessSupervisor.getRoleId());

            final SProcessSupervisor supervisor = supervisorService.createProcessSupervisor(sProcessSupervisor);
            if (technicalLoggerService.isLoggable(getClass(), TechnicalLogSeverity.INFO)) {
                technicalLoggerService.log(getClass(), TechnicalLogSeverity.INFO,
                        "The process manager has been created with process definition id = <"
                                + sProcessSupervisor.getProcessDefId() + ">, user id = <"
                                + sProcessSupervisor.getUserId() + ">, group id = <"
                                + sProcessSupervisor.getGroupId() + ">, and role id = <"
                                + sProcessSupervisor.getRoleId() + ">");
            }
            return ModelConvertor.toProcessSupervisor(supervisor);
        } catch (final SBonitaException e) {
            throw new CreationException(e);
        }
    }

    private void checkIfProcessSupervisorAlreadyExists(final long processDefinitionId, final Long userId,
            final Long groupId, final Long roleId) throws AlreadyExistsException {
        try {
            final List<SProcessSupervisor> processSupervisors = searchSProcessSupervisors(processDefinitionId,
                    userId, groupId, roleId);
            if (!processSupervisors.isEmpty()) {
                throw new AlreadyExistsException("This supervisor already exists for process definition id = <"
                        + processDefinitionId + ">, user id = <" + userId + ">, group id = <" + groupId
                        + ">, role id = <" + roleId + ">");
            }
        } catch (final SBonitaReadException e1) {
            // Nothing to do
        }
    }

    protected List<SProcessSupervisor> searchSProcessSupervisors(final Long processDefinitionId, final Long userId,
            final Long groupId, final Long roleId) throws SBonitaReadException {
        final TenantServiceAccessor serviceAccessor = getTenantAccessor();
        final SupervisorMappingService supervisorService = serviceAccessor.getSupervisorService();
        final SProcessSupervisorBuilderFactory sProcessSupervisorBuilderFactory = BuilderFactory
                .get(SProcessSupervisorBuilderFactory.class);

        final List<OrderByOption> oderByOptions = Collections.singletonList(new OrderByOption(
                SProcessSupervisor.class, sProcessSupervisorBuilderFactory.getUserIdKey(), OrderByType.DESC));
        final List<FilterOption> filterOptions = new ArrayList<FilterOption>();
        filterOptions.add(
                new FilterOption(SProcessSupervisor.class, sProcessSupervisorBuilderFactory.getProcessDefIdKey(),
                        processDefinitionId == null ? -1 : processDefinitionId));
        filterOptions.add(new FilterOption(SProcessSupervisor.class,
                sProcessSupervisorBuilderFactory.getUserIdKey(), userId == null ? -1 : userId));
        filterOptions.add(new FilterOption(SProcessSupervisor.class,
                sProcessSupervisorBuilderFactory.getGroupIdKey(), groupId == null ? -1 : groupId));
        filterOptions.add(new FilterOption(SProcessSupervisor.class,
                sProcessSupervisorBuilderFactory.getRoleIdKey(), roleId == null ? -1 : roleId));

        return supervisorService
                .searchProcessSupervisors(new QueryOptions(0, 1, oderByOptions, filterOptions, null));
    }

    @Deprecated
    @Override
    public SearchResult<ProcessDeploymentInfo> searchUncategorizedProcessDeploymentInfosUserCanStart(
            final long userId, final SearchOptions searchOptions) throws SearchException {
        return searchUncategorizedProcessDeploymentInfosCanBeStartedBy(userId, searchOptions);
    }

    @Override
    public SearchResult<ProcessDeploymentInfo> searchUncategorizedProcessDeploymentInfosCanBeStartedBy(
            final long userId, final SearchOptions searchOptions) throws SearchException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();
        final SearchProcessDefinitionsDescriptor searchDescriptor = searchEntitiesDescriptor
                .getSearchProcessDefinitionsDescriptor();
        final SearchUncategorizedProcessDeploymentInfosCanBeStartedBy transactionSearch = new SearchUncategorizedProcessDeploymentInfosCanBeStartedBy(
                processDefinitionService, searchDescriptor, searchOptions, userId);
        try {
            transactionSearch.execute();
        } catch (final SBonitaException e) {
            throw new SearchException("Error while retrieving process definitions", e);
        }
        return transactionSearch.getResult();
    }

    @Override
    public SearchResult<ArchivedHumanTaskInstance> searchArchivedHumanTasksManagedBy(final long managerUserId,
            final SearchOptions searchOptions) throws SearchException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        final FlowNodeStateManager flowNodeStateManager = tenantAccessor.getFlowNodeStateManager();
        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();
        final SearchArchivedTasksManagedBy searchTransaction = new SearchArchivedTasksManagedBy(managerUserId,
                searchOptions, activityInstanceService, flowNodeStateManager,
                searchEntitiesDescriptor.getSearchArchivedHumanTaskInstanceDescriptor());
        try {
            searchTransaction.execute();
        } catch (final SBonitaException e) {
            throw new SearchException(e);
        }
        return searchTransaction.getResult();
    }

    @Override
    public SearchResult<ProcessInstance> searchOpenProcessInstancesInvolvingUser(final long userId,
            final SearchOptions searchOptions) throws SearchException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final ProcessInstanceService processInstanceService = tenantAccessor.getProcessInstanceService();
        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();
        final SearchOpenProcessInstancesInvolvingUser searchOpenProcessInstances = new SearchOpenProcessInstancesInvolvingUser(
                processInstanceService, searchEntitiesDescriptor.getSearchProcessInstanceDescriptor(), userId,
                searchOptions, processDefinitionService);
        try {
            searchOpenProcessInstances.execute();
            return searchOpenProcessInstances.getResult();
        } catch (final SBonitaException sbe) {
            throw new SearchException(sbe);
        }
    }

    @Override
    public SearchResult<ProcessInstance> searchOpenProcessInstancesInvolvingUsersManagedBy(final long managerUserId,
            final SearchOptions searchOptions) throws SearchException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final ProcessInstanceService processInstanceService = tenantAccessor.getProcessInstanceService();
        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();
        final SearchOpenProcessInstancesInvolvingUsersManagedBy searchOpenProcessInstances = new SearchOpenProcessInstancesInvolvingUsersManagedBy(
                processInstanceService, searchEntitiesDescriptor.getSearchProcessInstanceDescriptor(),
                managerUserId, searchOptions, processDefinitionService);
        try {
            searchOpenProcessInstances.execute();
            return searchOpenProcessInstances.getResult();
        } catch (final SBonitaException sbe) {
            throw new SearchException(sbe);
        }
    }

    @Override
    public SearchResult<ArchivedHumanTaskInstance> searchArchivedHumanTasks(final SearchOptions searchOptions)
            throws SearchException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        final FlowNodeStateManager flowNodeStateManager = tenantAccessor.getFlowNodeStateManager();
        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();

        final SearchArchivedTasks searchArchivedTasks = new SearchArchivedTasks(activityInstanceService,
                flowNodeStateManager, searchEntitiesDescriptor.getSearchArchivedHumanTaskInstanceDescriptor(),
                searchOptions);
        try {
            searchArchivedTasks.execute();
        } catch (final SBonitaException sbe) {
            throw new SearchException(sbe);
        }
        return searchArchivedTasks.getResult();
    }

    @Override
    public SearchResult<HumanTaskInstance> searchAssignedTasksManagedBy(final long managerUserId,
            final SearchOptions searchOptions) throws SearchException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        final FlowNodeStateManager flowNodeStateManager = tenantAccessor.getFlowNodeStateManager();
        final SearchAssignedTaskManagedBy searchAssignedTaskManagedBy = new SearchAssignedTaskManagedBy(
                activityInstanceService, flowNodeStateManager,
                searchEntitiesDescriptor.getSearchHumanTaskInstanceDescriptor(), managerUserId, searchOptions);
        try {
            searchAssignedTaskManagedBy.execute();
        } catch (final SBonitaException e) {
            throw new SearchException(e);
        }
        return searchAssignedTaskManagedBy.getResult();
    }

    @Override
    public SearchResult<ArchivedProcessInstance> searchArchivedProcessInstancesSupervisedBy(final long userId,
            final SearchOptions searchOptions) throws SearchException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final ProcessInstanceService processInstanceService = tenantAccessor.getProcessInstanceService();
        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();
        final SearchArchivedProcessInstancesSupervisedBy searchArchivedProcessInstances = new SearchArchivedProcessInstancesSupervisedBy(
                userId, processInstanceService, tenantAccessor.getProcessDefinitionService(),
                searchEntitiesDescriptor.getSearchArchivedProcessInstanceDescriptor(), searchOptions);
        try {
            searchArchivedProcessInstances.execute();
            return searchArchivedProcessInstances.getResult();
        } catch (final SBonitaException sbe) {
            throw new SearchException(sbe);
        }
    }

    @Override
    public SearchResult<ArchivedProcessInstance> searchArchivedProcessInstancesInvolvingUser(final long userId,
            final SearchOptions searchOptions) throws SearchException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final ProcessInstanceService processInstanceService = tenantAccessor.getProcessInstanceService();
        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();
        final SearchArchivedProcessInstancesInvolvingUser searchArchivedProcessInstances = new SearchArchivedProcessInstancesInvolvingUser(
                userId, processInstanceService, tenantAccessor.getProcessDefinitionService(),
                searchEntitiesDescriptor.getSearchArchivedProcessInstanceDescriptor(), searchOptions);
        try {
            searchArchivedProcessInstances.execute();
            return searchArchivedProcessInstances.getResult();
        } catch (final SBonitaException sbe) {
            throw new SearchException(sbe);
        }
    }

    @Override
    public SearchResult<HumanTaskInstance> searchPendingTasksForUser(final long userId,
            final SearchOptions searchOptions) throws SearchException {
        return searchTasksForUser(userId, searchOptions, false);
    }

    /**
     * @param orAssignedToUser
     *        do we also want to retrieve tasks directly assigned to this user ?
     * @throws SearchException
     */
    private SearchResult<HumanTaskInstance> searchTasksForUser(final long userId, final SearchOptions searchOptions,
            final boolean orAssignedToUser) throws SearchException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        final FlowNodeStateManager flowNodeStateManager = tenantAccessor.getFlowNodeStateManager();
        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();
        final SearchPendingTasksForUser searchPendingTasksForUser = new SearchPendingTasksForUser(
                activityInstanceService, flowNodeStateManager,
                searchEntitiesDescriptor.getSearchHumanTaskInstanceDescriptor(), userId, searchOptions,
                orAssignedToUser);
        try {
            searchPendingTasksForUser.execute();
            return searchPendingTasksForUser.getResult();
        } catch (final SBonitaException sbe) {
            throw new SearchException(sbe);
        }
    }

    @Override
    public SearchResult<HumanTaskInstance> searchMyAvailableHumanTasks(final long userId,
            final SearchOptions searchOptions) throws SearchException {
        return searchTasksForUser(userId, searchOptions, true);
    }

    @Override
    public SearchResult<HumanTaskInstance> searchPendingTasksSupervisedBy(final long userId,
            final SearchOptions searchOptions) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        final FlowNodeStateManager flowNodeStateManager = tenantAccessor.getFlowNodeStateManager();
        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();
        final SearchPendingTasksSupervisedBy searchPendingTasksSupervisedBy = new SearchPendingTasksSupervisedBy(
                activityInstanceService, flowNodeStateManager,
                searchEntitiesDescriptor.getSearchHumanTaskInstanceDescriptor(), userId, searchOptions);
        try {
            searchPendingTasksSupervisedBy.execute();
            return searchPendingTasksSupervisedBy.getResult();
        } catch (final SBonitaException sbe) {
            throw new BonitaRuntimeException(sbe);
        }
    }

    @Override
    public SearchResult<Comment> searchComments(final SearchOptions searchOptions) throws SearchException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final SCommentService commentService = tenantAccessor.getCommentService();
        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();
        final SearchComments searchComments = new SearchComments(
                searchEntitiesDescriptor.getSearchCommentDescriptor(), searchOptions, commentService);
        try {
            searchComments.execute();
            return searchComments.getResult();
        } catch (final SBonitaException sbe) {
            throw new SearchException(sbe);
        }
    }

    @Override
    public Comment addProcessComment(final long processInstanceId, final String comment) throws CreationException {
        try {
            // TODO: refactor this method when deprecated addComment() method is removed from API:
            return addComment(processInstanceId, comment);
        } catch (final RetrieveException e) {
            throw new CreationException(buildCantAddCommentOnProcessInstance(), e.getCause());
        }
    }

    private String buildCantAddCommentOnProcessInstance() {
        return "Cannot add a comment on a finished or inexistant process instance";
    }

    @Override
    @Deprecated
    public Comment addComment(final long processInstanceId, final String comment) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        try {
            tenantAccessor.getProcessInstanceService().getProcessInstance(processInstanceId);
        } catch (final SProcessInstanceReadException e) {
            throw new RetrieveException(buildCantAddCommentOnProcessInstance(), e); // FIXME: should be another exception
        } catch (final SProcessInstanceNotFoundException e) {
            throw new RetrieveException(buildCantAddCommentOnProcessInstance(), e); // FIXME: should be another exception
        }
        final SCommentService commentService = tenantAccessor.getCommentService();
        final AddComment addComment = new AddComment(commentService, processInstanceId, comment);
        try {
            addComment.execute();
            final SComment sComment = addComment.getResult();
            return ModelConvertor.toComment(sComment);
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
    }

    @Deprecated
    @Override
    public List<Comment> getComments(final long processInstanceId) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final SCommentService commentService = tenantAccessor.getCommentService();
        try {
            final List<SComment> sComments = commentService.getComments(processInstanceId);
            return ModelConvertor.toComments(sComments);
        } catch (final SBonitaReadException sbe) {
            throw new RetrieveException(sbe);
        }
    }

    @Override
    public Document attachDocument(final long processInstanceId, final String documentName, final String fileName,
            final String mimeType, final String url)
            throws DocumentAttachmentException, ProcessInstanceNotFoundException {
        return documentAPI.attachDocument(processInstanceId, documentName, fileName, mimeType, url);
    }

    @Override
    public Document attachDocument(final long processInstanceId, final String documentName, final String fileName,
            final String mimeType, final byte[] documentContent)
            throws DocumentAttachmentException, ProcessInstanceNotFoundException {
        return documentAPI.attachDocument(processInstanceId, documentName, fileName, mimeType, documentContent);
    }

    @Override
    public Document attachNewDocumentVersion(final long processInstanceId, final String documentName,
            final String fileName, final String mimeType, final String url) throws DocumentAttachmentException {
        return documentAPI.attachNewDocumentVersion(processInstanceId, documentName, fileName, mimeType, url);
    }

    @Override
    public Document attachNewDocumentVersion(final long processInstanceId, final String documentName,
            final String contentFileName, final String contentMimeType, final byte[] documentContent)
            throws DocumentAttachmentException {
        return documentAPI.attachNewDocumentVersion(processInstanceId, documentName, contentFileName,
                contentMimeType, documentContent);
    }

    @Override
    public Document getDocument(final long documentId) throws DocumentNotFoundException {
        return documentAPI.getDocument(documentId);
    }

    @Override
    public List<Document> getLastVersionOfDocuments(final long processInstanceId, final int pageIndex,
            final int numberPerPage, final DocumentCriterion pagingCriterion)
            throws DocumentException, ProcessInstanceNotFoundException {
        return documentAPI.getLastVersionOfDocuments(processInstanceId, pageIndex, numberPerPage, pagingCriterion);
    }

    @Override
    public byte[] getDocumentContent(final String documentStorageId) throws DocumentNotFoundException {
        return documentAPI.getDocumentContent(documentStorageId);
    }

    @Override
    public Document getLastDocument(final long processInstanceId, final String documentName)
            throws DocumentNotFoundException {
        return documentAPI.getLastDocument(processInstanceId, documentName);
    }

    @Override
    public long getNumberOfDocuments(final long processInstanceId) throws DocumentException {
        return documentAPI.getNumberOfDocuments(processInstanceId);
    }

    @Override
    public Document getDocumentAtProcessInstantiation(final long processInstanceId, final String documentName)
            throws DocumentNotFoundException {

        return documentAPI.getDocumentAtProcessInstantiation(processInstanceId, documentName);
    }

    @Override
    public Document getDocumentAtActivityInstanceCompletion(final long activityInstanceId,
            final String documentName) throws DocumentNotFoundException {
        return documentAPI.getDocumentAtActivityInstanceCompletion(activityInstanceId, documentName);
    }

    @Override
    public SearchResult<HumanTaskInstance> searchPendingTasksManagedBy(final long managerUserId,
            final SearchOptions searchOptions) throws SearchException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        final FlowNodeStateManager flowNodeStateManager = tenantAccessor.getFlowNodeStateManager();
        final SearchPendingTasksManagedBy searchPendingTasksManagedBy = new SearchPendingTasksManagedBy(
                activityInstanceService, flowNodeStateManager,
                searchEntitiesDescriptor.getSearchHumanTaskInstanceDescriptor(), managerUserId, searchOptions);
        try {
            searchPendingTasksManagedBy.execute();
        } catch (final SBonitaException e) {
            throw new SearchException(e);
        }
        return searchPendingTasksManagedBy.getResult();
    }

    @Override
    public Map<Long, Long> getNumberOfOverdueOpenTasks(final List<Long> userIds) throws RetrieveException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();

        try {
            return activityInstanceService.getNumberOfOverdueOpenTasksForUsers(userIds);
        } catch (final SBonitaException e) {
            log(tenantAccessor, e);
            throw new RetrieveException(e.getMessage());
        }
    }

    @Override
    public SearchResult<ProcessDeploymentInfo> searchUncategorizedProcessDeploymentInfos(
            final SearchOptions searchOptions) throws SearchException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();
        final SearchProcessDefinitionsDescriptor searchDescriptor = searchEntitiesDescriptor
                .getSearchProcessDefinitionsDescriptor();
        final SearchUncategorizedProcessDeploymentInfos transactionSearch = new SearchUncategorizedProcessDeploymentInfos(
                processDefinitionService, searchDescriptor, searchOptions);
        try {
            transactionSearch.execute();
        } catch (final SBonitaException e) {
            throw new SearchException("Problem encountered while searching for Uncategorized Process Definitions",
                    e);
        }
        return transactionSearch.getResult();
    }

    @Override
    public SearchResult<Comment> searchCommentsManagedBy(final long managerUserId,
            final SearchOptions searchOptions) throws SearchException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final SCommentService commentService = tenantAccessor.getCommentService();
        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();
        final SearchCommentsManagedBy searchComments = new SearchCommentsManagedBy(
                searchEntitiesDescriptor.getSearchCommentDescriptor(), searchOptions, commentService,
                managerUserId);
        try {
            searchComments.execute();
            return searchComments.getResult();
        } catch (final SBonitaException sbe) {
            throw new SearchException(sbe);
        }
    }

    @Override
    public SearchResult<Comment> searchCommentsInvolvingUser(final long userId, final SearchOptions searchOptions)
            throws SearchException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final SCommentService commentService = tenantAccessor.getCommentService();
        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();
        final SearchCommentsInvolvingUser searchComments = new SearchCommentsInvolvingUser(
                searchEntitiesDescriptor.getSearchCommentDescriptor(), searchOptions, commentService, userId);
        try {
            searchComments.execute();
            return searchComments.getResult();
        } catch (final SBonitaException sbe) {
            throw new SearchException(sbe);
        }
    }

    @Override
    public List<Long> getChildrenInstanceIdsOfProcessInstance(final long processInstanceId, final int startIndex,
            final int maxResults, final ProcessInstanceCriterion criterion) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessInstanceService processInstanceService = tenantAccessor.getProcessInstanceService();

        long totalNumber;
        try {
            totalNumber = processInstanceService.getNumberOfChildInstancesOfProcessInstance(processInstanceId);
            if (totalNumber == 0) {
                return Collections.emptyList();
            }
            final OrderAndField orderAndField = OrderAndFields.getOrderAndFieldForProcessInstance(criterion);
            return processInstanceService.getChildInstanceIdsOfProcessInstance(processInstanceId, startIndex,
                    maxResults, orderAndField.getField(), orderAndField.getOrder());
        } catch (final SProcessInstanceReadException e) {
            throw new RetrieveException(e);
        }
    }

    @Override
    public SearchResult<ProcessDeploymentInfo> searchUncategorizedProcessDeploymentInfosSupervisedBy(
            final long userId, final SearchOptions searchOptions) throws SearchException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();
        final SearchProcessDefinitionsDescriptor searchDescriptor = searchEntitiesDescriptor
                .getSearchProcessDefinitionsDescriptor();
        final SearchUncategorizedProcessDeploymentInfosSupervisedBy transactionSearch = new SearchUncategorizedProcessDeploymentInfosSupervisedBy(
                processDefinitionService, searchDescriptor, searchOptions, userId);
        try {
            transactionSearch.execute();
        } catch (final SBonitaException e) {
            throw new SearchException(
                    "Problem encountered while searching for Uncategorized Process Definitions for a supervisor",
                    e);
        }
        return transactionSearch.getResult();
    }

    @Override
    public Map<Long, ProcessDeploymentInfo> getProcessDeploymentInfosFromIds(
            final List<Long> processDefinitionIds) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();

        try {
            final List<SProcessDefinitionDeployInfo> processDefinitionDeployInfos = processDefinitionService
                    .getProcessDeploymentInfos(processDefinitionIds);
            final List<ProcessDeploymentInfo> processDeploymentInfos = ModelConvertor
                    .toProcessDeploymentInfo(processDefinitionDeployInfos);
            final Map<Long, ProcessDeploymentInfo> mProcessDefinitions = new HashMap<Long, ProcessDeploymentInfo>();
            for (final ProcessDeploymentInfo p : processDeploymentInfos) {
                mProcessDefinitions.put(p.getProcessId(), p);
            }
            return mProcessDefinitions;
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
    }

    @Override
    public List<ConnectorImplementationDescriptor> getConnectorImplementations(final long processDefinitionId,
            final int startIndex, final int maxsResults, final ConnectorCriterion sortingCriterion) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final ConnectorService connectorService = tenantAccessor.getConnectorService();
        final OrderAndField orderAndField = OrderAndFields
                .getOrderAndFieldForConnectorImplementation(sortingCriterion);
        final GetConnectorImplementations transactionContent = new GetConnectorImplementations(connectorService,
                processDefinitionId, tenantAccessor.getTenantId(), startIndex, maxsResults,
                orderAndField.getField(), orderAndField.getOrder());
        try {
            transactionContent.execute();
            final List<SConnectorImplementationDescriptor> sConnectorImplementationDescriptors = transactionContent
                    .getResult();
            return ModelConvertor.toConnectorImplementationDescriptors(sConnectorImplementationDescriptors);
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
    }

    @Override
    public long getNumberOfConnectorImplementations(final long processDefinitionId) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final ConnectorService connectorService = tenantAccessor.getConnectorService();
        final GetNumberOfConnectorImplementations transactionContent = new GetNumberOfConnectorImplementations(
                connectorService, processDefinitionId, tenantAccessor.getTenantId());
        try {
            transactionContent.execute();
            return transactionContent.getResult();
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
    }

    @Override
    public SearchResult<ActivityInstance> searchActivities(final SearchOptions searchOptions)
            throws SearchException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        final FlowNodeStateManager flowNodeStateManager = tenantAccessor.getFlowNodeStateManager();
        final SearchActivityInstances searchActivityInstancesTransaction = new SearchActivityInstances(
                activityInstanceService, flowNodeStateManager,
                searchEntitiesDescriptor.getSearchActivityInstanceDescriptor(), searchOptions);
        try {
            searchActivityInstancesTransaction.execute();
        } catch (final SBonitaException e) {
            throw new SearchException(e);
        }
        return searchActivityInstancesTransaction.getResult();
    }

    @Override
    public SearchResult<ArchivedFlowNodeInstance> searchArchivedFlowNodeInstances(final SearchOptions searchOptions)
            throws SearchException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        final FlowNodeStateManager flowNodeStateManager = tenantAccessor.getFlowNodeStateManager();
        final SearchArchivedFlowNodeInstances searchTransaction = new SearchArchivedFlowNodeInstances(
                activityInstanceService, flowNodeStateManager,
                searchEntitiesDescriptor.getSearchArchivedFlowNodeInstanceDescriptor(), searchOptions);
        try {
            searchTransaction.execute();
        } catch (final SBonitaException e) {
            throw new SearchException(e);
        }
        return searchTransaction.getResult();
    }

    @Override
    public SearchResult<FlowNodeInstance> searchFlowNodeInstances(final SearchOptions searchOptions)
            throws SearchException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        final FlowNodeStateManager flowNodeStateManager = tenantAccessor.getFlowNodeStateManager();
        final SearchFlowNodeInstances searchFlowNodeInstancesTransaction = new SearchFlowNodeInstances(
                activityInstanceService, flowNodeStateManager,
                searchEntitiesDescriptor.getSearchFlowNodeInstanceDescriptor(), searchOptions);
        try {
            searchFlowNodeInstancesTransaction.execute();
        } catch (final SBonitaException e) {
            throw new SearchException(e);
        }
        return searchFlowNodeInstancesTransaction.getResult();
    }

    @Override
    public SearchResult<TimerEventTriggerInstance> searchTimerEventTriggerInstances(final long processInstanceId,
            final SearchOptions searchOptions) throws SearchException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();
        final EventInstanceService eventInstanceService = tenantAccessor.getEventInstanceService();
        final SearchTimerEventTriggerInstances transaction = new SearchTimerEventTriggerInstances(
                eventInstanceService, searchEntitiesDescriptor.getSearchEventTriggerInstanceDescriptor(),
                processInstanceId, searchOptions);
        try {
            transaction.execute();
        } catch (final SBonitaException e) {
            throw new SearchException(e);
        }
        return transaction.getResult();
    }

    @Override
    public Date updateExecutionDateOfTimerEventTriggerInstance(final long timerEventTriggerInstanceId,
            final Date executionDate) throws TimerEventTriggerInstanceNotFoundException, UpdateException {
        if (executionDate == null) {
            throw new UpdateException("The date must be not null !!");
        }
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final EventInstanceService eventInstanceService = tenantAccessor.getEventInstanceService();
        final SchedulerService schedulerService = tenantAccessor.getSchedulerService();

        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();
        descriptor.addField(STimerEventTriggerInstanceBuilder.EXECUTION_DATE, executionDate.getTime());

        try {
            final STimerEventTriggerInstance sTimerEventTriggerInstance = eventInstanceService
                    .getEventTriggerInstance(STimerEventTriggerInstance.class, timerEventTriggerInstanceId);
            if (sTimerEventTriggerInstance == null) {
                throw new TimerEventTriggerInstanceNotFoundException(timerEventTriggerInstanceId);
            }
            eventInstanceService.updateEventTriggerInstance(sTimerEventTriggerInstance, descriptor);
            return schedulerService.rescheduleJob(sTimerEventTriggerInstance.getJobTriggerName(),
                    String.valueOf(getTenantAccessor().getTenantId()), executionDate);
        } catch (final SBonitaException sbe) {
            throw new UpdateException(sbe);
        }
    }

    @Override
    public SearchResult<ArchivedActivityInstance> searchArchivedActivities(final SearchOptions searchOptions)
            throws SearchException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        final FlowNodeStateManager flowNodeStateManager = tenantAccessor.getFlowNodeStateManager();
        final SearchArchivedActivityInstances searchActivityInstancesTransaction = new SearchArchivedActivityInstances(
                activityInstanceService, flowNodeStateManager,
                searchEntitiesDescriptor.getSearchArchivedActivityInstanceDescriptor(), searchOptions);
        try {
            searchActivityInstancesTransaction.execute();
        } catch (final SBonitaException e) {
            throw new SearchException(e);
        }
        return searchActivityInstancesTransaction.getResult();
    }

    @Override
    public ConnectorImplementationDescriptor getConnectorImplementation(final long processDefinitionId,
            final String connectorId, final String connectorVersion) throws ConnectorNotFoundException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final ConnectorService connectorService = tenantAccessor.getConnectorService();
        final GetConnectorImplementation transactionContent = new GetConnectorImplementation(connectorService,
                processDefinitionId, connectorId, connectorVersion, tenantAccessor.getTenantId());
        try {
            transactionContent.execute();
            final SConnectorImplementationDescriptor sConnectorImplementationDescriptor = transactionContent
                    .getResult();
            return ModelConvertor.toConnectorImplementationDescriptor(sConnectorImplementationDescriptor);
        } catch (final SBonitaException e) {
            throw new ConnectorNotFoundException(e);
        }
    }

    @Override
    public void cancelProcessInstance(final long processInstanceId)
            throws ProcessInstanceNotFoundException, UpdateException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final LockService lockService = tenantAccessor.getLockService();
        final TransactionalProcessInstanceInterruptor processInstanceInterruptor = buildProcessInstanceInterruptor(
                tenantAccessor);
        // lock process execution
        final String objectType = SFlowElementsContainerType.PROCESS.name();
        BonitaLock lock = null;
        try {
            lock = lockService.lock(processInstanceId, objectType, tenantAccessor.getTenantId());
            processInstanceInterruptor.interruptProcessInstance(processInstanceId, SStateCategory.CANCELLING,
                    getUserId());
        } catch (final SProcessInstanceNotFoundException spinfe) {
            throw new ProcessInstanceNotFoundException(processInstanceId);
        } catch (final SBonitaException e) {
            throw new UpdateException(e);
        } finally {
            // unlock process execution
            try {
                lockService.unlock(lock, tenantAccessor.getTenantId());
            } catch (final SLockException e) {
                // ignore it
            }
        }
    }

    protected TransactionalProcessInstanceInterruptor buildProcessInstanceInterruptor(
            final TenantServiceAccessor tenantAccessor) {
        final ProcessInstanceService processInstanceService = tenantAccessor.getProcessInstanceService();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        final ProcessExecutor processExecutor = tenantAccessor.getProcessExecutor();
        return new TransactionalProcessInstanceInterruptor(processInstanceService, activityInstanceService,
                processExecutor, tenantAccessor.getTechnicalLoggerService());
    }

    @Override
    public void setProcessInstanceState(final ProcessInstance processInstance, final String state)
            throws UpdateException {
        // NOW, is only available for COMPLETED, ABORTED, CANCELLED, STARTED
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final ProcessInstanceService processInstanceService = tenantAccessor.getProcessInstanceService();
        try {
            final ProcessInstanceState processInstanceState = ModelConvertor.getProcessInstanceState(state);
            final SetProcessInstanceState transactionContent = new SetProcessInstanceState(processInstanceService,
                    processInstance.getId(), processInstanceState);
            transactionContent.execute();
        } catch (final IllegalArgumentException e) {
            throw new UpdateException(e.getMessage());
        } catch (final SBonitaException e) {
            throw new UpdateException(e.getMessage());
        }
    }

    @Override
    public Map<Long, ProcessDeploymentInfo> getProcessDeploymentInfosFromProcessInstanceIds(
            final List<Long> processInstanceIds) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();

        try {
            final Map<Long, SProcessDefinitionDeployInfo> sProcessDeploymentInfos = processDefinitionService
                    .getProcessDeploymentInfosFromProcessInstanceIds(processInstanceIds);
            return ModelConvertor.toProcessDeploymentInfos(sProcessDeploymentInfos);
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
    }

    @Override
    public Map<Long, ProcessDeploymentInfo> getProcessDeploymentInfosFromArchivedProcessInstanceIds(
            final List<Long> archivedProcessInstantsIds) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();

        try {
            final Map<Long, SProcessDefinitionDeployInfo> sProcessDeploymentInfos = processDefinitionService
                    .getProcessDeploymentInfosFromArchivedProcessInstanceIds(archivedProcessInstantsIds);
            return ModelConvertor.toProcessDeploymentInfos(sProcessDeploymentInfos);
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
    }

    @Override
    public SearchResult<Document> searchDocuments(final SearchOptions searchOptions) throws SearchException {

        return documentAPI.searchDocuments(searchOptions);
    }

    @Override
    public SearchResult<Document> searchDocumentsSupervisedBy(final long userId, final SearchOptions searchOptions)
            throws SearchException, UserNotFoundException {
        return documentAPI.searchDocumentsSupervisedBy(userId, searchOptions);
    }

    @Override
    public SearchResult<ArchivedDocument> searchArchivedDocuments(final SearchOptions searchOptions)
            throws SearchException {

        return documentAPI.searchArchivedDocuments(searchOptions);
    }

    @Override
    public SearchResult<ArchivedDocument> searchArchivedDocumentsSupervisedBy(final long userId,
            final SearchOptions searchOptions) throws SearchException, UserNotFoundException {
        return documentAPI.searchArchivedDocumentsSupervisedBy(userId, searchOptions);
    }

    @Override
    public void retryTask(final long activityInstanceId) throws ActivityExecutionException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        final FlowNodeStateManager flowNodeStateManager = tenantAccessor.getFlowNodeStateManager();
        final FlowNodeExecutor flowNodeExecutor = tenantAccessor.getFlowNodeExecutor();
        final ProcessExecutor processExecutor = tenantAccessor.getProcessExecutor();
        final SAutomaticTaskInstanceBuilderFactory keyProvider = BuilderFactory
                .get(SAutomaticTaskInstanceBuilderFactory.class);

        try {
            final SFlowNodeInstance activity = activityInstanceService.getFlowNodeInstance(activityInstanceId);
            final long processDefinitionId = activity.getLogicalGroup(keyProvider.getProcessDefinitionIndex());
            final FlowNodeState flowNodeState = flowNodeStateManager.getState(activity.getStateId());
            final int stateId = activity.getPreviousStateId();
            final FlowNodeState state = flowNodeStateManager.getState(stateId);

            if (!ActivityStates.FAILED_STATE.equals(flowNodeState.getName())) {
                throw new ActivityExecutionException("Unable to retry a task that is not failed - task name="
                        + activity.getName() + " id=" + activityInstanceId + " that was in state " + flowNodeState);
            }

            flowNodeExecutor.setStateByStateId(processDefinitionId, activity.getId(), stateId);
            // execute the flow node only if it is not the final state
            if (!state.isTerminal()) {
                final long userIdFromSession = getUserId();
                // no need to handle failed state, all is in the same tx, if the node fail we just have an exception on client side + rollback
                processExecutor.executeFlowNode(activityInstanceId, null, null,
                        activity.getParentProcessInstanceId(), userIdFromSession, userIdFromSession);
            }
        } catch (final SBonitaException e) {
            throw new ActivityExecutionException(e);
        }
    }

    @Override
    public ArchivedDocument getArchivedVersionOfProcessDocument(final long sourceObjectId)
            throws ArchivedDocumentNotFoundException {

        return documentAPI.getArchivedVersionOfProcessDocument(sourceObjectId);
    }

    @Override
    public ArchivedDocument getArchivedProcessDocument(final long archivedProcessDocumentId)
            throws ArchivedDocumentNotFoundException {
        return documentAPI.getArchivedProcessDocument(archivedProcessDocumentId);
    }

    @Override
    public SearchResult<ArchivedComment> searchArchivedComments(final SearchOptions searchOptions)
            throws SearchException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();
        final SCommentService sCommentService = tenantAccessor.getCommentService();
        final SearchArchivedComments searchArchivedComments = new SearchArchivedComments(sCommentService,
                searchEntitiesDescriptor.getSearchArchivedCommentsDescriptor(), searchOptions);
        try {
            searchArchivedComments.execute();
        } catch (final SBonitaException e) {
            throw new SearchException(e);
        }
        return searchArchivedComments.getResult();
    }

    @Override
    public ArchivedComment getArchivedComment(final long archivedCommentId)
            throws RetrieveException, NotFoundException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final SCommentService sCommentService = tenantAccessor.getCommentService();
        try {
            final SAComment archivedComment = sCommentService.getArchivedComment(archivedCommentId);
            return ModelConvertor.toArchivedComment(archivedComment);
        } catch (final SCommentNotFoundException e) {
            throw new NotFoundException(e);
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
    }

    @Override
    public Map<Long, ActorInstance> getActorsFromActorIds(final List<Long> actorIds) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final Map<Long, ActorInstance> res = new HashMap<Long, ActorInstance>();
        final ActorMappingService actormappingService = tenantAccessor.getActorMappingService();
        final GetActorsByActorIds getActorsByActorIds = new GetActorsByActorIds(actormappingService, actorIds);
        try {
            getActorsByActorIds.execute();
        } catch (final SBonitaException e1) {
            throw new RetrieveException(e1);
        }
        final List<SActor> actors = getActorsByActorIds.getResult();
        for (final SActor actor : actors) {
            res.put(actor.getId(), ModelConvertor.toActorInstance(actor));
        }
        return res;
    }

    @Override
    public Serializable evaluateExpressionOnProcessDefinition(final Expression expression,
            final Map<String, Serializable> context, final long processDefinitionId)
            throws ExpressionEvaluationException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ExpressionResolverService expressionResolverService = tenantAccessor.getExpressionResolverService();

        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();
        final SExpression sExpression = ModelConvertor.constructSExpression(expression);
        final SExpressionContext expcontext = new SExpressionContext();
        expcontext.setProcessDefinitionId(processDefinitionId);
        SProcessDefinition processDef;
        try {
            processDef = processDefinitionService.getProcessDefinition(processDefinitionId);
            if (processDef != null) {
                expcontext.setProcessDefinition(processDef);
            }
            final HashMap<String, Object> hashMap = new HashMap<String, Object>(context);
            expcontext.setInputValues(hashMap);
            return (Serializable) expressionResolverService.evaluate(sExpression, expcontext);
        } catch (final SExpressionEvaluationException e) {
            throw new ExpressionEvaluationException(e, e.getExpressionName());
        } catch (final SInvalidExpressionException e) {
            throw new ExpressionEvaluationException(e, e.getExpressionName());
        } catch (final SBonitaException e) {
            throw new ExpressionEvaluationException(e, null);
        }
    }

    @Override
    public void updateDueDateOfTask(final long userTaskId, final Date dueDate) throws UpdateException {
        if (dueDate == null) {
            throw new UpdateException("Unable to update a due date to null");
        }
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        try {
            final SetExpectedEndDate updateProcessInstance = new SetExpectedEndDate(activityInstanceService,
                    userTaskId, dueDate);
            updateProcessInstance.execute();
        } catch (final SFlowNodeNotFoundException e) {
            throw new UpdateException(e);
        } catch (final SBonitaException e) {
            throw new UpdateException(e);
        }
    }

    @Override
    public long countComments(final SearchOptions searchOptions) throws SearchException {
        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 0)
                .setFilters(searchOptions.getFilters()).searchTerm(searchOptions.getSearchTerm());
        final SearchResult<Comment> searchResult = searchComments(searchOptionsBuilder.done());
        return searchResult.getCount();
    }

    @Override
    public long countAttachments(final SearchOptions searchOptions) throws SearchException {
        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 0)
                .setFilters(searchOptions.getFilters()).searchTerm(searchOptions.getSearchTerm());
        final SearchResult<Document> searchResult = documentAPI.searchDocuments(searchOptionsBuilder.done());
        return searchResult.getCount();
    }

    @Override
    public void sendSignal(final String signalName) throws SendEventException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final EventsHandler eventsHandler = tenantAccessor.getEventsHandler();
        final SThrowSignalEventTriggerDefinition signalEventTriggerDefinition = BuilderFactory
                .get(SThrowSignalEventTriggerDefinitionBuilderFactory.class).createNewInstance(signalName).done();
        try {
            eventsHandler.handleThrowEvent(signalEventTriggerDefinition);
        } catch (final SBonitaException e) {
            throw new SendEventException(e);
        }
    }

    @Override
    public void sendMessage(final String messageName, final Expression targetProcess,
            final Expression targetFlowNode, final Map<Expression, Expression> messageContent)
            throws SendEventException {
        sendMessage(messageName, targetProcess, targetFlowNode, messageContent, null);
    }

    @Override
    public void sendMessage(final String messageName, final Expression targetProcess,
            final Expression targetFlowNode, final Map<Expression, Expression> messageContent,
            final Map<Expression, Expression> correlations) throws SendEventException {
        if (correlations != null && correlations.size() > 5) {
            throw new SendEventException("Too many correlations: a message can not have more than 5 correlations.");
        }
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final EventsHandler eventsHandler = tenantAccessor.getEventsHandler();
        final ExpressionResolverService expressionResolverService = tenantAccessor.getExpressionResolverService();

        final SExpression targetProcessNameExp = ModelConvertor.constructSExpression(targetProcess);
        SExpression targetFlowNodeNameExp = null;
        if (targetFlowNode != null) {
            targetFlowNodeNameExp = ModelConvertor.constructSExpression(targetFlowNode);
        }
        final SThrowMessageEventTriggerDefinitionBuilder builder = BuilderFactory
                .get(SThrowMessageEventTriggerDefinitionBuilderFactory.class)
                .createNewInstance(messageName, targetProcessNameExp, targetFlowNodeNameExp);
        if (correlations != null && !correlations.isEmpty()) {
            addMessageCorrelations(builder, correlations);
        }
        try {
            if (messageContent != null && !messageContent.isEmpty()) {
                addMessageContent(builder, expressionResolverService, messageContent);
            }
            final SThrowMessageEventTriggerDefinition messageEventTriggerDefinition = builder.done();
            eventsHandler.handleThrowEvent(messageEventTriggerDefinition);
        } catch (final SBonitaException e) {
            throw new SendEventException(e);
        }

    }

    private void addMessageContent(
            final SThrowMessageEventTriggerDefinitionBuilder messageEventTriggerDefinitionBuilder,
            final ExpressionResolverService expressionResolverService,
            final Map<Expression, Expression> messageContent) throws SBonitaException {
        for (final Entry<Expression, Expression> entry : messageContent.entrySet()) {
            expressionResolverService.evaluate(ModelConvertor.constructSExpression(entry.getKey()));
            final SDataDefinitionBuilder dataDefinitionBuilder = BuilderFactory
                    .get(SDataDefinitionBuilderFactory.class)
                    .createNewInstance(entry.getKey().getContent(), entry.getValue().getReturnType());
            dataDefinitionBuilder.setDefaultValue(ModelConvertor.constructSExpression(entry.getValue()));
            messageEventTriggerDefinitionBuilder.addData(dataDefinitionBuilder.done());
        }

    }

    private void addMessageCorrelations(
            final SThrowMessageEventTriggerDefinitionBuilder messageEventTriggerDefinitionBuilder,
            final Map<Expression, Expression> messageCorrelations) {
        for (final Entry<Expression, Expression> entry : messageCorrelations.entrySet()) {
            messageEventTriggerDefinitionBuilder.addCorrelation(ModelConvertor.constructSExpression(entry.getKey()),
                    ModelConvertor.constructSExpression(entry.getValue()));
        }
    }

    @Override
    public List<Problem> getProcessResolutionProblems(final long processDefinitionId)
            throws ProcessDefinitionNotFoundException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();

        final List<ProcessDependencyResolver> resolvers = tenantAccessor.getDependencyResolver().getResolvers();
        SProcessDefinition processDefinition;
        try {
            processDefinition = processDefinitionService.getProcessDefinition(processDefinitionId);
        } catch (final SProcessDefinitionNotFoundException e) {
            throw new ProcessDefinitionNotFoundException(e);
        } catch (final SProcessDefinitionReadException e) {
            throw new ProcessDefinitionNotFoundException(e);
        }
        final List<Problem> problems = new ArrayList<Problem>();
        for (final ProcessDependencyResolver resolver : resolvers) {
            final List<Problem> problem = resolver.checkResolution(tenantAccessor, processDefinition);
            if (problem != null) {
                problems.addAll(problem);
            }
        }
        return problems;
    }

    @Override
    public List<ProcessDeploymentInfo> getProcessDeploymentInfos(final int startIndex, final int maxResults,
            final ProcessDeploymentInfoCriterion pagingCriterion) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();

        final SearchProcessDefinitionsDescriptor processDefinitionsDescriptor = tenantAccessor
                .getSearchEntitiesDescriptor().getSearchProcessDefinitionsDescriptor();
        final GetProcessDefinitionDeployInfos transactionContentWithResult = new GetProcessDefinitionDeployInfos(
                processDefinitionService, processDefinitionsDescriptor, startIndex, maxResults, pagingCriterion);
        try {
            transactionContentWithResult.execute();
            return transactionContentWithResult.getResult();
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
    }

    @Override
    public List<ProcessDeploymentInfo> getProcessDeploymentInfosWithActorOnlyForGroup(final long groupId,
            final int startIndex, final int maxResults, final ProcessDeploymentInfoCriterion sortingCriterion) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();

        final SearchProcessDefinitionsDescriptor processDefinitionsDescriptor = tenantAccessor
                .getSearchEntitiesDescriptor().getSearchProcessDefinitionsDescriptor();
        final GetProcessDefinitionDeployInfosWithActorOnlyForGroup transactionContentWithResult = new GetProcessDefinitionDeployInfosWithActorOnlyForGroup(
                processDefinitionService, processDefinitionsDescriptor, startIndex, maxResults, sortingCriterion,
                groupId);
        try {
            transactionContentWithResult.execute();
            return transactionContentWithResult.getResult();
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
    }

    @Override
    public List<ProcessDeploymentInfo> getProcessDeploymentInfosWithActorOnlyForGroups(final List<Long> groupIds,
            final int startIndex, final int maxResults, final ProcessDeploymentInfoCriterion sortingCriterion) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();

        final SearchProcessDefinitionsDescriptor processDefinitionsDescriptor = tenantAccessor
                .getSearchEntitiesDescriptor().getSearchProcessDefinitionsDescriptor();
        final GetProcessDefinitionDeployInfosWithActorOnlyForGroups transactionContentWithResult = new GetProcessDefinitionDeployInfosWithActorOnlyForGroups(
                processDefinitionService, processDefinitionsDescriptor, startIndex, maxResults, sortingCriterion,
                groupIds);
        try {
            transactionContentWithResult.execute();
            return transactionContentWithResult.getResult();
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
    }

    @Override
    public List<ProcessDeploymentInfo> getProcessDeploymentInfosWithActorOnlyForRole(final long roleId,
            final int startIndex, final int maxResults, final ProcessDeploymentInfoCriterion sortingCriterion) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();

        final SearchProcessDefinitionsDescriptor processDefinitionsDescriptor = tenantAccessor
                .getSearchEntitiesDescriptor().getSearchProcessDefinitionsDescriptor();
        final GetProcessDefinitionDeployInfosWithActorOnlyForRole transactionContentWithResult = new GetProcessDefinitionDeployInfosWithActorOnlyForRole(
                processDefinitionService, processDefinitionsDescriptor, startIndex, maxResults, sortingCriterion,
                roleId);
        try {

            transactionContentWithResult.execute();
            return transactionContentWithResult.getResult();
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
    }

    @Override
    public List<ProcessDeploymentInfo> getProcessDeploymentInfosWithActorOnlyForRoles(final List<Long> roleIds,
            final int startIndex, final int maxResults, final ProcessDeploymentInfoCriterion sortingCriterion) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();

        final SearchProcessDefinitionsDescriptor processDefinitionsDescriptor = tenantAccessor
                .getSearchEntitiesDescriptor().getSearchProcessDefinitionsDescriptor();
        final GetProcessDefinitionDeployInfosWithActorOnlyForRoles transactionContentWithResult = new GetProcessDefinitionDeployInfosWithActorOnlyForRoles(
                processDefinitionService, processDefinitionsDescriptor, startIndex, maxResults, sortingCriterion,
                roleIds);
        try {
            transactionContentWithResult.execute();
            return transactionContentWithResult.getResult();
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
    }

    @Override
    public List<ProcessDeploymentInfo> getProcessDeploymentInfosWithActorOnlyForUser(final long userId,
            final int startIndex, final int maxResults, final ProcessDeploymentInfoCriterion sortingCriterion) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();
        final SearchProcessDefinitionsDescriptor processDefinitionsDescriptor = tenantAccessor
                .getSearchEntitiesDescriptor().getSearchProcessDefinitionsDescriptor();
        final GetProcessDefinitionDeployInfosWithActorOnlyForUser transactionContentWithResult = new GetProcessDefinitionDeployInfosWithActorOnlyForUser(
                processDefinitionService, processDefinitionsDescriptor, startIndex, maxResults, sortingCriterion,
                userId);
        try {
            transactionContentWithResult.execute();
            return transactionContentWithResult.getResult();
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
    }

    @Override
    public List<ProcessDeploymentInfo> getProcessDeploymentInfosWithActorOnlyForUsers(final List<Long> userIds,
            final int startIndex, final int maxResults, final ProcessDeploymentInfoCriterion sortingCriterion) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();
        final SearchProcessDefinitionsDescriptor processDefinitionsDescriptor = tenantAccessor
                .getSearchEntitiesDescriptor().getSearchProcessDefinitionsDescriptor();
        final GetProcessDefinitionDeployInfosWithActorOnlyForUsers transactionContentWithResult = new GetProcessDefinitionDeployInfosWithActorOnlyForUsers(
                processDefinitionService, processDefinitionsDescriptor, startIndex, maxResults, sortingCriterion,
                userIds);
        try {
            transactionContentWithResult.execute();
            return transactionContentWithResult.getResult();
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
    }

    @Override
    public SearchResult<ConnectorInstance> searchConnectorInstances(final SearchOptions searchOptions)
            throws RetrieveException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();
        final ConnectorInstanceService connectorInstanceService = tenantAccessor.getConnectorInstanceService();
        final SearchConnectorInstances searchConnector = new SearchConnectorInstances(connectorInstanceService,
                searchEntitiesDescriptor.getSearchConnectorInstanceDescriptor(), searchOptions);
        try {
            searchConnector.execute();
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
        return searchConnector.getResult();
    }

    @Override
    public SearchResult<ArchivedConnectorInstance> searchArchivedConnectorInstances(
            final SearchOptions searchOptions) throws RetrieveException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();
        final ConnectorInstanceService connectorInstanceService = tenantAccessor.getConnectorInstanceService();
        final ArchiveService archiveService = tenantAccessor.getArchiveService();
        final ReadPersistenceService persistenceService = archiveService
                .getDefinitiveArchiveReadPersistenceService();
        final SearchArchivedConnectorInstance searchArchivedConnectorInstance = new SearchArchivedConnectorInstance(
                connectorInstanceService, searchEntitiesDescriptor.getSearchArchivedConnectorInstanceDescriptor(),
                searchOptions, persistenceService);
        try {
            searchArchivedConnectorInstance.execute();
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
        return searchArchivedConnectorInstance.getResult();
    }

    @Override
    public List<HumanTaskInstance> getHumanTaskInstances(final long processInstanceId, final String taskName,
            final int startIndex, final int maxResults) {
        try {
            final List<HumanTaskInstance> humanTaskInstances = getHumanTaskInstances(processInstanceId, taskName,
                    startIndex, maxResults, HumanTaskInstanceSearchDescriptor.PROCESS_INSTANCE_ID, Order.ASC);
            if (humanTaskInstances == null) {
                return Collections.emptyList();
            }
            return humanTaskInstances;
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
    }

    @Override
    public HumanTaskInstance getLastStateHumanTaskInstance(final long processInstanceId, final String taskName)
            throws NotFoundException {
        try {
            final List<HumanTaskInstance> humanTaskInstances = getHumanTaskInstances(processInstanceId, taskName, 0,
                    1, HumanTaskInstanceSearchDescriptor.REACHED_STATE_DATE, Order.DESC);
            if (humanTaskInstances == null || humanTaskInstances.isEmpty()) {
                throw new NotFoundException("Task '" + taskName + "' not found");
            }
            return humanTaskInstances.get(0);
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
    }

    private List<HumanTaskInstance> getHumanTaskInstances(final long processInstanceId, final String taskName,
            final int startIndex, final int maxResults, final String field, final Order order)
            throws SBonitaException {
        final SearchOptionsBuilder builder = new SearchOptionsBuilder(startIndex, maxResults);
        builder.filter(HumanTaskInstanceSearchDescriptor.PROCESS_INSTANCE_ID, processInstanceId)
                .filter(HumanTaskInstanceSearchDescriptor.NAME, taskName);
        builder.sort(field, order);
        final SearchResult<HumanTaskInstance> searchHumanTasks = searchHumanTasksTransaction(builder.done());
        return searchHumanTasks.getResult();
    }

    @Override
    public SearchResult<User> searchUsersWhoCanStartProcessDefinition(final long processDefinitionId,
            final SearchOptions searchOptions) throws SearchException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();

        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();
        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();
        final SearchUserDescriptor searchDescriptor = searchEntitiesDescriptor.getSearchUserDescriptor();
        final SearchUsersWhoCanStartProcessDeploymentInfo transactionSearch = new SearchUsersWhoCanStartProcessDeploymentInfo(
                processDefinitionService, searchDescriptor, processDefinitionId, searchOptions);
        try {
            transactionSearch.execute();
        } catch (final SBonitaException e) {
            throw new SearchException(e);
        }
        return transactionSearch.getResult();
    }

    @Override
    public Map<String, Serializable> evaluateExpressionsAtProcessInstanciation(final long processInstanceId,
            final Map<Expression, Map<String, Serializable>> expressions) throws ExpressionEvaluationException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessInstanceService processInstanceService = tenantAccessor.getProcessInstanceService();
        try {
            try {
                final SProcessInstance processInstance = processInstanceService
                        .getProcessInstance(processInstanceId);
                // if it exists and is initializing or started
                final int stateId = processInstance.getStateId();
                if (stateId == 0/* initializing */ || stateId == 1/* started */) {
                    // the evaluation date is either now (initializing) or the start date if available
                    final long evaluationDate = stateId == 0 ? System.currentTimeMillis()
                            : processInstance.getStartDate();
                    return evaluateExpressionsInstanceLevelAndArchived(expressions, processInstanceId,
                            CONTAINER_TYPE_PROCESS_INSTANCE, processInstance.getProcessDefinitionId(),
                            evaluationDate);
                }
            } catch (final SProcessInstanceNotFoundException spinfe) {
                // get it in the archive
            }
            final ArchivedProcessInstance archiveProcessInstance = getStartedArchivedProcessInstance(
                    processInstanceId);
            final Map<String, Serializable> evaluateExpressionInArchiveProcessInstance = evaluateExpressionsInstanceLevelAndArchived(
                    expressions, processInstanceId, CONTAINER_TYPE_PROCESS_INSTANCE,
                    archiveProcessInstance.getProcessDefinitionId(),
                    archiveProcessInstance.getStartDate().getTime());
            return evaluateExpressionInArchiveProcessInstance;
        } catch (final SExpressionEvaluationException e) {
            throw new ExpressionEvaluationException(e, e.getExpressionName());
        } catch (final SInvalidExpressionException e) {
            throw new ExpressionEvaluationException(e, e.getExpressionName());
        } catch (final SBonitaException e) {
            throw new ExpressionEvaluationException(e, null);
        }
    }

    @Override
    public Map<String, Serializable> evaluateExpressionOnCompletedProcessInstance(final long processInstanceId,
            final Map<Expression, Map<String, Serializable>> expressions) throws ExpressionEvaluationException {
        try {
            final ArchivedProcessInstance lastArchivedProcessInstance = getLastArchivedProcessInstance(
                    processInstanceId);
            return evaluateExpressionsInstanceLevelAndArchived(expressions, processInstanceId,
                    CONTAINER_TYPE_PROCESS_INSTANCE, lastArchivedProcessInstance.getProcessDefinitionId(),
                    lastArchivedProcessInstance.getArchiveDate().getTime());
        } catch (final SExpressionEvaluationException e) {
            throw new ExpressionEvaluationException(e, e.getExpressionName());
        } catch (final SInvalidExpressionException e) {
            throw new ExpressionEvaluationException(e, e.getExpressionName());
        } catch (final SBonitaException e) {
            throw new ExpressionEvaluationException(e, null);
        }
    }

    @Override
    public Map<String, Serializable> evaluateExpressionsOnProcessInstance(final long processInstanceId,
            final Map<Expression, Map<String, Serializable>> expressions) throws ExpressionEvaluationException {
        try {
            final SProcessInstance sProcessInstance = getSProcessInstance(processInstanceId);
            return evaluateExpressionsInstanceLevel(expressions, processInstanceId, CONTAINER_TYPE_PROCESS_INSTANCE,
                    sProcessInstance.getProcessDefinitionId());
        } catch (final SExpressionEvaluationException e) {
            throw new ExpressionEvaluationException(e, e.getExpressionName());
        } catch (final SInvalidExpressionException e) {
            throw new ExpressionEvaluationException(e, e.getExpressionName());
        } catch (final SBonitaException e) {
            throw new ExpressionEvaluationException(e, null);
        }
    }

    @Override
    public Map<String, Serializable> evaluateExpressionsOnProcessDefinition(final long processDefinitionId,
            final Map<Expression, Map<String, Serializable>> expressions) throws ExpressionEvaluationException {
        try {
            return evaluateExpressionsDefinitionLevel(expressions, processDefinitionId);
        } catch (final SExpressionEvaluationException e) {
            throw new ExpressionEvaluationException(e, e.getExpressionName());
        } catch (final SInvalidExpressionException e) {
            throw new ExpressionEvaluationException(e, e.getExpressionName());
        } catch (final SBonitaException e) {
            throw new ExpressionEvaluationException(e, null);
        }
    }

    @Override
    public Map<String, Serializable> evaluateExpressionsOnActivityInstance(final long activityInstanceId,
            final Map<Expression, Map<String, Serializable>> expressions) throws ExpressionEvaluationException {
        try {
            final SActivityInstance sActivityInstance = getSActivityInstance(activityInstanceId);
            final SProcessInstance sProcessInstance = getSProcessInstance(
                    sActivityInstance.getParentProcessInstanceId());
            return evaluateExpressionsInstanceLevel(expressions, activityInstanceId,
                    CONTAINER_TYPE_ACTIVITY_INSTANCE, sProcessInstance.getProcessDefinitionId());
        } catch (final SExpressionEvaluationException e) {
            throw new ExpressionEvaluationException(e, e.getExpressionName());
        } catch (final SInvalidExpressionException e) {
            throw new ExpressionEvaluationException(e, e.getExpressionName());
        } catch (final SBonitaException e) {
            throw new ExpressionEvaluationException(e, null);
        }
    }

    @Override
    public Map<String, Serializable> evaluateExpressionsOnCompletedActivityInstance(final long activityInstanceId,
            final Map<Expression, Map<String, Serializable>> expressions) throws ExpressionEvaluationException {
        try {
            final ArchivedActivityInstance activityInstance = getArchivedActivityInstance(activityInstanceId);
            // same archive time to process even if there're many activities in the process
            final ArchivedProcessInstance lastArchivedProcessInstance = getLastArchivedProcessInstance(
                    activityInstance.getProcessInstanceId());

            return evaluateExpressionsInstanceLevelAndArchived(expressions, activityInstanceId,
                    CONTAINER_TYPE_ACTIVITY_INSTANCE, lastArchivedProcessInstance.getProcessDefinitionId(),
                    activityInstance.getArchiveDate().getTime());
        } catch (final ActivityInstanceNotFoundException e) {
            throw new ExpressionEvaluationException(e, null);
        } catch (final SExpressionEvaluationException e) {
            throw new ExpressionEvaluationException(e, e.getExpressionName());
        } catch (final SInvalidExpressionException e) {
            throw new ExpressionEvaluationException(e, e.getExpressionName());
        } catch (final SBonitaException e) {
            throw new ExpressionEvaluationException(e, null);
        }
    }

    private Map<String, Serializable> evaluateExpressionsDefinitionLevel(
            final Map<Expression, Map<String, Serializable>> expressionsAndTheirPartialContext,
            final long processDefinitionId) throws SBonitaException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ExpressionResolverService expressionResolverService = tenantAccessor.getExpressionResolverService();
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();
        final EvaluateExpressionsDefinitionLevel evaluations = createDefinitionLevelExpressionEvaluator(
                expressionsAndTheirPartialContext, processDefinitionId, expressionResolverService,
                processDefinitionService);
        evaluations.execute();
        return evaluations.getResult();
    }

    protected EvaluateExpressionsDefinitionLevel createDefinitionLevelExpressionEvaluator(
            final Map<Expression, Map<String, Serializable>> expressionsAndTheirPartialContext,
            final long processDefinitionId, final ExpressionResolverService expressionResolverService,
            final ProcessDefinitionService processDefinitionService) {
        return new EvaluateExpressionsDefinitionLevel(expressionsAndTheirPartialContext, processDefinitionId,
                expressionResolverService, processDefinitionService,
                getTenantAccessor().getBusinessDataRepository());
    }

    private Map<String, Serializable> evaluateExpressionsInstanceLevel(
            final Map<Expression, Map<String, Serializable>> expressions, final long containerId,
            final String containerType, final long processDefinitionId) throws SBonitaException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ExpressionResolverService expressionService = tenantAccessor.getExpressionResolverService();

        final EvaluateExpressionsInstanceLevel evaluations = createInstanceLevelExpressionEvaluator(expressions,
                containerId, containerType, processDefinitionId, expressionService);
        evaluations.execute();
        return evaluations.getResult();
    }

    protected EvaluateExpressionsInstanceLevel createInstanceLevelExpressionEvaluator(
            final Map<Expression, Map<String, Serializable>> expressions, final long containerId,
            final String containerType, final long processDefinitionId,
            final ExpressionResolverService expressionService) {
        return new EvaluateExpressionsInstanceLevel(expressions, containerId, containerType, processDefinitionId,
                expressionService, getTenantAccessor().getBusinessDataRepository());
    }

    private Map<String, Serializable> evaluateExpressionsInstanceLevelAndArchived(
            final Map<Expression, Map<String, Serializable>> expressions, final long containerId,
            final String containerType, final long processDefinitionId, final long time) throws SBonitaException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ExpressionResolverService expressionService = tenantAccessor.getExpressionResolverService();
        final EvaluateExpressionsInstanceLevelAndArchived evaluations = createInstanceAndArchivedLevelExpressionEvaluator(
                expressions, containerId, containerType, processDefinitionId, time, expressionService);
        evaluations.execute();
        return evaluations.getResult();
    }

    protected EvaluateExpressionsInstanceLevelAndArchived createInstanceAndArchivedLevelExpressionEvaluator(
            final Map<Expression, Map<String, Serializable>> expressions, final long containerId,
            final String containerType, final long processDefinitionId, final long time,
            final ExpressionResolverService expressionService) {
        return new EvaluateExpressionsInstanceLevelAndArchived(expressions, containerId, containerType,
                processDefinitionId, time, expressionService, getTenantAccessor().getBusinessDataRepository());
    }

    private ArchivedProcessInstance getStartedArchivedProcessInstance(final long processInstanceId)
            throws SBonitaException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessInstanceService processInstanceService = tenantAccessor.getProcessInstanceService();
        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();

        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 2);
        searchOptionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.ARCHIVE_DATE, Order.ASC);
        searchOptionsBuilder.filter(ArchivedProcessInstancesSearchDescriptor.SOURCE_OBJECT_ID, processInstanceId);
        searchOptionsBuilder.filter(ArchivedProcessInstancesSearchDescriptor.STATE_ID,
                ProcessInstanceState.STARTED.getId());
        final SearchArchivedProcessInstances searchArchivedProcessInstances = new SearchArchivedProcessInstances(
                processInstanceService, tenantAccessor.getProcessDefinitionService(),
                searchEntitiesDescriptor.getSearchArchivedProcessInstanceDescriptor(), searchOptionsBuilder.done());
        searchArchivedProcessInstances.execute();

        try {
            return searchArchivedProcessInstances.getResult().getResult().get(0);
        } catch (final IndexOutOfBoundsException e) {
            throw new SAProcessInstanceNotFoundException(processInstanceId, ProcessInstanceState.STARTED.name());
        }

    }

    protected ArchivedProcessInstance getLastArchivedProcessInstance(final long processInstanceId)
            throws SBonitaException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessInstanceService processInstanceService = tenantAccessor.getProcessInstanceService();
        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();
        final GetLastArchivedProcessInstance searchArchivedProcessInstances = new GetLastArchivedProcessInstance(
                processInstanceService, tenantAccessor.getProcessDefinitionService(), processInstanceId,
                searchEntitiesDescriptor);

        searchArchivedProcessInstances.execute();
        return searchArchivedProcessInstances.getResult();
    }

    @Override
    public List<FailedJob> getFailedJobs(final int startIndex, final int maxResults) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final JobService jobService = tenantAccessor.getJobService();
        try {
            final List<SFailedJob> failedJobs = jobService.getFailedJobs(startIndex, maxResults);
            return ModelConvertor.toFailedJobs(failedJobs);
        } catch (final SSchedulerException sse) {
            throw new RetrieveException(sse);
        }
    }

    @Override
    public void replayFailedJob(final long jobDescriptorId) throws ExecutionException {
        replayFailedJob(jobDescriptorId, null);
    }

    @Override
    public void replayFailedJob(final long jobDescriptorId, final Map<String, Serializable> parameters)
            throws ExecutionException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final SchedulerService schedulerService = tenantAccessor.getSchedulerService();
        try {
            if (parameters == null || parameters.isEmpty()) {
                schedulerService.executeAgain(jobDescriptorId);
            } else {
                final List<SJobParameter> jobParameters = getJobParameters(parameters);
                schedulerService.executeAgain(jobDescriptorId, jobParameters);
            }
        } catch (final SSchedulerException sse) {
            throw new ExecutionException(sse);
        }
    }

    protected List<SJobParameter> getJobParameters(final Map<String, Serializable> parameters) {
        final List<SJobParameter> jobParameters = new ArrayList<SJobParameter>();
        for (final Entry<String, Serializable> parameter : parameters.entrySet()) {
            jobParameters.add(buildSJobParameter(parameter.getKey(), parameter.getValue()));
        }
        return jobParameters;
    }

    protected SJobParameter buildSJobParameter(final String parameterKey, final Serializable parameterValue) {
        return getSJobParameterBuilderFactory().createNewInstance(parameterKey, parameterValue).done();
    }

    protected SJobParameterBuilderFactory getSJobParameterBuilderFactory() {
        return BuilderFactory.get(SJobParameterBuilderFactory.class);
    }

    @Override
    public ArchivedDataInstance getArchivedProcessDataInstance(final String dataName, final long processInstanceId)
            throws ArchivedDataNotFoundException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ParentContainerResolver parentContainerResolver = tenantAccessor.getParentContainerResolver();
        final DataInstanceService dataInstanceService = tenantAccessor.getDataInstanceService();
        try {
            final SADataInstance dataInstance = dataInstanceService.getLastSADataInstance(dataName,
                    processInstanceId, DataInstanceContainer.PROCESS_INSTANCE.toString(), parentContainerResolver);
            return ModelConvertor.toArchivedDataInstance(dataInstance);
        } catch (final SDataInstanceException sdie) {
            throw new ArchivedDataNotFoundException(sdie);
        }
    }

    @Override
    public ArchivedDataInstance getArchivedActivityDataInstance(final String dataName,
            final long activityInstanceId) throws ArchivedDataNotFoundException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ParentContainerResolver parentContainerResolver = tenantAccessor.getParentContainerResolver();
        final DataInstanceService dataInstanceService = tenantAccessor.getDataInstanceService();
        try {
            final SADataInstance dataInstance = dataInstanceService.getLastSADataInstance(dataName,
                    activityInstanceId, DataInstanceContainer.ACTIVITY_INSTANCE.toString(),
                    parentContainerResolver);
            return ModelConvertor.toArchivedDataInstance(dataInstance);
        } catch (final SDataInstanceException sdie) {
            throw new ArchivedDataNotFoundException(sdie);
        }
    }

    @Override
    public List<ArchivedDataInstance> getArchivedProcessDataInstances(final long processInstanceId,
            final int startIndex, final int maxResults) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final DataInstanceService dataInstanceService = tenantAccessor.getDataInstanceService();
        try {
            final List<SADataInstance> dataInstances = dataInstanceService.getLastLocalSADataInstances(
                    processInstanceId, DataInstanceContainer.PROCESS_INSTANCE.toString(), startIndex, maxResults);
            return ModelConvertor.toArchivedDataInstances(dataInstances);
        } catch (final SDataInstanceException sdie) {
            throw new RetrieveException(sdie);
        }
    }

    @Override
    public List<ArchivedDataInstance> getArchivedActivityDataInstances(final long activityInstanceId,
            final int startIndex, final int maxResults) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final DataInstanceService dataInstanceService = tenantAccessor.getDataInstanceService();
        try {
            final List<SADataInstance> dataInstances = dataInstanceService.getLastLocalSADataInstances(
                    activityInstanceId, DataInstanceContainer.ACTIVITY_INSTANCE.toString(), startIndex, maxResults);
            return ModelConvertor.toArchivedDataInstances(dataInstances);
        } catch (final SDataInstanceException sdie) {
            throw new RetrieveException(sdie);
        }
    }

    @Override
    public List<User> getPossibleUsersOfPendingHumanTask(final long humanTaskInstanceId, final int startIndex,
            final int maxResults) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        try {
            // pagination of this method is based on order by username:
            final List<Long> userIds = activityInstanceService.getPossibleUserIdsOfPendingTasks(humanTaskInstanceId,
                    startIndex, maxResults);
            final IdentityService identityService = getTenantAccessor().getIdentityService();
            // This method below is also ordered by username, so the order is preserved:
            final List<SUser> sUsers = identityService.getUsers(userIds);
            return ModelConvertor.toUsers(sUsers);
        } catch (final SBonitaException sbe) {
            throw new RetrieveException(sbe);
        }
    }

    @Override
    public List<User> getPossibleUsersOfHumanTask(final long processDefinitionId, final String humanTaskName,
            final int startIndex, final int maxResults) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();
        try {
            final SProcessDefinition processDefinition = processDefinitionService
                    .getProcessDefinition(processDefinitionId);
            final SFlowNodeDefinition flowNode = processDefinition.getProcessContainer().getFlowNode(humanTaskName);
            if (!(flowNode instanceof SHumanTaskDefinition)) {
                return Collections.emptyList();
            }
            final SHumanTaskDefinition humanTask = (SHumanTaskDefinition) flowNode;
            final String actorName = humanTask.getActorName();
            final List<Long> userIds = getUserIdsForActor(tenantAccessor, processDefinitionId, actorName,
                    startIndex, maxResults);
            final List<SUser> users = tenantAccessor.getIdentityService().getUsers(userIds);
            return ModelConvertor.toUsers(users);
        } catch (final SProcessDefinitionNotFoundException spdnfe) {
            return Collections.emptyList();
        } catch (final SBonitaException sbe) {
            throw new RetrieveException(sbe);
        }
    }

    private List<Long> getUserIdsForActor(final TenantServiceAccessor tenantAccessor,
            final long processDefinitionId, final String actorName, final int startIndex, final int maxResults)
            throws SActorNotFoundException, SBonitaReadException {
        final ActorMappingService actorMappingService = tenantAccessor.getActorMappingService();
        final SActor actor = actorMappingService.getActor(actorName, processDefinitionId);
        return actorMappingService.getPossibleUserIdsOfActorId(actor.getId(), startIndex, maxResults);
    }

    @Override
    public List<Long> getUserIdsForActor(final long processDefinitionId, final String actorName,
            final int startIndex, final int maxResults) {
        try {
            return getUserIdsForActor(getTenantAccessor(), processDefinitionId, actorName, startIndex, maxResults);
        } catch (final SBonitaException e) {
            throw new RetrieveException(e);
        }
    }

    @Override
    public SearchResult<User> searchUsersWhoCanExecutePendingHumanTask(final long humanTaskInstanceId,
            final SearchOptions searchOptions) {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();
        final SearchUserDescriptor searchDescriptor = searchEntitiesDescriptor.getSearchUserDescriptor();
        final SearchUsersWhoCanExecutePendingHumanTaskDeploymentInfo searcher = new SearchUsersWhoCanExecutePendingHumanTaskDeploymentInfo(
                humanTaskInstanceId, activityInstanceService, searchDescriptor, searchOptions);
        try {
            searcher.execute();
        } catch (final SBonitaException sbe) {
            throw new RetrieveException(sbe);
        }
        return searcher.getResult();
    }

    @Override
    public SearchResult<HumanTaskInstance> searchAssignedAndPendingHumanTasksFor(final long rootProcessDefinitionId,
            final long userId, final SearchOptions searchOptions) throws SearchException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        final FlowNodeStateManager flowNodeStateManager = tenantAccessor.getFlowNodeStateManager();
        final SearchHumanTaskInstanceDescriptor searchDescriptor = searchEntitiesDescriptor
                .getSearchHumanTaskInstanceDescriptor();
        final SearchAssignedAndPendingHumanTasksFor searcher = new SearchAssignedAndPendingHumanTasksFor(
                activityInstanceService, flowNodeStateManager, searchDescriptor, rootProcessDefinitionId, userId,
                searchOptions);
        try {
            searcher.execute();
        } catch (final SBonitaException sbe) {
            throw new SearchException(sbe);
        }
        return searcher.getResult();
    }

    @Override
    public SearchResult<HumanTaskInstance> searchAssignedAndPendingHumanTasks(final long rootProcessDefinitionId,
            final SearchOptions searchOptions) throws SearchException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final SearchEntitiesDescriptor searchEntitiesDescriptor = tenantAccessor.getSearchEntitiesDescriptor();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        final FlowNodeStateManager flowNodeStateManager = tenantAccessor.getFlowNodeStateManager();
        final SearchHumanTaskInstanceDescriptor searchDescriptor = searchEntitiesDescriptor
                .getSearchHumanTaskInstanceDescriptor();
        final SearchAssignedAndPendingHumanTasks searcher = new SearchAssignedAndPendingHumanTasks(
                activityInstanceService, flowNodeStateManager, searchDescriptor, rootProcessDefinitionId,
                searchOptions);
        try {
            searcher.execute();
        } catch (final SBonitaException sbe) {
            throw new SearchException(sbe);
        }
        return searcher.getResult();
    }

    @Override
    public ContractDefinition getUserTaskContract(final long userTaskId) throws UserTaskNotFoundException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final ActivityInstanceService activityInstanceService = tenantAccessor.getActivityInstanceService();
        try {
            final SHumanTaskInstance taskInstance = activityInstanceService.getHumanTaskInstance(userTaskId);
            if (!(taskInstance instanceof SUserTaskInstance)) {
                throw new UserTaskNotFoundException("Impossible to find a user task with id: " + userTaskId);
            }
            final SProcessDefinition processDefinition = getTenantAccessor().getProcessDefinitionService()
                    .getProcessDefinition(taskInstance.getProcessDefinitionId());
            final SUserTaskDefinition userTask = (SUserTaskDefinition) processDefinition.getProcessContainer()
                    .getFlowNode(taskInstance.getFlowNodeDefinitionId());
            return ModelConvertor.toContract(userTask.getContract());
        } catch (final SActivityInstanceNotFoundException sainfe) {
            throw new UserTaskNotFoundException(sainfe.getMessage());
        } catch (final SProcessDefinitionNotFoundException spdnfe) {
            throw new UserTaskNotFoundException(spdnfe.getMessage());
        } catch (final SProcessDefinitionReadException spdre) {
            throw new UserTaskNotFoundException(spdre.getMessage());
        } catch (final SActivityReadException e) {
            throw new UserTaskNotFoundException(e.getMessage());
        }
    }

    @Override
    public ContractDefinition getProcessContract(final long processDefinitionId)
            throws ProcessDefinitionNotFoundException {
        try {
            final SProcessDefinition processDefinition = getTenantAccessor().getProcessDefinitionService()
                    .getProcessDefinition(processDefinitionId);
            return ModelConvertor.toContract(processDefinition.getContract());
        } catch (final SProcessDefinitionNotFoundException spdnfe) {
            throw new ProcessDefinitionNotFoundException(spdnfe.getMessage());
        } catch (final SProcessDefinitionReadException spdre) {
            throw new ProcessDefinitionNotFoundException(spdre.getMessage());
        }
    }

    @CustomTransactions
    @Override
    public void executeUserTask(final long flownodeInstanceId, final Map<String, Serializable> inputs)
            throws FlowNodeExecutionException, ContractViolationException, UserTaskNotFoundException {
        executeUserTask(0, flownodeInstanceId, inputs);
    }

    @CustomTransactions
    @Override
    public void executeUserTask(final long userId, final long flownodeInstanceId,
            final Map<String, Serializable> inputs)
            throws FlowNodeExecutionException, ContractViolationException, UserTaskNotFoundException {
        try {
            executeFlowNode(userId, flownodeInstanceId, true, inputs);
        } catch (final SFlowNodeNotFoundException e) {
            throw new UserTaskNotFoundException(e);
        } catch (final SBonitaException e) {
            throw new FlowNodeExecutionException(e);
        }
    }

    protected void executeFlowNode(final long userId, final long flownodeInstanceId,
            final boolean wrapInTransaction, final Map<String, Serializable> inputs)
            throws ContractViolationException, SBonitaException, SFlowNodeNotFoundException {
        final TenantServiceAccessor tenantAccessor = getTenantAccessor();
        final GetFlowNodeInstance getFlowNodeInstance = new GetFlowNodeInstance(
                tenantAccessor.getActivityInstanceService(), flownodeInstanceId);
        executeTransactionContent(tenantAccessor, getFlowNodeInstance, wrapInTransaction);
        final SFlowNodeInstance flowNodeInstance = getFlowNodeInstance.getResult();
        if (flowNodeInstance instanceof SUserTaskInstance) {
            throwContractViolationExceptionIfContractIsInvalid(wrapInTransaction, inputs, tenantAccessor,
                    flowNodeInstance);
        }

        final LockService lockService = tenantAccessor.getLockService();
        final BonitaLock lock = lockService.lock(flowNodeInstance.getParentProcessInstanceId(),
                SFlowElementsContainerType.PROCESS.name(), tenantAccessor.getTenantId());
        try {
            final ExecuteFlowNode executeFlowNode = new ExecuteFlowNode(tenantAccessor, userId, flowNodeInstance,
                    inputs);
            executeTransactionContent(tenantAccessor, executeFlowNode, wrapInTransaction);
        } finally {
            lockService.unlock(lock, tenantAccessor.getTenantId());
        }
    }

    private void throwContractViolationExceptionIfContractIsInvalid(final boolean wrapInTransaction,
            final Map<String, Serializable> inputs, final TenantServiceAccessor tenantAccessor,
            final SFlowNodeInstance flowNodeInstance) throws SBonitaException, ContractViolationException {
        final GetContractOfUserTaskInstance contractOfUserTaskInstance = new GetContractOfUserTaskInstance(
                tenantAccessor.getProcessDefinitionService(), (SUserTaskInstance) flowNodeInstance);
        executeTransactionContent(tenantAccessor, contractOfUserTaskInstance, wrapInTransaction);
        final SContractDefinition contractDefinition = contractOfUserTaskInstance.getResult();
        final ContractValidator validator = new ContractValidatorFactory()
                .createContractValidator(tenantAccessor.getTechnicalLoggerService());
        if (!validator.isValid(contractDefinition, inputs)) {
            throw new ContractViolationException("Contract is not valid: ", validator.getComments());
        }
    }

    @Override
    public Document removeDocument(final long documentId) throws DocumentNotFoundException, DeletionException {
        return documentAPI.removeDocument(documentId);
    }

    @Override
    public List<Document> getDocumentList(final long processInstanceId, final String name, final int from,
            final int numberOfResult) throws DocumentNotFoundException {
        return documentAPI.getDocumentList(processInstanceId, name, from, numberOfResult);
    }

    @Override
    public void setDocumentList(final long processInstanceId, final String name,
            final List<DocumentValue> documentsValues) throws DocumentException, DocumentNotFoundException {
        documentAPI.setDocumentList(processInstanceId, name, documentsValues);
    }

    @Override
    public void deleteContentOfArchivedDocument(final long archivedDocumentId)
            throws DocumentException, DocumentNotFoundException {
        documentAPI.deleteContentOfArchivedDocument(archivedDocumentId);
    }

    TenantServiceAccessor getTenantAccessor() {
        return APIUtils.getTenantAccessor();
    }

    long getUserId() {
        return APIUtils.getUserId();
    }

    @Override
    public Document addDocument(final long processInstanceId, final String documentName, final String description,
            final DocumentValue documentValue)
            throws ProcessInstanceNotFoundException, DocumentAttachmentException, AlreadyExistsException {
        return documentAPI.addDocument(processInstanceId, documentName, description, documentValue);
    }

    @Override
    public Document updateDocument(final long documentId, final DocumentValue documentValue)
            throws ProcessInstanceNotFoundException, DocumentAttachmentException, AlreadyExistsException {
        return documentAPI.updateDocument(documentId, documentValue);
    }

    @Override
    public void purgeClassLoader(final long processDefinitionId)
            throws ProcessDefinitionNotFoundException, UpdateException {
        processManagementAPIImplDelegate.purgeClassLoader(processDefinitionId);
    }

    @Override
    public Serializable getUserTaskContractVariableValue(final long userTaskInstanceId, final String name)
            throws ContractDataNotFoundException {
        final ContractDataService contractDataService = getTenantAccessor().getContractDataService();
        try {
            return (Serializable) contractDataService.getArchivedUserTaskDataValue(userTaskInstanceId, name);
        } catch (final SContractDataNotFoundException scdnfe) {
            throw new ContractDataNotFoundException(scdnfe);
        } catch (final SBonitaReadException sbe) {
            throw new RetrieveException(sbe);
        }
    }

    @Override
    public Serializable getProcessInputValueDuringInitialization(long processInstanceId, String name)
            throws ContractDataNotFoundException {
        try {
            return getTenantAccessor().getContractDataService().getProcessDataValue(processInstanceId, name);
        } catch (SContractDataNotFoundException | SBonitaReadException e) {
            throw new ContractDataNotFoundException(e);
        }
    }

    @Override
    public Serializable getProcessInputValueAfterInitialization(long processInstanceId, String name)
            throws ContractDataNotFoundException {
        try {
            return getTenantAccessor().getContractDataService().getArchivedProcessDataValue(processInstanceId,
                    name);
        } catch (SContractDataNotFoundException | SBonitaReadException e) {
            throw new ContractDataNotFoundException(e);
        }
    }

    @Override
    public int getNumberOfParameterInstances(final long processDefinitionId) {
        return processManagementAPIImplDelegate.getNumberOfParameterInstances(processDefinitionId);
    }

    @Override
    public ParameterInstance getParameterInstance(final long processDefinitionId, final String parameterName)
            throws NotFoundException {
        return processManagementAPIImplDelegate.getParameterInstance(processDefinitionId, parameterName);
    }

    @Override
    public List<ParameterInstance> getParameterInstances(final long processDefinitionId, final int startIndex,
            final int maxResults, final ParameterCriterion sort) {
        return processManagementAPIImplDelegate.getParameterInstances(processDefinitionId, startIndex, maxResults,
                sort);
    }

}