Versions Compared

Key

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

...

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 )

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 provided further down 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.

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.

Possible Risks and Problems

  • Access to the TaskManager different than in jBPM.
  • 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(
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.

...

(

...

);
}

Custom approach

Continue down the road we chose and ignore User Tasks:

<Insert magic here>

 

Image Removed

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.