Implement a JSF Task Form for a BPMN User Task.

Now we add a task form and configure it in the BPMN 2.0 process, then, re-deploy the application and go to the Camunda Tasklist to see the JSF form.

After the order has been persisted, a user can approve the order. For that, a task form is needed to display the order information and backend logic to fetch and update business objects.

Add a CDI Controller Bean

To update the persisted entity we use a named CDI Bean ApproveOrderController. To gather the persisted order entity, we get the order id from the process variables of the businessProcess. With the id we can load the order entity through the order business logic. After the order has been updated, the detached entity state is merged by the order business logic.

package org.camunda.bpm.getstarted.pizza;

import org.camunda.bpm.engine.cdi.BusinessProcess;
import org.camunda.bpm.engine.cdi.jsf.TaskForm;

import javax.enterprise.context.ConversationScoped;
import javax.inject.Inject;
import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.io.IOException;
import java.io.Serializable;

@Named
@ConversationScoped
public class ApproveOrderController implements Serializable {

  private static  final long serialVersionUID = 1L;

  // Inject the BusinessProcess to access the process variables
  @Inject
  private BusinessProcess businessProcess;

  // Inject the EntityManager to access the persisted order
  @PersistenceContext
  private EntityManager entityManager;

  // Inject the OrderBusinessLogic to update the persisted order
  @Inject
  private OrderBusinessLogic orderBusinessLogic;

  // Caches the OrderEntity during the conversation
  private OrderEntity orderEntity;

  public OrderEntity getOrderEntity() {
    if (orderEntity == null) {
      // Load the order entity from the database if not already cached
      orderEntity = orderBusinessLogic.getOrder((Long) businessProcess.getVariable("orderId"));
    }
    return orderEntity;
  }

  public void submitForm() throws IOException {
    // Persist updated order entity and complete task form
    orderBusinessLogic.mergeOrderAndCompleteTask(orderEntity);
  }
}

Extend Order Business Logic

The order business logic is extended to provide a method to load an order entity from the database by order id, to merge a detached order entity and to complete the task form. For that, the task form is injected, which is provided by the Camunda CDI artifact.

Please note that the merging of the detached order entity and the completion of the task form are intentionally placed in one method. This ensures that both operations are executed in a single transaction. An error during that transaction will rollback both changes.

@Stateless
@Named
public class OrderBusinessLogic {

  // ...

  // Inject task form available through the Camunda cdi artifact
  @Inject
  private TaskForm taskForm;

  public void persistOrder(DelegateExecution delegateExecution) {
    // ...
  }

  public OrderEntity getOrder(Long orderId) {
    // Load order entity from database
    return entityManager.find(OrderEntity.class, orderId);
  }

  /*
    Merge updated order entity and complete task form in one transaction. This ensures
    that both changes will rollback if an error occurs during transaction.
   */
  public void mergeOrderAndCompleteTask(OrderEntity orderEntity) {
    // Merge detached order entity with current persisted state
    entityManager.merge(orderEntity);
    try {
      // Complete user task from
      taskForm.completeTask();
    } catch (IOException e) {
      // Rollback both transactions on error
      throw new RuntimeException("Cannot complete task", e);
    }
  }

}

Create a JSF Form Task Form

Add a file named approveorder.xhtml to the src/main/webapp folder. Add the following content:

<!DOCTYPE HTML>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"
  xmlns:ui="http://java.sun.com/jsf/facelets"
  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:f="http://java.sun.com/jsf/core">

  <f:view>
    <f:metadata>
      <!-- Start working on a task. Task Id is read internally from
       request parameters and cached in the CDI conversation scope.
      -->

    <f:event type="preRenderView" listener="#{camundaTaskForm.startTaskForm()}" />
  </f:metadata>
  <h:head>
    <title>Approve Order</title>
  </h:head>
  <h:body>
    <h1>Order:</h1>
    <p>Customer: #{approveOrderController.orderEntity.customer}</p>
    <p>Address: #{approveOrderController.orderEntity.address}</p>
    <p>Pizza: #{approveOrderController.orderEntity.pizza}</p>
    <h:form id="submitForm">
      <h:outputLabel>Approve Order?</h:outputLabel>
      <h:selectBooleanCheckbox value="#{approveOrderController.orderEntity.approved}"/><br/>
      <h:commandButton id="submit_button" value="Approve Order" action="#{approveOrderController.submitForm()}" />
    </h:form>
  </h:body>
  </f:view>
</html>

The JSF view displays the order properties and provides a checkbox to approve the order on submit. Additionally, an event listener is configured which is triggered before the view is rendered. It will call the camundaTaskForm.startTaskForm() method which extracts the task id from the URL and starts a conversation for the task form.

When the user approves or disapproves the order, it is directly set on the cached order entity.

On form submit, the approveOrderController.submitForm() method calls the EJB mergeOrderAndCompleteTask method with the cached order entity. The EJB will merge the updated order entity if necessary and completes the task form.

Configure the Task Form in the Process

Open the process with the modeler. Click on the Approve Order user task. In the properties view, set the Form Key property to app:approveorder.jsf. This means that we want to use an external JSF form and that the form is loaded from the application.

Configure the Conditional Sequence Flows in the Process

Open the process with the modeler. In the properties view, set the Condition property of the conditional sequence flows after the exclusive gateway to ${orderBusinessLogic.getOrder(orderId).approved} respectively ${not orderBusinessLogic.getOrder(orderId).approved}.

When you are done, save all resources, perform a Maven build, and redeploy the process application. Start the process Order Pizza in Tasklist. Fill out the emerging form. Select the All Tasks element on the left side of Tasklist. An Approve Order task should then be listed in the tasklist. Go to the task and press on . Now you can approve the pizza order.

Catch up: Get the Sources of Step-6.

Download as .zip or checkout the corresponding tag with Git.

You can checkout the current state from the GitHub repository.

If you have not cloned the repository yet, please execute the following command:

git clone https://github.com/camunda/camunda-get-started-javaee.git

To checkout the current state of the process application please execute the following command:

git checkout -f Step-6
Or download as archive from here.

On this Page: