Cloud Software Group, Inc. EBX®
Documentation > Developer Guide
Navigation modeDocumentation > Developer Guide

Workflow model

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.

Bean categories

Step

Library

Specific

Scripts

ScriptTaskBean

ScriptTask

Conditions

ConditionBean

Condition

User task

UserTask

Sample of ScriptTask

Java Code

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

Sample of ScriptTaskBean

Java Code

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

Configuration through module.xml

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>

Samples of UserTask

Service declaration via module.xml

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>

A more complex UserTask

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");
    }
}

Samples of Condition

Java Code

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;
    }
}

Sample of ConditionBean

Java Code

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;
    }
}

Configuration through module.xml

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>

Sample of SubWorkflowsInvocationBean

Java Code

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");
	}
}

Configuration through module.xml

SubWorkflowsInvocationBean bean must be declared in module.xml:

<module>
    <beans>
        <bean className="com.orchestranetworks.workflow.test.MySubWorkflowsInvocationBean"/>
    </beans>
</module>

Sample of WaitTaskBean

Java Code

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"));
	}
}

Configuration through module.xml

WaitTaskBean bean must be declared in module.xml:

<module>
    <beans>
        <bean className="com.orchestranetworks.workflow.test.MyWaitTaskBean"/>
    </beans>
</module>

Sample of ActionPermissionsOnWorkflow

Java Code

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

}

Configuration through module.xml

ActionPermissionsOnWorkflow bean must be declared in module.xml:

<module>
    <beans>
        <bean className="com.orchestranetworks.workflow.test.MyDynamicPermissions"/>
    </beans>
</module>

Sample of WorkflowTriggerBean

Java Code

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);
	}
}

Configuration through module.xml

WorkflowTriggerBean bean must be declared in module.xml:

<module>
    <beans>
        <bean className="com.orchestranetworks.workflow.test.MyWorkflowTriggerBean"/>
    </beans>
</module>

Sample of trigger starting a process instance

Sample

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();

    }
      //...
}
Documentation > Developer Guide