Cloud Software Group, Inc. EBX®
Documentation > Developer Guide
Navigation mode Documentation > 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());
        }
    }

##### See also

- com.orchestranetworks.workflow.ScriptTask

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

##### See also

- com.orchestranetworks.workflow.ScriptTaskBean

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

##### See also

- com.orchestranetworks.workflow.UserTask

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

##### See also

- com.orchestranetworks.workflow.Condition

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

##### See also

- com.orchestranetworks.workflow.ConditionBean

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

##### See also

- com.orchestranetworks.workflow.SubWorkflowsInvocationBean

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

##### See also

- com.orchestranetworks.workflow.WaitTaskBean

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

    }

##### See also

- com.orchestranetworks.workflow.ActionPermissionsOnWorkflow

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

##### See also

- com.orchestranetworks.workflow.WorkflowTriggerBean

### 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