Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Further more User Tasks allow defining Swim lanes inside your process. A swim lane allows you to define multiple steps or user tasks inside your process to be automatically assigned to the first user who picks up the first task of the swim lane.

Note: Human Tasks allow setting a Task Name, which could be used to define the further visual representation or the actions available for a certain task in Magnolia. This is not the case for other Service Tasks, which is why you end up creating a handler per service task. What the Implementation field can be used for has to be clarified.

Human Task WorkItemHandler 

The handler is simply registered in the RegisterableItemsFactory using Human Task as identifier. Magnolia's 5.3 version of workflow already provides a custom class called RegisteredItemsFactory.

 

Code Block
languagejava
titleorg.jbpm.runtime.manager.impl.DefaultRegisterableItemsFactory
    public Map<String, WorkItemHandler> getWorkItemHandlers(RuntimeEngine runtime) {
	    WorkItemHandler handler = getHTWorkItemHandler(runtime);
        defaultHandlers.put("Human Task", handler);
		...
	}
 
    protected WorkItemHandler getHTWorkItemHandler(RuntimeEngine runtime) {
        
        ExternalTaskEventListener listener = new ExternalTaskEventListener();
        LocalHTWorkItemHandler humanTaskHandler = new LocalHTWorkItemHandler();

        humanTaskHandler.setRuntimeManager(((RuntimeEngineImpl)runtime).getManager());
        if (runtime.getTaskService() instanceof EventService) {
            ((EventService)runtime.getTaskService()).registerTaskEventListener(listener);
        }
		...
   }
       

...

  • create a Task based on the parameters provided by the workItem
  • create the ContentData based on parameters from the workItem (? This data is then altered by the user executing the task and later on appended as result map to the process)
  • add the task to the TaskService
  • claim the task is the task is part of a swim lane, otherwise an actor would have to manually claim it

...

At this point the WorkitemHandler is finished. It does not complete the WorkItem and for now the process is paused at this stage. Even more the whole execution of the task is decoupled from the process execution and has a completely separated life cycle. This lifecycle is taken care of by the TaskService.

Proceed execution of process after User tasks completes

Because the User Tasks lifecycle is decoupled, the process needs to be informed when a task has been completed and allow the process to proceed its execution. This is is done exactly the same way other tasks are completed inside the process by notifying the WorkItemManager (manager.completeWorkItem()), but as part of the HTWorkItemHandler it is triggered by an event fired by LifeCycleManager and handled by e.g. org.jbpm.services.task.wih.ExternalTaskEventListener#processTaskState. The Listener is registered when registering the HTWorkItemHandler inside the RegisterableItemsFactory.

org.jbpm.services.task.wih.ExternalTaskEventListener#processTaskState

TaskService

The TaskService is registered by the RuntimeManager to the RuntimeEngine. Magnolia provides it's own RuntimeManager implementation for easy registration of a custom TaskService Implementation. It's purpose is to handle the lifecycle of User Tasks, which is pretty obvious when looking at its interface:

Code Block
languagejava
titleTaskService
/**
 * The Task Service Entry Point serves as 
 *  facade of all the other services, providing a single entry point
 *  to access to all the services
 */
public interface TaskService {
    
    void activate(long taskId, String userId);
    void claim(long taskId, String userId);
    void claimNextAvailable(String userId, String language);
    void complete(long taskId, String userId, Map<String, Object> data);
    void delegate(long taskId, String userId, String targetUserId);
    void exit(long taskId, String userId);
    void fail(long taskId, String userId, Map<String, Object> faultData);
    void forward(long taskId, String userId, String targetEntityId);
    Task getTaskByWorkItemId(long workItemId);
    Task getTaskById(long taskId);
    List<TaskSummary> getTasksAssignedAsBusinessAdministrator(String userId, String language);
    List<TaskSummary> getTasksAssignedAsPotentialOwner(String userId, String language);
    List<TaskSummary> getTasksAssignedAsPotentialOwnerByStatus(String userId, List<Status> status, String language);
    List<TaskSummary> getTasksOwned(String userId, String language);
    List<TaskSummary> getTasksOwnedByStatus(String userId, List<Status> status, String language);
    List<TaskSummary> getTasksByStatusByProcessInstanceId(long processInstanceId, List<Status> status, String language);
    List<Long> getTasksByProcessInstanceId(long processInstanceId);
    
