The workflow offers two types of steps: 'library' or 'specific'.
'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.
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(); } //... }