Skip to content

Instantly share code, notes, and snippets.

@marcus-nl
Last active July 29, 2019 07:48
Show Gist options
  • Save marcus-nl/8a9b0ba3636297696058965e2ce4ec27 to your computer and use it in GitHub Desktop.
Save marcus-nl/8a9b0ba3636297696058965e2ce4ec27 to your computer and use it in GitHub Desktop.
Camunda custom form handling example
class ExampleProcessEnginePlugin extends AbstractProcessEnginePlugin {
@Override
public void preInit(ProcessEngineConfigurationImpl processEngineConfiguration) {
super.preInit(processEngineConfiguration);
ExampleFormEngine formEngine = new ExampleFormEngine();
processEngineConfiguration.setCustomFormEngines(singletonList(formEngine));
processEngineConfiguration.setCustomPreBPMNParseListeners(singletonList(new ExampleParseListener(formEngine)));
}
}
class ExampleParseListener extends AbstractBpmnParseListener {
private final ExampleFormEngine formEngine;
public ExampleParseListener(ExampleFormEngine formEngine) {
this.formEngine = formEngine;
}
@Override
public void parseUserTask(Element userTaskElement, ScopeImpl scope, ActivityImpl activity) {
TaskDefinition taskDefinition = ((UserTaskActivityBehavior) activity.getActivityBehavior()).getTaskDefinition();
taskDefinition.setTaskFormHandler(new ExampleFormHandler(formEngine));
}
}
class ExampleFormHandler implements TaskFormHandler, StartFormHandler {
private final ExampleFormEngine formEngine;
private String deploymentId;
private String formKey; // fully qualified name of form class
public ExampleFormHandler(ExampleFormEngine formEngine) {
this.formEngine = formEngine;
}
@Override
public void parseConfiguration(Element activityElement, DeploymentEntity deployment, ProcessDefinitionEntity processDefinition, BpmnParse bpmnParse) {
this.deploymentId = deployment.getId();
this.formKey = activityElement.attributeNS(BpmnParse.CAMUNDA_BPMN_EXTENSIONS_NS, "formKey");
}
@Override
public StartFormData createStartFormData(ProcessDefinitionEntity processDefinition) {
checkState(deploymentId != null, "deploymentId must be set");
checkState(formKey != null, "formKey must be set");
StartFormDataImpl startFormData = new StartFormDataImpl();
startFormData.setDeploymentId(deploymentId);
startFormData.setProcessDefinition(processDefinition);
startFormData.setFormKey(formKey);
return startFormData;
}
@Override
public TaskFormData createTaskForm(TaskEntity task) {
checkState(deploymentId != null, "deploymentId must be set");
checkState(formKey != null, "formKey must be set");
TaskFormDataImpl taskFormData = new TaskFormDataImpl();
taskFormData.setDeploymentId(deploymentId);
taskFormData.setTask(task);
taskFormData.setFormKey(formKey);
return taskFormData;
}
@Override
public void submitFormVariables(VariableMap properties, VariableScope variableScope) {
Object result = formEngine.submitForm(deploymentId, formKey, properties);
variableScope.setVariable(formKey, result);
}
}
public class ExampleFormEngine implements FormEngine {
private final ObjectMapper objectMapper = new ObjectMapper();
// and some kind of cache for the form infos
@Override
public String getName() {
return "example";
}
@Override
public Object renderStartForm(StartFormData startForm) {
String deploymentId = startForm.getDeploymentId();
String formKey = startForm.getFormKey();
return renderForm(deploymentId, formKey, null);
}
@Override
public Object renderTaskForm(TaskFormData taskForm) {
String deploymentId = taskForm.getDeploymentId();
String formKey = taskForm.getFormKey();
VariableMap vars = ((VariableScope) taskForm.getTask()).getVariablesLocalTyped();
return renderForm(deploymentId, formKey, vars);
}
ObjectNode renderForm(String deploymentId, String formKey, VariableMap vars) {
checkNotNull(deploymentId, "deploymentId required");
checkNotNull(formKey, "formKey required");
FormInfo formInfo = createFormInfo(getFormClass(deploymentId, formKey)); // can be cached
return renderForm(formInfo, vars);
}
@SuppressWarnings("unchecked")
ObjectNode renderForm(FormInfo formInfo, VariableMap vars) {
ArrayNode fields = objectMapper.createArrayNode();
for (FieldInfo field : formInfo.getFields()) {
ObjectNode fieldNode = objectMapper.createObjectNode();
fieldNode.set("name", TextNode.valueOf(field.getName()));
fieldNode.set("type", TextNode.valueOf(field.getType().name()));
fieldNode.set("label", TextNode.valueOf(field.getLabel()));
String valueStr = vars.getValue(field.getName(), String.class);
fieldNode.set("value", TextNode.valueOf(valueStr));
fields.add(fieldNode);
}
ObjectNode formNode = objectMapper.createObjectNode();
formNode.set("fields", fields);
return formNode;
}
Form submitForm(String deploymentId, String formKey, VariableMap vars) {
checkNotNull(deploymentId, "deploymentId required");
checkNotNull(formKey, "formKey required");
FormInfo formInfo = createFormInfo(getFormClass(deploymentId, formKey)); // can be cached
return submitForm(formInfo, vars);
}
Form submitForm(FormInfo formInfo, VariableMap vars) {
Form form = formInfo.newInstance();
for (FieldInfo field : formInfo.getFields()) {
String valueStr = vars.getValue(field.getName(), String.class);
Object value = Converters.get(field.getType()).fromString(valueStr);
field.setValue(form, value);
}
return form;
}
FormInfo getFormInfo(String deploymentId, String formKey) {
// we can apply caching here using (deploymentId, formKey) as key
return createFormInfo(getFormClass(deploymentId, formKey));
}
@SuppressWarnings("unchecked")
Class<? extends Form> getFormClass(String deploymentId, String formKey) {
try {
ProcessApplicationInterface pa = ProcessApplicationContextUtil.getTargetProcessApplication(deploymentId).getProcessApplication();
return (Class<? extends Form>) pa.getProcessApplicationClassloader().loadClass(formKey);
}
catch (Exception e) {
throw new RuntimeException("Unable to get form class for deployment '" + deploymentId + "' and form key '" + formKey + "'", e);
}
}
FormInfo createFormInfo(Class<? extends Form> formClass) {
// ...
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment