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();
}
//...
}