All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.flowable.cdi.BusinessProcess Maven / Gradle / Ivy

The newest version!
/* Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.flowable.cdi;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

import jakarta.enterprise.context.Conversation;
import jakarta.enterprise.context.RequestScoped;
import jakarta.enterprise.inject.Instance;
import jakarta.inject.Inject;
import jakarta.inject.Named;

import org.flowable.cdi.annotation.BusinessProcessScoped;
import org.flowable.cdi.impl.context.ContextAssociationManager;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.common.engine.impl.context.Context;
import org.flowable.engine.ProcessEngine;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;

/**
 * Bean supporting contextual business process management. This allows us to implement a unit of work, in which a particular CDI scope (Conversation / Request / Thread) is associated with a particular
 * Execution / ProcessInstance or Task.
 * 

* The protocol is that we associate the {@link BusinessProcess} bean with a particular Execution / Task, then perform some changes (retrieve / set process variables) and then end the unit of * work. This bean makes sure that our changes are only "flushed" to the process engine when we successfully complete the unit of work. *

* A typical usage scenario might look like this:
* 1st unit of work ("process instantiation"): *

*

 * conversation.begin();
 * ...
 * businessProcess.setVariable("billingId", "1"); // setting variables before starting the process
 * businessProcess.startProcessByKey("billingProcess");
 * conversation.end();
 * 
*

* 2nd unit of work ("perform a user task"): *

*

 * conversation.begin();
 * businessProcess.startTask(id); // now we have associated a task with the current conversation
 * ...                            // this allows us to retrieve and change process variables
 *                                // and @BusinessProcessScoped beans
 * businessProcess.setVariable("billingDetails", "someValue"); // these changes are cached in the conversation
 * ...
 * businessProcess.completeTask(); // now all changed process variables are flushed
 * conversation.end();
 * 
*

* NOTE: in the absence of a conversation, (non faces request, i.e. when processing a JAX-RS, JAX-WS, JMS, remote EJB or plain Servlet requests), the {@link BusinessProcess} bean * associates with the current Request (see {@link RequestScoped @RequestScoped}). *

* NOTE: in the absence of a request, ie. when the JobExecutor accesses {@link BusinessProcessScoped @BusinessProcessScoped} beans, the execution is associated with the current * thread. * * @author Daniel Meyer * @author Falko Menge */ @Named public class BusinessProcess implements Serializable { private static final long serialVersionUID = 1L; @Inject private ProcessEngine processEngine; @Inject private ContextAssociationManager associationManager; @Inject private Instance conversationInstance; protected void validateValidUsage() { if (Context.getCommandContext() != null) { throw new FlowableCdiException("Cannot use this method of the BusinessProcess bean within an active command."); } } public ProcessInstance startProcessById(String processDefinitionId) { validateValidUsage(); ProcessInstance instance = processEngine.getRuntimeService().startProcessInstanceById(processDefinitionId, getAndClearCachedVariables()); if (!instance.isEnded()) { setExecution(instance); } return instance; } public ProcessInstance startProcessById(String processDefinitionId, String businessKey) { validateValidUsage(); ProcessInstance instance = processEngine.getRuntimeService().startProcessInstanceById(processDefinitionId, businessKey, getAndClearCachedVariables()); if (!instance.isEnded()) { setExecution(instance); } return instance; } public ProcessInstance startProcessById(String processDefinitionId, Map variables) { validateValidUsage(); Map cachedVariables = getAndClearCachedVariables(); cachedVariables.putAll(variables); ProcessInstance instance = processEngine.getRuntimeService().startProcessInstanceById(processDefinitionId, cachedVariables); if (!instance.isEnded()) { setExecution(instance); } return instance; } public ProcessInstance startProcessById(String processDefinitionId, String businessKey, Map variables) { validateValidUsage(); Map cachedVariables = getAndClearCachedVariables(); cachedVariables.putAll(variables); ProcessInstance instance = processEngine.getRuntimeService().startProcessInstanceById(processDefinitionId, businessKey, cachedVariables); if (!instance.isEnded()) { setExecution(instance); } return instance; } public ProcessInstance startProcessByKey(String key) { validateValidUsage(); ProcessInstance instance = processEngine.getRuntimeService().startProcessInstanceByKey(key, getAndClearCachedVariables()); if (!instance.isEnded()) { setExecution(instance); } return instance; } public ProcessInstance startProcessByKey(String key, String businessKey) { validateValidUsage(); ProcessInstance instance = processEngine.getRuntimeService().startProcessInstanceByKey(key, businessKey, getAndClearCachedVariables()); if (!instance.isEnded()) { setExecution(instance); } return instance; } public ProcessInstance startProcessByKey(String key, Map variables) { validateValidUsage(); Map cachedVariables = getAndClearCachedVariables(); cachedVariables.putAll(variables); ProcessInstance instance = processEngine.getRuntimeService().startProcessInstanceByKey(key, cachedVariables); if (!instance.isEnded()) { setExecution(instance); } return instance; } public ProcessInstance startProcessByKey(String key, String businessKey, Map variables) { validateValidUsage(); Map cachedVariables = getAndClearCachedVariables(); cachedVariables.putAll(variables); ProcessInstance instance = processEngine.getRuntimeService().startProcessInstanceByKey(key, businessKey, cachedVariables); if (!instance.isEnded()) { setExecution(instance); } return instance; } public ProcessInstance startProcessByMessage(String messageName) { validateValidUsage(); Map cachedVariables = getAndClearCachedVariables(); ProcessInstance processInstance = processEngine.getRuntimeService().startProcessInstanceByMessage(messageName, cachedVariables); if (!processInstance.isEnded()) { setExecution(processInstance); } return processInstance; } public ProcessInstance startProcessByMessage(String messageName, Map processVariables) { validateValidUsage(); Map cachedVariables = getAndClearCachedVariables(); cachedVariables.putAll(processVariables); ProcessInstance processInstance = processEngine.getRuntimeService().startProcessInstanceByMessage(messageName, cachedVariables); if (!processInstance.isEnded()) { setExecution(processInstance); } return processInstance; } public ProcessInstance startProcessByMessage(String messageName, String businessKey, Map processVariables) { validateValidUsage(); Map cachedVariables = getAndClearCachedVariables(); cachedVariables.putAll(processVariables); ProcessInstance processInstance = processEngine.getRuntimeService().startProcessInstanceByMessage(messageName, businessKey, cachedVariables); if (!processInstance.isEnded()) { setExecution(processInstance); } return processInstance; } /** * @deprecated */ @Deprecated public ProcessInstance startProcessByName(String string) { if (Context.getCommandContext() != null) { throw new FlowableCdiException("Cannot use startProcessByName in an active command."); } ProcessDefinition definition = processEngine.getRepositoryService().createProcessDefinitionQuery().processDefinitionName(string).singleResult(); if (definition == null) { throw new FlowableObjectNotFoundException("No process definition found for name: " + string, ProcessDefinition.class); } ProcessInstance instance = processEngine.getRuntimeService().startProcessInstanceById(definition.getId(), getAndClearCachedVariables()); if (!instance.isEnded()) { setExecution(instance); } return instance; } /** * @deprecated */ @Deprecated public ProcessInstance startProcessByName(String string, Map variables) { if (Context.getCommandContext() != null) { throw new FlowableCdiException("Cannot use startProcessByName in an active command."); } ProcessDefinition definition = processEngine.getRepositoryService().createProcessDefinitionQuery().processDefinitionName(string).singleResult(); if (definition == null) { throw new FlowableObjectNotFoundException("No process definition found for name: " + string, ProcessDefinition.class); } Map cachedVariables = getAndClearCachedVariables(); cachedVariables.putAll(variables); ProcessInstance instance = processEngine.getRuntimeService().startProcessInstanceById(definition.getId(), cachedVariables); if (!instance.isEnded()) { setExecution(instance); } return instance; } /** * Associate with the provided execution. This starts a unit of work. * * @param executionId the id of the execution to associate with. * @throws FlowableCdiException if no such execution exists */ public void associateExecutionById(String executionId) { Execution execution = processEngine.getRuntimeService().createExecutionQuery().executionId(executionId).singleResult(); if (execution == null) { throw new FlowableCdiException("Cannot associate execution by id: no execution with id '" + executionId + "' found."); } associationManager.setExecution(execution); } /** * returns true if an {@link Execution} is associated. * * @see #associateExecutionById(String) */ public boolean isAssociated() { return associationManager.getExecutionId() != null; } /** * Signals the current execution, see {@link RuntimeService#trigger(String)} *

* Ends the current unit of work (flushes changes to process variables set using {@link #setVariable(String, Object)} or made on {@link BusinessProcessScoped @BusinessProcessScoped} beans). * * @throws FlowableCdiException if no execution is currently associated * @throws FlowableException if the command fails */ public void triggerExecution() { assertAssociated(); processEngine.getRuntimeService().trigger(associationManager.getExecutionId(), getAndClearCachedVariables()); associationManager.disAssociate(); } /** * @see #triggerExecution() *

* In addition, this method allows to end the current conversation */ public void triggerExecution(boolean endConversation) { triggerExecution(); if (endConversation) { conversationInstance.get().end(); } } // ------------------------------------- /** * Associates the task with the provided taskId with the current conversation. * * @param taskId the id of the task * @return the resumed task * @throws FlowableCdiException if no such task is found */ public Task startTask(String taskId) { Task currentTask = associationManager.getTask(); if (currentTask != null && currentTask.getId().equals(taskId)) { return currentTask; } Task task = processEngine.getTaskService().createTaskQuery().taskId(taskId).singleResult(); if (task == null) { throw new FlowableCdiException("Cannot resume task with id '" + taskId + "', no such task."); } associationManager.setTask(task); associateExecutionById(task.getProcessInstanceId()); return task; } /** * @see #startTask(String) *

* this method allows to start a conversation if no conversation is active */ public Task startTask(String taskId, boolean beginConversation) { if (beginConversation) { Conversation conversation = conversationInstance.get(); if (conversation.isTransient()) { conversation.begin(); } } return startTask(taskId); } /** * Completes the current UserTask, see {@link TaskService#complete(String)} *

* Ends the current unit of work (flushes changes to process variables set using {@link #setVariable(String, Object)} or made on {@link BusinessProcessScoped @BusinessProcessScoped} beans). * * @throws FlowableCdiException if no task is currently associated * @throws FlowableException if the command fails */ public void completeTask() { assertTaskAssociated(); processEngine.getTaskService().complete(getTask().getId(), getAndClearCachedVariables()); associationManager.disAssociate(); } /** * @see BusinessProcess#completeTask() *

* In addition this allows to end the current conversation. */ public void completeTask(boolean endConversation) { completeTask(); if (endConversation) { conversationInstance.get().end(); } } public boolean isTaskAssociated() { return associationManager.getTask() != null; } // ------------------------------------------------- /** * @param variableName the name of the process variable for which the value is to be retrieved * @return the value of the provided process variable or 'null' if no such variable is set */ @SuppressWarnings("unchecked") public T getVariable(String variableName) { Object variable = associationManager.getVariable(variableName); if (variable == null) { return null; } else { return (T) variable; } } /** * Set a value for a process variable. *

*

* NOTE: If no execution is currently associated, the value is temporarily cached and flushed to the process instance at the end of the unit of work * * @param variableName the name of the process variable for which a value is to be set * @param value the value to be set */ public void setVariable(String variableName, Object value) { associationManager.setVariable(variableName, value); } // ----------------------------------- Getters / Setters /* * Note that Producers should go into {@link CurrentProcessInstance} in order to allow for specializing {@link BusinessProcess}. */ /** * @see #startTask(String) */ public void setTask(Task task) { startTask(task.getId()); } /** * @see #startTask(String) */ public void setTaskId(String taskId) { startTask(taskId); } /** * @see #associateExecutionById(String) */ public void setExecution(Execution execution) { associateExecutionById(execution.getId()); } /** * @see #associateExecutionById(String) */ protected void setExecutionId(String executionId) { associateExecutionById(executionId); } /** * Returns the id of the currently associated process instance or 'null' */ public String getProcessInstanceId() { Execution execution = associationManager.getExecution(); return execution != null ? execution.getProcessInstanceId() : null; } /** * Returns the id of the task associated with the current conversation or 'null'. */ public String getTaskId() { Task task = getTask(); return task != null ? task.getId() : null; } /** * Returns the currently associated {@link Task} or 'null' * * @throws FlowableCdiException if no {@link Task} is associated. Use {@link #isTaskAssociated()} to check whether an association exists. */ public Task getTask() { return associationManager.getTask(); } /** * Returns the currently associated execution or 'null' */ public Execution getExecution() { return associationManager.getExecution(); } /** * @see #getExecution() */ public String getExecutionId() { Execution e = getExecution(); return e != null ? e.getId() : null; } /** * Returns the {@link ProcessInstance} currently associated or 'null' * * @throws FlowableCdiException if no {@link Execution} is associated. Use {@link #isAssociated()} to check whether an association exists. */ public ProcessInstance getProcessInstance() { Execution execution = getExecution(); if (execution != null && !(execution.getProcessInstanceId().equals(execution.getId()))) { return processEngine.getRuntimeService().createProcessInstanceQuery().processInstanceId(execution.getProcessInstanceId()).singleResult(); } return (ProcessInstance) execution; } // internal implementation // ////////////////////////////////////////////////////////// protected void assertAssociated() { if (associationManager.getExecution() == null) { throw new FlowableCdiException("No execution associated. Call businessProcess.associateExecutionById() or businessProcess.startTask() first."); } } protected void assertTaskAssociated() { if (associationManager.getTask() == null) { throw new FlowableCdiException("No task associated. Call businessProcess.startTask() first."); } } protected Map getCachedVariables() { return associationManager.getCachedVariables(); } protected Map getAndClearCachedVariables() { Map beanStore = getCachedVariables(); Map copy = new HashMap<>(beanStore); beanStore.clear(); return copy; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy