Flowable actual form and process variables

Posted by Tubby on Wed, 12 Jan 2022 22:11:44 +0100

1, Process variables

  when a process instance executes step by step, some data needs to be saved and used. In Flowable, these data are called variable s.

  process instances can hold variables called process variables.

   in order to use efficiency, Flowable divides variables into two types: runtime variables and historical variables.

1.1 runtime variables

   variables of process instance runtime, stored in act_ru_variable table. At the end of the process instance, the variables of this instance are deleted in the table.

  process variables can be set when creating and starting process instances. All startProcessInstanceXXX methods have an optional parameter for setting variables. For example, in the RuntimeService:

    ProcessInstance startProcessInstanceByKey(String processDefinitionKey, Map<String, Object> variables);

  you can also add variables in process execution. For example, (RuntimeService):

    void setVariable(String executionId, String variableName, Object value);
    void setVariableLocal(String executionId, String variableName, Object value);
    void setVariables(String executionId, Map<String, ? extends Object> variables);
    void setVariablesLocal(String executionId, Map<String, ? extends Object> variables);

   read variable method (please note that there are similar methods in TaskService. This means that the task can hold local variables like execution, and its lifetime is the duration of the task.)

    Map<String, Object> getVariables(String executionId);
    Map<String, Object> getVariablesLocal(String executionId);
    Map<String, Object> getVariables(String executionId, Collection<String> variableNames);
    Map<String, Object> getVariablesLocal(String executionId, Collection<String> variableNames);
    Object getVariable(String executionId, String variableName);
    <T> T getVariable(String executionId, String variableName, Class<T> variableClass);

Note: when the process instance ends, the data corresponding to the runtime table will be deleted. Therefore, querying the variables of a completed process instance can only be found in the history variable table.

1.2 historical variables

  historical variable, stored in Act_ hi_ In the varinst table. When the process starts, the process variables will be stored in the history variable table at the same time; At the end of the process, the variables in the history table still exist. Process variables that can be understood as "permanent generation".

    obtain all historical variable instances in the completed process instance with id 'XXX', and sort them by variable name.

    historyService.createHistoricVariableInstanceQuery()
      .processInstanceId("XXX")
      .orderByVariableName.desc()
      .list();

2, Form

   in the actual business, the process is accompanied by a variety of forms. The Flowable engine stores the form data as process variables in the variable table. Therefore, for the Flowable engine, it can run completely independent of the form, because the process variable can be used to represent the single data.

  but generally, we need structured data, and forms are still our recommended usage.

  there are two methods for form definition, built-in form and external form.

2.1 built in forms

  taking leave as an example, the XML content:

<process id="leave" name="Leave process-Built in form">
    <startEvent id="start">
          <extensionElements>
            <flowable:formProperty id="startDate" name="Leave start event" type="date" 
              datePattern="dd-MMM-yyyy" required="true" readable="true" writeable="true"/>
            <flowable:formProperty id="endDate" name="End event of leave" type="date" 
              datePattern="dd-MMM-yyyy" required="true" readable="true" writeable="true"/>
            <flowable:formProperty id="reason" name="Reason for leave" type="string" 
              required="true" readable="true" writeable="true"/>
            <flowable:formProperty id="leaveType" type="enum" name="Leave type">
              <flowable:value id="personalLeave" name="compassionate leave" />
              <flowable:value id="annualLeave" name="annual leave" />
            </flowable:formProperty>
          </extensionElements>
    </startEvent>
</process>

  usage:

    StartFormData FormService.getStartFormData(String processDefinitionId)

  or

    TaskFormData FormService.getTaskFormData(String taskId)

   you can understand the built-in forms. The practical application is more about using external forms.

2.2 external forms

   a task form rendered by itself according to the form file is called an external form.

2.2.1 XML content

    <process id="leave" name="Leave process-Built in form">
        <startEvent id="start" flowable:formKey="form1"></startEvent>
    </process>

Note: "form1" in flowable:formKey="form1" corresponds to the "key" value of the form definition file.

2.2.2 form definition

  • The suffix of the form definition file is form.
  • The JSON definition of the form starts with key, name, and description.
  • The form engine uses the attribute key to identify the unique identity of the form in the whole form engine. For versions of the same form definition from the same source, the system also operates based on the attribute key.
  • The second part is an array type fields. The fields defined by the form are clarified here.
  • The third part is optional and is used to define the result outcomes of the form. Examples are as follows:
{
    "key": "form1",
    "name": "My first form",
    "fields": [
        {
            "id": "input1",
            "name": "Input1",
            "type": "text",
            "required": false,
            "placeholder": "empty"
        }
    ],
    "outcomes": [
        {
            "id": "null",
            "name": "Accept"
        },
        {
            "id": "null",
            "name": "Reject"
        }
    ]
}

2.2.3 deployment form

  in the springboot environment, the resources/forms directory is not available Form definition files with form suffix are automatically deployed.

  for example, save the 2.2.2 form definition content as leave Form file and put it in the resources/forms directory.

Note: in practical application, the front-end process designer should generate a form definition file in the specified format, and update and deploy the process definition and form definition resources through the interface method mentioned above.

