Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migration of unmigrated content due to installation of a new plugin

...

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

TaskService

Anchor
taskservice
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:

...

This diagram shows how User Tasks are persisted in JPA. Please compare this with the current implementation to see where Magnolia's implementation can easily be extended for a custom TaskService implementation.

Possible approaches

JcrTaskPersistenceContext

Magnolias Approach

Introduce own implementation of TaskService which delegates to Magnolia's TaskManager.

By choosing this approach we would sacrifice the idea of having 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.

Having the persistence in place still leaves questions open on how to actually interact with the tasks. We would have to access the stored tasks, and provide a limited set of possibilities to manipulate them. 

The JPATaskPersistenceContext alone 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();
}

Custom approach

Continue down the road we chose and ignore User Tasks:

<Insert magic here>

Hybrid Approach

Introduce own implementation of TaskService which delegates to Magnolia's TaskManager.

By choosing this approach we would sacrifice the idea of having a complete integration of jBPM (in case that idea ever was there..) in Magnolia. But, as part of workflow 5.3 we have laid the ground to hook into jBPM's engine at the right spots and provide custom, yet slimmed down implementations targeted towards our needs and most probably towards 97,6% of our customers needs. Still if somebody would need to have the full persistence of User Tasks as specified by jBPM registering the default CommandBasedTaskService based on a custom TaskPersistenceContext is possible, in theory.

 Image Removed

Update

During implementation we realised that this will be more complicated than necessary. While registering the TaskService to the RuntimeEngine would be nice, the gain is not much more than being a bit more consistent with jBPM. And when looking at the TaskService interface provided further up in the document, we would somehow have to wrap a lot of objects to work with our own Task's implementation, which should be part of the UI.

(in case that idea ever was there..) in Magnolia. But, as part of workflow 5.3 we have laid the ground to hook into jBPM's engine at the right spots and provide custom, yet slimmed down implementations targeted towards our needs and most probably towards 97,6% of our customers needs. Still if somebody would need to have the full persistence of User Tasks as specified by jBPM registering the default CommandBasedTaskService based on a custom TaskPersistenceContext is possible, in theory.

 Image Added

Update

During implementation we realised that this will be more complicated than necessary. While registering the TaskService to the RuntimeEngine would be nice, the gain is not much more than being a bit more consistent with jBPM. And when looking at the TaskService interface we would somehow have to wrap a lot of objects to work with our own Task's implementation, which should be part of the UI.

So instead of implementing this the TaskService interface and registering it to the RuntimeEngine we are going to create a custom TaskManager interface (naming due to consistency with MessageManager/MessageStore).

Image Added

Compared to the first diagram, the TaskManager is not registered to the Runtime, but it is injected into the HTWorkItemHandler. The LifeCycleManager which takes care of notifying the process about completed tasks is replaced by TasksEventManager, which is part of the UI project and allows registering EventHandlers. This could e.g. be used to register an handler which sends mail every time a task is created, or in our case we register an handler, which takes care of notifying the process about completed user tasks.

This approach allows a very slim integration with Magnolia's TaskManager. The whole Task Management is a standalone, reusable part of the UI project and for jBPM we communicate with the standard TaskManager interfaces using the HTWorkItemHandler and event handlers registered to the TaskEventmanager

Input from architecture meeting

3.4.2014: Task related classes should go to a separate module in main

Possible Risks and Problems

Accessing the TaskManager

Accessing the TaskManager is different than in jBPM. In jBPM the TaskService is bound to a RuntimeEngine and can be obtained like this:

long taskId = ((InternalTaskService) runtime.getTaskService()).addTask(task, content);

This will not be doable with our approach. 

Extending the Task object

How to extend the Task object by workflow specific parameters, how to store it?

 

Other 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.

Having the persistence in place still leaves questions open on how to actually interact with the tasks. We would have to access the stored tasks, and provide a limited set of possibilities to manipulate them. 

The JPATaskPersistenceContext alone 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();
}

Custom approach

Continue down the road we chose and ignore User Tasks:

<Insert magic here>

So instead of implementing this the TaskService interface and registering it to the RuntimeEngine we are going to create a custom TaskManager interface (naming due to constency with MessageManager/MessageStore).Image Added