The workflow offers three types of steps: 'library' or 'specific' and 'script'.
'Library' is a bean defined in module.xml
and is reusable. Using the 'library' bean improves the ergonomics: parameters are dynamically displayed in the definition screens.
A 'specific' object is a bean defined only by its class name. In this case, the display is not dynamic.
A 'script' is a procedural language based on DSL (Domain Specific Language) used to define custom tasks. The EBX script IDE is used to create and edit the script. The parameters of the workflow context are dynamically displayed in the definition screens.
Step | Library | Specific |
---|---|---|
Scripts | ScriptTaskBean | ScriptTask |
Conditions | ConditionBean | Condition |
User task | UserTask |
A script task has to override the method execute
as in the following example:
public class NppScriptTask_CreateWorkingBranch extends ScriptTask { public void executeScript(ScriptTaskContext aContext) throws OperationException { Repository repository = aContext.getRepository(); String initialBranchString = aContext.getVariableString("initialBranch"); AdaptationHome initialBranch = repository.lookupHome(HomeKey.forBranchName(initialBranchString)); if (initialBranch == null) throw OperationException.createError("Null value for initialBranch"); HomeCreationSpec spec = new HomeCreationSpec(); spec.setParent(initialBranch); spec.setKey(HomeKey.forBranchName("Name")); spec.setOwner(Profile.EVERYONE); spec.setHomeToCopyPermissionsFrom(initialBranch); AdaptationHome newHome = repository.createHome(spec, aContext.getSession()); //feeds dataContext aContext.setVariableString("workingBranch", newHome.getKey().getName()); } }
A script task bean has to override the method executeScript
as in the following example:
public class ScriptTaskBean_CreateBranch extends ScriptTaskBean { private String initialBranchName; private String newBranch; public String getInitialBranchName() { return this.initialBranchName; } public void setInitialBranchName(String initialBranchName) { this.initialBranchName = initialBranchName; } public String getNewBranch() { return this.newBranch; } public void setNewBranch(String newBranch) { this.newBranch = newBranch; } public void executeScript(ScriptTaskBeanContext aContext) throws OperationException { final Repository repository = aContext.getRepository(); String initialBranchName = this.getInitialBranchName(); final AdaptationHome initialBranch = repository.lookupHome(HomeKey.forBranchName(initialBranchName)); final HomeCreationSpec spec = new HomeCreationSpec(); spec.setParent(initialBranch); spec.setKey(HomeKey.forBranchName(XsFormats.SINGLETON.formatDateTime(new Date()))); spec.setOwner(Profile.EVERYONE); spec.setHomeToCopyPermissionsFrom(initialBranch); final AdaptationHome branchCreate = repository.createHome(spec, aContext.getSession()); this.setNewBranch(branchCreate.getKey().getName()); } }
A script task bean must be declared in module.xml
:
<module> <beans> <bean className="com.orchestranetworks.workflow.genericScriptTask.ScriptTaskBean_CreateBranch"> <documentation xml:lang="fr-FR"> <label>Créer une branche</label> <description> Ce script permet de créer une branche </description> </documentation> <documentation xml:lang="en-US"> <label>Create a branch</label> <description> This script creates a branch </description> </documentation> <properties> <property name="initialBranchName" input="true"> <documentation xml:lang="fr-FR"> <label>Branche initiale</label> <description> Nom de la branche initiale. </description> </documentation> <documentation xml:lang="en-US"> <label>Initial branch</label> <description> Initial branch name. </description> </documentation> </property> <property name="newBranch" output="true"> <documentation xml:lang="fr-FR"> <label>Nouvelle branche</label> <description> Nom de la branche créée </description> </documentation> <documentation xml:lang="en-US"> <label>New branch</label> <description> Created branch name. </description> </documentation> </property> </properties> </bean> </beans> </module>
A built-in service can be declared in module.xml
to be used in the user task definition.
<services> <service name="ServiceModule"> <resourcePath>/service.jsp</resourcePath> <type>branch</type> <documentation xml:lang="fr-FR"> <label>Workflow service</label> <description> Ce service permet de ... </description> </documentation> <documentation xml:lang="en-US"> <label>Service workflow</label> <description> The purpose of this service is ... </description> </documentation> <properties> <property name="param1" input="true"> <documentation xml:lang="fr-FR"> <label>Param1</label> <description>Param1 ...</description> </documentation> </property> <property name="param2" output="true"> </property> </properties> </service> <serviceLink serviceName="adaptationService"> <importFromSchema> /WEB-INF/ebx/schema/schema.xsd </importFromSchema> </serviceLink> </services>
The GUI is quite similar as the example above. The field 'Rule' must be filled to define the class extending the 'UserTask' to invoke.
public class NppUserTask_ValidateProduct extends UserTask { public void handleWorkItemCompletion(UserTaskWorkItemCompletionContext context) throws OperationException { if (context.getCompletedWorkItem().isRejected()) { context.setVariableString(NppConstants.VAR_VALIDATION, "KO"); context.completeUserTask(); } else if (context.checkAllWorkItemMatchStrategy()) { context.setVariableString(NppConstants.VAR_VALIDATION, "OK"); context.completeUserTask(); } } public void handleCreate(UserTaskCreationContext context) throws OperationException { CreationWorkItemSpec spec = CreationWorkItemSpec.forOfferring(NppConstants.ROLE_PVALIDATOR); spec.setNotificationMail("1"); context.createWorkItem(spec); context.setVariableString(NppConstants.VAR_VALIDATION, "validating"); } }
The method evaluate
has to be overridden:
public class NppCondition_IsValidationOK extends Condition { public boolean evaluateCondition(ConditionContext context) throws OperationException { String validation = context.getVariableString("validationResult"); boolean hasError = "KO".equals(validation); return !hasError; } }
The method evaluateCondition
has to be overridden as in the following sample:
public class ConditionBean_IsBranchValid extends ConditionBean { private String branchName; public String getBranchName() { return this.branchName; } public void setBranchName(String branchName) { this.branchName = branchName; } public boolean evaluateCondition(ConditionBeanContext aContext) throws OperationException { final Repository repository = aContext.getRepository(); Severity severityForValidation = Severity.ERROR; String branchToTestName = this.getBranchName(); final AdaptationHome branchToTest = repository.lookupHome(HomeKey.forBranchName(branchToTestName)); if (branchToTest.getValidationReportsMap(severityForValidation) != null && branchToTest.getValidationReportsMap(severityForValidation).size() > 0) { return false; } return true; } }
The condition bean must be declared in module.xml
:
<module> <beans> <bean className="com.orchestranetworks.workflow.genericScriptTask.ConditionBean_IsBranchValid"> <documentation xml:lang="fr-FR"> <label>Branche valide ?</label> <description> Ce script permet de tester si une branche est valide. </description> </documentation> <documentation xml:lang="en-US"> <label>Branch valid ?</label> <description> This script allows to check if a branch is valid. </description> </documentation> <properties> <property name="branchName" input="true"> <documentation xml:lang="fr-FR"> <label>Branche à contrôler</label> <description> Nom de la branche à valider. </description> </documentation> <documentation xml:lang="en-US"> <label>Branch to check</label> <description> Branch name to check. </description> </documentation> </property> </properties> </bean> </beans> </module>
public class MySubWorkflowsInvocationBean extends SubWorkflowsInvocationBean { @Override public void handleCreateSubWorkflows(SubWorkflowsCreationContext aContext) throws OperationException { final ProcessLauncher subWorkflow1 = aContext.registerSubWorkflow( AdaptationName.forName("validateProduct"), "validateProduct1"); subWorkflow1.setLabel(UserMessage.createInfo("Validate the new product by marketing")); subWorkflow1.setInputParameter("workingBranch", aContext.getVariableString("workingBranch")); subWorkflow1.setInputParameter("code", aContext.getVariableString("code")); subWorkflow1.setInputParameter("service", aContext.getVariableString("marketing")); final ProcessLauncher subWorkflow2 = aContext.registerSubWorkflow( AdaptationName.forName("validateProduct"), "validateProduct2"); subWorkflow2.setLabel(UserMessage.createInfo("Validate the new product by direction")); subWorkflow2.setInputParameter("workingBranch", aContext.getVariableString("workingBranch")); subWorkflow2.setInputParameter("code", aContext.getVariableString("code")); subWorkflow2.setInputParameter("service", aContext.getVariableString("direction")); // Conditional launching. if (aContext.getVariableString("productType").equals("book")) { final ProcessLauncher subWorkflow3 = aContext.registerSubWorkflow( AdaptationName.forName("generateISBN"), "generateISBN"); subWorkflow3.setLabel(UserMessage.createInfo("Generate ISBN")); subWorkflow3.setInputParameter( "workingBranch", aContext.getVariableString("workingBranch")); subWorkflow3.setInputParameter("code", aContext.getVariableString("code")); } aContext.launchSubWorkflows(); } @Override public void handleCompleteAllSubWorkflows(SubWorkflowsCompletionContext aContext) throws OperationException { aContext.getCompletedSubWorkflows(); final ProcessInstance validateProductMarketing = aContext.getCompletedSubWorkflow("validateProduct1"); final ProcessInstance validateProductDirection = aContext.getCompletedSubWorkflow("validateProduct2"); if (aContext.getVariableString("productType").equals("book")) { final ProcessInstance generateISBN = aContext.getCompletedSubWorkflow("generateISBN"); aContext.setVariableString("isbn", generateISBN.getDataContext().getVariableString( "newCode")); } if (validateProductMarketing.getDataContext().getVariableString("Accepted").equals("true") && validateProductDirection.getDataContext().getVariableString("Accepted").equals( "true")) aContext.setVariableString("validation", "ok"); } }
SubWorkflowsInvocationBean
bean must be declared in module.xml
:
<module> <beans> <bean className="com.orchestranetworks.workflow.test.MySubWorkflowsInvocationBean"/> </beans> </module>
public class MyWaitTaskBean extends WaitTaskBean { @Override public void onStart(WaitTaskOnStartContext aContext) { Map<String, String> params = new HashMap<String, String>(); params.put("resumeId", aContext.getResumeId()); myMethod.callWebService(params); } @Override public void onResume(WaitTaskOnResumeContext aContext) throws OperationException { // Defines a specific mapping. aContext.setVariableString("code", aContext.getOutputParameters().get("isbn")); aContext.setVariableString("comment", aContext.getOutputParameters().get("isbnComment")); } }
WaitTaskBean
bean must be declared in module.xml
:
<module> <beans> <bean className="com.orchestranetworks.workflow.test.MyWaitTaskBean"/> </beans> </module>
package com.orchestranetworks.workflow.test; import com.orchestranetworks.service.*; import com.orchestranetworks.workflow.*; import com.orchestranetworks.workflow.ProcessExecutionContext.*; /** */ public class MyDynamicPermissions extends ActionPermissionsOnWorkflow { public ActionPermission getActionPermission( WorkflowPermission aWorkflowAction, ActionPermissionsOnWorkflowContext aContext) { if (WorkflowPermission.VIEW.equals(aWorkflowAction) || WorkflowPermission.CREATE_PROCESS.equals(aWorkflowAction)) return ActionPermission.getEnabled(); return ActionPermission.getDisabled(); } }
ActionPermissionsOnWorkflow
bean must be declared in module.xml
:
<module> <beans> <bean className="com.orchestranetworks.workflow.test.MyDynamicPermissions"/> </beans> </module>
public class MyWorkflowTriggerBean extends WorkflowTriggerBean { @Override public void handleAfterProcessInstanceStart( WorkflowTriggerAfterProcessInstanceStartContext aContext) throws OperationException { final DisplayPolicy policy = DisplayPolicyFactory.getPolicyForSession(aContext.getSession()); final MailSpec spec = aContext.createMailSpec(); spec.notify(NotificationType.TO, "supervisor@mail.com"); spec.setSubject("[TRIGGER] After process instance start"); spec.setBody("The workflow '" + policy.formatUserMessage(aContext.getProcessInstance().getLabel()) + "' has been created."); spec.sendMail(Locale.US); } @Override public void handleBeforeProcessInstanceTermination( WorkflowTriggerBeforeProcessInstanceTerminationContext aContext) throws OperationException { final DisplayPolicy policy = DisplayPolicyFactory.getPolicyForSession(aContext.getSession()); final MailSpec spec = aContext.createMailSpec(); spec.notify(NotificationType.TO, "supervisor@mail.com"); spec.setSubject("[TRIGGER] Before process instance termination"); spec.setBody("The workflow '" + policy.formatUserMessage(aContext.getProcessInstance().getLabel()) + "' has been completed. The created product is: '" + aContext.getVariableString(NppConstants.VAR_CODE) + "'."); spec.sendMail(Locale.US); } @Override public void handleAfterWorkItemCreation(WorkflowTriggerAfterWorkItemCreationContext aContext) throws OperationException { DisplayPolicy policy = DisplayPolicyFactory.getPolicyForSession(aContext.getSession()); MailSpec spec = aContext.createMailSpec(); spec.notify(NotificationType.TO, "supervisor@mail.com"); spec.setSubject("[TRIGGER] After work item creation"); WorkItem workItem = aContext.getWorkItem(); State state = workItem.getState(); String body = "The work item '" + policy.formatUserMessage(workItem.getLabel()) + "' has been created. \n The step id is : " + aContext.getCurrentStepId() + ". \n The work item is in state : " + policy.formatUserMessage(state.getLabel()); if (workItem.getOfferedTo() != null) body += "\n The role is :" + workItem.getOfferedTo().format(); if (workItem.getUserReference() != null) body += "\n The user is :" + workItem.getUserReference().format(); spec.setBody(body); spec.sendMail(Locale.US); } @Override public void handleBeforeWorkItemStart(WorkflowTriggerBeforeWorkItemStartContext aContext) throws OperationException { DisplayPolicy policy = DisplayPolicyFactory.getPolicyForSession(aContext.getSession()); MailSpec spec = aContext.createMailSpec(); spec.notify(NotificationType.TO, "supervisor@mail.com"); spec.setSubject("[TRIGGER] Before work item start"); spec.setBody("The work item '" + policy.formatUserMessage(aContext.getWorkItem().getLabel()) + "' has been started. \n The current step id is : " + aContext.getCurrentStepId() + ". \n The work item user is: '" + DirectoryHandler.getInstance(aContext.getRepository()).displayUser( aContext.getWorkItem().getUserReference(), aContext.getSession().getLocale()) + "'."); spec.sendMail(Locale.US); } @Override public void handleBeforeWorkItemAllocation( WorkflowTriggerBeforeWorkItemAllocationContext aContext) throws OperationException { DisplayPolicy policy = DisplayPolicyFactory.getPolicyForSession(aContext.getSession()); MailSpec spec = aContext.createMailSpec(); spec.notify(NotificationType.TO, "supervisor@mail.com"); spec.setSubject("[TRIGGER] Before work item allocation"); spec.setBody("The work item '" + policy.formatUserMessage(aContext.getWorkItem().getLabel()) + "' has been allocated. \n The current step id is: " + aContext.getCurrentStepId() + ". \n The work item user is: '" + DirectoryHandler.getInstance(aContext.getRepository()).displayUser( aContext.getUserReference(), aContext.getSession().getLocale()) + "'."); spec.sendMail(Locale.US); } @Override public void handleBeforeWorkItemDeallocation( WorkflowTriggerBeforeWorkItemDeallocationContext aContext) throws OperationException { DisplayPolicy policy = DisplayPolicyFactory.getPolicyForSession(aContext.getSession()); MailSpec spec = aContext.createMailSpec(); spec.notify(NotificationType.TO, "supervisor@mail.com"); spec.setSubject("[TRIGGER] Before work item deallocation"); spec.setBody("The work item '" + policy.formatUserMessage(aContext.getWorkItem().getLabel()) + "' has been deallocated. \n The current step id is: " + aContext.getCurrentStepId() + ". \n The old work item user is: '" + DirectoryHandler.getInstance(aContext.getRepository()).displayUser( aContext.getWorkItem().getUserReference(), aContext.getSession().getLocale()) + "."); spec.sendMail(Locale.US); } @Override public void handleBeforeWorkItemReallocation( WorkflowTriggerBeforeWorkItemReallocationContext aContext) throws OperationException { DisplayPolicy policy = DisplayPolicyFactory.getPolicyForSession(aContext.getSession()); MailSpec spec = aContext.createMailSpec(); spec.notify(NotificationType.TO, "supervisor@mail.com"); spec.setSubject("[TRIGGER] Before work item reallocation"); spec.setBody("The work item '" + policy.formatUserMessage(aContext.getWorkItem().getLabel()) + "' has been reallocated. \n The current step id is: " + aContext.getCurrentStepId() + ". \n The work item user is: '" + DirectoryHandler.getInstance(aContext.getRepository()).displayUser( aContext.getUserReference(), aContext.getSession().getLocale()) + "'. The old work item user is: '" + DirectoryHandler.getInstance(aContext.getRepository()).displayUser( aContext.getWorkItem().getUserReference(), aContext.getSession().getLocale()) + "'."); spec.sendMail(Locale.US); } @Override public void handleBeforeWorkItemTermination( WorkflowTriggerBeforeWorkItemTerminationContext aContext) throws OperationException { DisplayPolicy policy = DisplayPolicyFactory.getPolicyForSession(aContext.getSession()); MailSpec spec = aContext.createMailSpec(); spec.notify(NotificationType.TO, "supervisor@mail.com"); spec.setSubject("[TRIGGER] Before work item termination"); spec.setBody("The work item '" + policy.formatUserMessage(aContext.getWorkItem().getLabel()) + "' has been terminated. \n The current step id is: " + aContext.getCurrentStepId() + ". \n The work item has been accepted ? " + aContext.isAccepted()); spec.sendMail(Locale.US); } }
WorkflowTriggerBean
bean must be declared in module.xml
:
<module> <beans> <bean className="com.orchestranetworks.workflow.test.MyWorkflowTriggerBean"/> </beans> </module>
public class TriggerWorkflow extends TableTrigger { public void handleAfterModify(AfterModifyOccurrenceContext aContext) throws OperationException { ValueContext currentRecord = aContext.getOccurrenceContext(); String code = (String) currentRecord.getValue(Path.parse("/code")); //Get published process PublishedProcessKey processPublishedKey = PublishedProcessKey.forName("productProcess"); //Defines process instance WorkflowEngine engine = WorkflowEngine.getFromProcedureContext(aContext.getProcedureContext()); ProcessLauncher launcher = engine.getProcessLauncher(processPublishedKey); //initialize Data Context launcher.setInputParameter("code", "/root/Client[./code=\"" + code + "\"]"); launcher.setInputParameter("workingBranch", aContext.getAdaptationHome().getKey().getName()); //Starts process launcher.launchProcess(); } //... }