2.2.4 obtaining and submitting form parameters

  in fact, all the data required to render the form is assembled in the following two methods:

    StartFormData FormService.getStartFormData(String processDefinitionId)
    TaskFormdata FormService.getTaskFormData(String taskId)

  you can submit form parameters through the following two methods:

    ProcessInstance FormService.submitStartFormData(String processDefinitionId, Map<String,String> properties)
    void FormService.submitTaskFormData(String taskId, Map<String,String> properties)

  specific information of form parameter FormProperty:

public interface FormProperty {
  /**
   * In {@ link FormService#submitStartFormData(String, java.util.Map)}
   * Or {@ link FormService#submitTaskFormData(String, java.util.Map)}
   * key used when submitting parameters in
   */
  String getId();

  /** Display label */
  String getName();

  /** Types defined in this interface, such as {@ link #TYPE_STRING} */
  FormType getType();

  /** Optional. The value to be displayed for this parameter */
  String getValue();

  /** Whether this parameter can be read: it is displayed in the form and can be read through
   * {@link FormService#getStartFormData(String)}
   * With {@ link FormService#getTaskFormData(String)}
   * Method access.
   */
  boolean isReadable();

  /** Can users include this parameter when submitting forms? */
  boolean isWritable();

  /** Is this parameter required in the input box */
  boolean isRequired();
}

2.2.5 obtaining and submitting form data

  how to obtain the form data of the specified process instance:

    FormModel RuntimeService.getStartFormModel(String processDefinitionId, String processInstanceId);

  method of submitting form data:

    // Start process instance with form data
    ProcessInstance RuntimeService.startProcessInstanceWithForm(String processDefinitionId, String outcome, Map<String,Object> properties, String taskName);
    // Complete the task with form data
    void TaskService.completeTaskWithForm(String taskId, String formDefinitionId, String outcome, Map<String,Object> properties);

   the form data is actually stored in the process variable table, so the form data can also be obtained and submitted by using the process variable method.

2.3 form type fields

  the form supports the following types of fields

  • Text: text field
  • Multi line text: multi line text field
  • integer: a text field, but only numeric values are allowed
  • boolean: check box field
  • date: date field
  • dropdown: select the box field. You can set the option value when defining the field
  • Radio buttons: a radio field. When defining a field, you can set the value of the option
  • people: in the selection box field, you can select a user in the user table
  • Functional group: select a group in the grouping table in the selection box field
  • Upload: upload file field
  • expression: a label in which you can use JUEL expressions to manipulate variables or other dynamic values

2.4 custom form field types

  in practical applications, the form field types provided by Flowable can not fully meet the requirements. We often need to customize the form field types.

  all custom field types need to inherit an expression type abstract class "org.flowable.engine.form.AbstractFormType".

  for example, define a "card" user-defined type:

public class CardFormType extends AbstractFormType {

    // Defines the identifier of the form type
    @Override
    public String getName() {
        return "card";
    }

    // Convert the values in the form into actual objects (the actual processing logic depends on the specific business)
    @Override
    public Object convertFormValueToModelValue(String propertyValue) {
        return propertyValue;
    }

    // Convert the value of the actual object into the value in the form (the actual processing logic depends on the specific business)
    @Override
    public String convertModelValueToFormValue(Object modelValue) {
        return (String) modelValue;
    }
    
}

  create a new configuration class and register a custom field type resolution class

@Configuration
public class ApplicationConfig extends WebMvcConfigurerAdapter {
    @Bean
    public BeanPostProcessor activitiConfigurer() {
        return new BeanPostProcessor() {
            @Override
            public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
                if (bean instanceof SpringProcessEngineConfiguration) {
                    List<AbstractFormType> customFormTypes = Arrays.<AbstractFormType>asList(new CardFormType());
                    ((SpringProcessEngineConfiguration)bean).setCustomFormTypes(customFormTypes);
                }
                return bean;
            }
            @Override
            public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
                return bean;
            }
        };
    }
}

2.5 custom form engine

  Flowable supports custom form engines to adapt to various scenarios. You only need to implement the interface org Flowable. engine. impl. form. Formengine, and then register a custom form engine implementation class in the engine.

public class MyFormEngine implements FormEngine {
    // The name of the form engine
    @Override
    public String getName() {
        return "MyFormEngine";
    }

    // The actual processing logic depends on the specific business
    @Override
    public Object renderStartForm(StartFormData startFormData) {
        return "MyStartData";
    }

    // The actual processing logic depends on the specific business
    @Override
    public Object renderTaskForm(TaskFormData taskFormData) {
        return "MyTaskData";
    }
}

  the registration method is similar to the custom form field type. Add the following statement in the configuration class:

    List<FormEngine> customFormEngines = Arrays.<FormEngine>asList(new MyFormEngine());
    ((SpringProcessEngineConfiguration)bean).setCustomFormEngines(customFormEngines);

  usage:

    Object FormService.getRenderedStartForm(String processDefinitionId, "myFormEngine");
    Object FormService.getRenderedTaskForm(String taskId);

3, Summary

   through this article, we learned about the specific use of forms and process variables. Similarly, in actual business use, we still need a lot of optimization. For example, we can save a common key in formKey, get the form template actually needed through algorithm or conversion, display one form in a Web application with ordinary screen size, and display another form in a small screen such as a mobile phone. There is also the "integrated JPA" that will be discussed in the next article to further optimize the use of forms and process variables.

Topics: bpmn