    List<TaskSummary> getTasksByVariousFields( List<Long> workItemIds, List<Long> taskIds, List<Long> procInstIds, 
            List<String> busAdmins, List<String> potOwners, List<String> taskOwners, 
            List<Status> status, boolean union);
    
    List<TaskSummary> getTasksByVariousFields(Map <String, List<?>> parameters, boolean union);
    
    long addTask(Task task, Map<String, Object> params);
    void release(long taskId, String userId);
    void resume(long taskId, String userId);
    void skip(long taskId, String userId);
    void start(long taskId, String userId);
    void stop(long taskId, String userId);
    void suspend(long taskId, String userId);
    void nominate(long taskId, String userId, List<OrganizationalEntity> potentialOwners);
    Content getContentById(long contentId);
    Attachment getAttachmentById(long attachId);
    
}

 

UML showing the default jBPM implementation

This diagram shows how User Tasks are persisted in JPA.  

Image Removed

How is a human task completed (manager.completeWorkItem()) and the process signaled to proceed?

...

Please compare this with the current implementation to see where Magnolia's implementation can easily be extended for a custom TaskService implementation.

Image Added

Possible approaches

JcrTaskPersistenceContext

By implementing a JCR implementation of the TaskPersistenceContext we would be able to create a complete storage for User Tasks. This would be the recommended approach in case we wanted to provide a complete integration of jBPM in Magnolia where customers would be able to take full advantage of jBPM and possibly also integrate 3rd party clients to connect to Magnolia's engine.

The JPATaskPersistenceContext is around 550 LOC and all it does is delegate the to the EntityManager using calls like em.merge(object) em.remove( object )


Code Block
package org.kie.internal.task.api;

public interface TaskPersistenceContext {

	Task findTask(Long taskId);
	Task persistTask(Task task);
	Task updateTask(Task task);
	Task removeTask(Task task);
	Group findGroup(String groupId);
	Group persistGroup(Group group);
    Group updateGroup(Group group);
	Group removeGroup(Group group);
	User findUser(String userId);
	User persistUser(User user);
	User updateUser(User user);
	User removeUser(User user);
	OrganizationalEntity findOrgEntity(String orgEntityId);
	OrganizationalEntity persistOrgEntity(OrganizationalEntity orgEntity);
	OrganizationalEntity updateOrgEntity(OrganizationalEntity orgEntity);
	OrganizationalEntity removeOrgEntity(OrganizationalEntity orgEntity);
	Content findContent(Long contentId);
	Content persistContent(Content content);
	Content updateContent(Content content);
	Content removeContent(Content content);
	Attachment findAttachment(Long attachmentId);
	Attachment persistAttachment(Attachment attachment);
	Attachment updateAttachment(Attachment attachment);
	Attachment removeAttachment(Attachment attachment);
	Comment findComment(Long commentId);
	Comment persistComment(Comment comment);
	Comment updateComment(Comment comment);
	Comment removeComment(Comment comment);
	Deadline findDeadline(Long deadlineId);
	Deadline persistDeadline(Deadline deadline);
	Deadline updateDeadline(Deadline deadline);
	Deadline removeDeadline(Deadline deadline);
	
	/*
	 * Query related methods
	 */
	
	...
    
    /*
     * Following are optional methods that are more like extension to 
     * default data model to allow flexible add-ons
     */
	<T> T persist(T object);
	<T> T find(Class<T> entityClass, Object primaryKey);
	<T> T remove(T entity);
    <T> T merge(T entity);
    
    /*
     * life cycle methods 
     */
    boolean isOpen();
    void joinTransaction();
    void close();
}