Workflow Customization - Deep Dive

Know how workflow kick-starts for entity and possible customization points

Liferay Portal is fully connected in its core to support workflow uses. Majority of entities in Liferay have capability to utilize workflow covering Web Contents, Document & Media, Blogs, Calendar Events, Comments, KB Articles, Message boards, Page revisions, Wiki, Users, Forms etc. Liferay also provides possibilities to utilize workflow capabilities in your custom entities as well, which can be easily achieved.


How workflow can be designed… Liferay provides rich UI interface to design your workflow or get with more technical xml based generation. Versioning is also supported in workflow definitions to have more controlled management of workflow definition.


Whole workflow process works in different steps in Liferay:

  1. Workflow definition
    XML based definition which says different states, tasks, action to be performed during workflow process execution. This gives the approval flow for an entity.
     
  2. Workflow configuration
    Defined which entities/folders will fall under workflow process. Inheritance of configuration can be applied to utilize workflow uses at different levels.
     
  3. Workflow kick-start
    This starts from the individual entity’s service implementation to kick-start workflow execution. 
     
  4. Workflow engine
    Once workflow get started, workflow engine takes care of maintaining correct next actions by responsible person.


Lets dive little more deeper...

How workflow process works

Lets check high level and deep level execution for workflow process:

Modules flow in Workflow processing:

Workflow execution starts from your entity’s web module which provides option to user for submitting your entity for publication - "Submit for publication" button. Web modules calls the service module which further calls the WorkflowHandlerRegistryUtil class from portal-kernel workflow package.

 

How workflow starts for entity:

Individual entity’s local service impl is the first kickoff point for workflow, which is further handled in WorkflowHandlerRegistryUtil to actually process the kickoff request. WorkflowHandlerRegistryUtil fetches the registered workflow handler (based on class name). Flow described in below diagram helps to understand how it gets processed:

 

 

Workflow in custom entity

To enable workflow on custom entities, Liferay have enough information available in its documents/articles explaining how workflow can be enabled on custom entity. In summary, this needs to have :

  • service layer and entity’s database table with 4 required fields status, statusByUserId, statusByUserName and statusDate
  • xxxLocalServiceImpl to update above status fields
  • call WorkflowHandlerRegistryUtil.startWorkflowInstance from xxxLocalServiceImpl in addXXX method while creating the entity.
  • Have updateStatus method in xxxLocalServiceImpl to update the status of entity

 

Customization for of OOTB Liferay entities

Based on above mentioned flow for initialization of Workflow on entity, we can notice the two areas where easy implementation is possible for custom entities as it falls under the control of developer who developed the entity implementation. For already exiting Liferay out of the box entities, customization is possible on two areas, where both fulfills the different type of needs.

Customization in XXXLocalServiceImpl

This customization can be applied to control some of the limited execution/steps while having the entity creation. Example of possible customization could be additional checks for entity addition and workflow initialization. For custom entity, accordingly the implementation can be done. For OOTB entity services implementation, additional checks can be applied to persist and start the workflow.

 

Customization in WorflowHandler

When there is need a to decide on the uses of specific workflow definition to be considered for workflow initialization, workflow handler takes this activity. Workflow handler have decision power to decide which workflow definition to be used. For example, in OOTB implementation, workflow handler for journal article decide if workflow definition configured on entity level, folder level or parent folder level etc. Similarly, other simple handler decides for respective entity if workflow definition is scoped to site or even deeper level (e.g. Folder).

To customize OOTB handler, this can be achieved by registering custom handler for OOTB entities. Steps:

  • Create new component module and component class
  • Component class should have property “model.class.name” which have value as qualifier entity class name
  • Specify higher service index
  • Component class should extend BaseWorkflowHandler of type target entity class
  • Component class must return correct class name for target entity class by overriding method getClassName
  • Method getWorkflowDefinitionLink is responsible for the desired logic to take decision about which definition link to be used. This method takes three arguments companyId, groupId, classPk, which are enough to decide on the logic for correct workflow definition link.
  • If I just want to change the decision logic for definition link, I would also need to implement additional methods as well… How i can save myself to copy paste the code?
    • If we provide our custom handler over OOTB workflow handler for just particular decision on workflow definition link, we may need to have additional methods implementation as well similar to OOTB code. We can overcome this problem by taking reference of original OOTB workflow handler and just call the methods of this. Reference of original OOTB workflow handler can be taken using osgi reference target filtering for model class name and component name. Below snippet can be considered as reference for using OOTB workflow handler for journal articles

@Reference(target = "(&(model.class.name=com.liferay.journal.model.JournalArticle)"
    + "(component.name=com.liferay.journal.internal.workflow.JournalArticleWorkflowHandler))")
protected void setOriginalJournalWorkflowHandler(WorkflowHandler<JournalArticle> originlJournalWorkflowHandler) {
    this.originlJournalWorkflowHandler = originlJournalWorkflowHandler;
}

By using original OOTB component reference, hassle of implementation additional methods can be avoided and on same time, implementation becomes upgrade friendly and low complexity on maintenance.

Some use cases could be, having different workflow based on:

  • Applying workflow if it is not provided OOTB for particular entity
  • Advanced logic to decide for workflow (Any naming convention)
  • Specific definition for specific value in entity field

With above points, we can customize on workflow kickoff and initialization of expected workflow definition.

In next blog, I will be explaining about how specific task execution works in workflow and how we can visualize that for customization/additions.

Photo Credit: Photo by Jakob Owens