Concepts

Adaptive Case Management

Classic BPM processes have a clear flow that defines how the process is executed. Within these strict processes the involved user has limited possibilities to improve the process while executing. Optimizations and flow changes often require a long modeling and re-deployment round-trip. Furthermore the process could get cryptic because every rarely occurring special case has to be modeled. Therefore, the process does no longer clearly show the most relevant business paths. Welcome to the world of spaghetti BPM.

Today the user has the need to adapt the process flow during execution. Optional side tasks are required in addition to the normal process flow or a set of tasks must be skipped because of a special condition. This brings back the power to the user which has often more knowledge about the domain and the current context of the process. For instance an important information could be received from a phone call, but the workflow system has no knowledge about this analog information.

Adaptive implementation

Invoking optional processes

Think about a process where the purchase of an asset must be approved by a line manager. The line manager may want to ask the requester to provide more details why the asset is required. Therefore he'd add a side task to ask the requester for clarification. This optional interaction should not be wired into the main approval process as it obfuscates the most used business path. But it could be available as an optional side task that the line manager can start and then gets executed within the current process context.

In Axon.ivy processes with side tasks can be invoked through Triggers or Signals.

Triggers

It is possible to trigger a strictly defined process. Strictly defined means that the calling process knows the target process as it has to be implemented in the same or a required project. RequestStart events can be declared as trigger-able. While the Trigger activity is used to actually trigger such a start.

So in the request verification front end, a manager could simply trigger the process to enrich the request with a trigger call activity.

Signals

Most of the time you'd prefer a looser coupling between processes. This could be accomplished with Signals. A process that wants to integrate other processes simply fires a signal when a certain state within the process is reached. Multiple other processes in the same application could listen to this signal and all of them will be executed as soon as the signal is fired. A dependency between the firing and listening processes is not required.

As example think of an employee that starts to work in a company. When the employee is registered from HR, other processes can setup the environment for this employee. An IT responsible will setup a new desktop workstation while an office administrator will get the personal keys for the employee. To do this tasks in parallel and loosely coupled signals are the first choice. The IT- and the office-process could listen to employee entry signals fired by the HR process.

Keeping loosely coupled processes in same context

A real world agile process execution can touch many different processes. But still the history and the context must be clear for anyone who is involved in a task. So the workflow needs to know whether an invoked process belongs to the invoking case. Or if the invoked process belongs to a completely new case.

The entity that can glue multiple process cases together is the Business Case. All cases and tasks that belong to the same Business Case are presented to the user of a workflow screen as related cases. Therefore, triggerable- and signalable-process start must define whether they belong to the same Business Case as the invoking process case. This can be done with a simple configuration on these starts. See the SignalStart and TriggerStart inscription for details.

You can also use the Public API to attach the current case to an existing Business Case.

if (in.departement.equals("HR")) // evaluate attachment by runtime conditions
{
  ivy.case.attachToBusinessCase(in.callerCaseId)
}

For workflow front end developers there exists API to list all tasks or cases of a Business Case. So showing the involved cases and tasks to a workflow user is a simple implementation. For more details see the Public API of ch.ivyteam.ivy.workflow.businesscase.IBusinessCase.

ivy.case.getBusinessCase().getActiveTasks(); // get involved tasks that are active
ivy.case.getBusinessCase().getTasks(); // get all involved tasks

Aborting tasks

A long running process could get into the situation that there are many open tasks that have to be done by human users. But eventually the environment of the case changes and it does no longer make any sense to accomplish the open tasks. For instance think about a car leasing process. If the customer decides shortly before contract signing that he requires leather seats instead of the furnished ones, the car will get more expensive. So the whole credit assessment process has to run again and open tasks become obsolete.

A UserTask can support abortion by listening to a signal. The UserTask activity can subscribe to an abortion signal by adding a Signal Boundary Event on it. When the signal, that the credit amount of the car changed, is fired from another process the listening UserTask will be aborted. And the process continues at the Signal Boundary Event. Classically after the Signal Boundary a clean up process follows.

Share data between processes

Often an initial larger process starts by gathering data that is later processed and enriched. This data is typically business relevant domain data that can be recognized by domain experts that contribute to the process. Think of bank employee that grants credits. The data for his processes could look like this when simplified:

Data class of a credit request

Figure 9.1. Data class of a credit request

To store this kind of data Axon.ivy provides a simple repository that is called Business Data. This stored data can then be accessed by multiple processes instances during the lifetime of a long living complex process. The repository provides access to the data with simple store and load functions similar to well known other repositories such as the EntityManager from JPA. But in comparison to JPA and similar technologies this repository can be used without any database or environment configuration.

CreditRequest creditRequest = ivy.repo.find(in.businessDataId, CreditRequest.class); // load a CreditRequest from the Business Data Repo
creditRequest.amount = 30000; // modify a field
ivy.repo.save(creditRequest); // save the modified CreditRequest back to the Repo.

By annotating a data class with the @BusinessCaseData annotation, all values of the annotated data class are automatically associated with the context of the current Business Case. The data is then shared and accessible from all processes belonging to the Business Case. Multiple data classes of different types can be used inside a Business Case.

Business Data analytics

Running business processes typically generate highly valuable data, that could influence critical business decisions. Based on the stored data you will typically want to visualize KPIs on a management dashboard. In our credit sample, you may want to visualize the aggregated sum of all open credits. The data in the Business Data repository is stored in form that is easily accessible and explorable with a tool like Kibana.

Kibana visualizing the total of the requested credit volume

Figure 9.2. Kibana visualizing the total of the requested credit volume

Regaining the big picture

Real world BPM projects have shown that big processes tend to get increasingly complex and need to be split up into huge process landscapes, which leads to an intransparent main process flow. Users of the process often do not see how their work contributes to the bigger business process and therefore great opportunities for improvements are not taken. There is also a big need for a unique view of adaptive case operations that can be used by process contributors. Like an overview of optional tasks that a clerk can start at any time.

The Case Map addresses the needs for flexible and agile Business Cases by providing a clear and simple view on the main process and its execution. With the Case Map you can easily orchestrate the main flow of processes and the business can identify and track the stages where a running process instance is.

Case Map of a credit lending process

Figure 9.3. Case Map of a credit lending process

A Case Map is divided into stages (in the sample above the stages are: Identification, Credit rating and Approval). Each stage defines a certain phase in the life cycle of a business process. A stage consists of processes (e.g. "Collect personal data"). The default flow or also known as happy path is from left to right and from top to bottom. If the last process of a stage has finished the flow continues on the stage to the right of the current stage. Stages typically have a name and icon. The idea is to reuse this icons in Workflow UIs and processes to give the end user a hint in which stage the current Business Case is.

Actual stage of a Business Case displayed in the portal

Figure 9.4. Actual stage of a Business Case displayed in the portal

Besides processes a stage of a Case Map can contain Sidesteps (e.g. "External solvency service" in the Case Map above). Sidesteps can be started manually by the workflow user during the ongoing Business Case. A typical Sidestep could be a process which aborts the business process (e.g. abort request). The use of Sidesteps can reduce the time spent on technical round trips, for modeling rare and costly edge cases.

A Sidestep "External solvency service" which can be started in the portal

Figure 9.5. A Sidestep "External solvency service" which can be started in the portal

The dependencies between Case Map, Business Cases and Business Data are as follows: Processes started inside a Case Map create new cases inside the Business Case, which themselves contain tasks for the users. Data between processes can be easily shared using Business Data. A Business Case can be attached to a Case Map, which in turn controls the flow of the processes.

The relationship between Business Case, Business Data and Case Map.

Figure 9.6. The relationship between Business Case, Business Data and Case Map.

Conclusion

To reiterate: signals and triggers can be used to loosely respectively tightly couple different processes. The innovative Case Map brings order in to chaos of spaghetti BPM. A domain expert always has a simple graphical view on the Business Case where he contributes to. The Case Map empowers the domain expert to steer the process execution by starting optional Sidesteps or gracefully skipping large parts of the pre-modeled standard flow.

The Case Map gives the developer and the user a common language to talk about a complex process landscape. The Case Map can be read and understood by anyone that contributes to the process without an introduction. This brings back the old BPM ideas that stood the test of time.

Signal Reference

Signals inform an unknown number of recipients of a specific event that has happened. Signals are sent application-wide without the need for project dependency between the sender and receiver.

Sending Signals

A Signal can be sent programmatically and consists of a Signal Code and optional signal data.

Note

Signal codes are defined as strings. Only letters and numbers [A-Za-z0-9] as well as the colon : as separator, are allowed characters for a Signal Code.

Valid:

hr:employee:quit, flight:cancel:no:LXL398

Send a Signal programmatically

A signal with a custom signal code can be sent using the following IvyScript code:

import ch.ivyteam.ivy.process.model.value.SignalCode;

// send simple signal
ivy.wf.signals().send("datarepository:updated");

// send signal with reference
ivy.wf.signals().send("order:canceled:"+in.order.id);

// send signal with signal data
ivy.wf.signals().send(new SignalCode("user:created"), in.employee.name);

Tip

It is not recommended to use data classes as signal data as not all receiving projects might have access to these data classes. Better send an id which references an object in a database or send payload data that is encoded as string (e.g. JSON).

Send a Signal manually in the Designer

While developing a process it is possible to send a Signal manually in the 'Signals' page of the Designer Workflow UI.

Receiving Signals

Signals can be received by Signal Boundary Events and Signal Start Events. Inscribed signal patterns can contain wildcards (*). Signal Boundary Events can react to a signal pattern containing macros.

Signal Boundary Event

A “Signal Boundary Event” attached to a User Task Element destroys the task if a matching signal is received and the task is in SUSPENDED state (see also “Signal Boundary Event” in the Workflow chapter). The inscribed pattern on the Signal Boundary Event defines the filter for awaited signals codes:

Listening for a cancelled order signal with a specific id defined as macro:

order:canceled:<%=in.orderNr%>

Listening to any user created signals:

user:created:*

Signal Start Event

With a “Signal Start” a new process is started if a matching signal code is received.

Tracing Signals

Signals can be traced by either using the Designer Workflow UI or the JSF Workflow UI both Workflow UIs make use of the Public API for Signals (ivy.wf.signals()).

Tip

For debugging the signal data of a Signal event you can set a breakpoint on a Signal Start or Signal Boundary Event and inspect the signal variable in the 'Variables' view.

Workflow

Case and Task

The ivy workflow manages the execution of process instances. A process instance is represented trough one Case and one or multiple Tasks. The Case exists from the first process step until the last process step and holds information of a process instance. When the Case gets finished even the process gets finished and backwards. A Case is processed though Tasks. Each Task defines an unit of work, which has to be done as one working step. Therefore a Task is assigned to a user or role which executes Task. A Task starts by a process-start element or a task-switch element and ends by the next task-switch element or an process-end element.

Business Case

Modern processes are loosely coupled and highly adaptive. Business processes can break out of the standard process flow and trigger asynchronous processes or send a signal that starts various other processes. As every running process creates a new Case instance it can get difficult for the workflow users to track the history and context of a task.

To clarify the workflow view, multiple Cases can be attached to a single Business Case. Triggered or signaled process-starts define in their inscription whether the started Case should be attached to the Business Case of the calling Case. Moreover, any Case can be attached to a Business Case by API. If a case map is started a business case is automatically created. See Workflow execution of Case Map Processes

Lifecycle

The first case of a process always acts as Business Case (Figure 9.7, “Initial Case”). If later an additional process case is attached to this initial case, this first initial case will be copied and treated as Business Case ((Figure 9.8, “Multiple Cases”)).

Initial Case

Figure 9.7. Initial Case

Multiple Cases

Figure 9.8. Multiple Cases

Case and Task Categories

A Case or a Task can be assigned to a category. A category is a structured String (e.g. Finance/Invoices) and categorize them into a hierarchical structure. It is beside the name of a Task (or Case) an important identification attribute of a Case or Task.

The Category API allows to get localized information from the CMS. E.g. the name of the category Finance/Invoices is stored in the CMS at /Categories/Finance/Invoices/name.

The following example shows a simple usage of a category on Case level. The API on Task level is identical.

ivy.case.setCategoryPath("Finance/Invoices");

String categoryName = ivy.case.getCategory().getName(); // EN: "Invoices", DE: "Rechnungen"
String categoryPath = ivy.case.getCategory().getPath(); // EN: "Finance/Invoices", DE: "Finanzen/Rechnungen"

Tip

The project WorkflowDemos demonstrates the usage of case and task categorisation. Typically the case category is used to categorize the over-all process (i.e. Business Case) and the task category is used to categorize a single or set of unions of work. Because the clear separation of case and task categorization even complex use cases could be handled.

E.g. in a midsized company the process to request an address from a customer change exists in multiple forms. There is one in the customer portal and one for partner agencies. The process executed from the customer portal has the case category 'CustomerPortal/AddressChange'. The process executed by a partner agency has the case category 'Partner/Customers/AddressChange'. Both processes has involved a task to validate the address. Finally the address verification is done by the same department/user. So this task has in both cases the category 'AddressVerification'. This allows the user to filter those tasks no matter where they where created.

Workflow API

There are several APIs to manipulate and query workflow tasks and cases.

Task and Case queries

The fluent workflow query API makes queries against all existing tasks and cases possible. The queries can be written in a SQL like manor.

import ch.ivyteam.ivy.workflow.query.TaskQuery;
import ch.ivyteam.ivy.workflow.ITask;

// create a new query
TaskQuery query = TaskQuery.create()
  .aggregate().avgCustomDecimalField1()
  .where().customVarCharField1().isEqual("ivy")
  .groupBy().state()
  .orderBy().customVarCharField2().descending();
// resolve query results
List<ITask> tasks = ivy.wf.getTaskQueryExecutor().getResults(query);

To resolve all tasks that the current user can work on use the following code:

TaskQuery query = TaskQuery.create()
  .where().currentUserCanWorkOn()
  .orderBy().priority();
List<ITask> userWorkTasks = ivy.wf.getTaskQueryExecutor().getResults(query);

To execute a query an instance of a IQueryExecutor is needed. It can be retrieved trough the ivy environment variable.

// Application specific query executors can be retrieved from the application context
ivy.wf.getTaskQueryExecutor().getResults(taskQuery);
ivy.wf.getCaseQueryExecutor().getResults(caseQuery);

Warning

Queries over all applications can be executed on the global workflow context. But queries that involve the current session could deliver useless results as users are not shared over multiple applications.

ivy.wf.getGlobalContext().getTaskQueryExecutor().getResults(taskQuery);
ivy.wf.getGlobalContext().getCaseQueryExecutor().getResults(caseQuery);

Task and Case manipulation

The API to manipulate tasks and cases is available trough the ivy environment variable.

  • ivy.case (ICase): represents the current process under execution

  • ivy.task (ITask): represents the user's current work unit in the process under execution.

  • ivy.wf (IWorkflowContext): addresses all workflow tasks and cases of all users for the application under execution.

  • ivy.session (IWorkflowSession): gives access to all workflow tasks and cases of the current user.

REST API

There is a REST API available that uses HTTP, JSON (application/json) as content type and HTTP basic as authentication method. Over this interface the following services are available:

HTTP GET /ivy/api/{application name}/workflow/processstarts

Returns all process starts that can be started by the authenticated user.

HTTP GET /ivy/api/{application name}/workflow/task/{taskId}

Returns the task with the given task identifier.

HTTP GET /ivy/api/{application name}/workflow/tasks

Returns the tasks the authenticated user can work on.

HTTP GET /ivy/api/{application name}/workflow/tasks/count

Returns the number of tasks the authenticated user can work on.

HTTP GET /ivy/api/{application name}/engine/info

Returns the version and the name of the engine

Workflow States

During a process execution the corresponding case and tasks have various states. Normally, a case is started non persistent. This means it is stored in memory only. As soon as the process hits a task switch the case and its tasks will be made persistent by storing them to the system database. Only persistent cases and tasks can be resolved with the query API's above.

Process without Task switch

 Process startProcess end
Case stateCREATEDDONE
Task stateCREATEDDONE
PersistentNONO

Table 9.1. Process without Task switch

Process with session timeout

 Process startUser Dialog
Case stateCREATEDZOMBIE
Task stateCREATEDZOMBIE
PersistentNONO

Table 9.2. Process with User Dialog that reaches a session timeout

Process with Task switch

 Process startTask switchProcess end
Case stateCREATEDRUNNINGDONE
Task state (Task 1)CREATEDDONE 
Task state (Task 2) SUSPENDEDDONE
PersistentNOYESYES

Table 9.3. Process with Task switch

Task switch states in detail

In detail the tasks are going to more technical task states inside of a task switch element. After a task reaches a task switch it is in state READY_FOR_JOIN. As soon as all input tasks have arrived at the task switch the state of all input tasks are switched to JOINING and the process data of the tasks are joined to one process data that is used as start data for the output tasks. After joining the input tasks are in state DONE and the output tasks are created in state SUSPENDED.

 Before Task switchTask switch (reached)Task switch (entry)Task switch (done/output)After Task switch
Case stateCREATED/RUNNINGRUNNING
Task state (Task 1)CREATED/RESUMEDREADY_FOR_JOINJOININGDONE-
Task state (Task 2)---SUSPENDEDRESUMED
PersistentNO/YESYES

Table 9.4. Process with Task switch

Task with session timeout

If a user resumes a task with an user dialog and then the session of the user timeouts then the task state is set back to state SUSPENDED and the process of the task is set back to the task switch element.

 Task switchUser DialogTask switch (after session timeout)
Case stateRUNNINGRUNNINGRUNNING
Task state (Task 1)SUSPENDEDRESUMEDSUSPENDED
PersistentYESYESYES

Table 9.5. Task with session timeout

User Task

A User Task is the combination of a Task Switch Event and a User Dialog. When the user start working on a normal Html User Dialog the task changes its state to RESUMED. In case of an 'Offline Dialog' the task state is not changed before the user submits the task form. Then the state changes from SUSPENDED to RESUMED. Subsequent steps are executed until the task is finally DONE. See also “Offline Tasks”.

Signal Boundary Event

A User Task with an attached Signal Boundary Event is listening to a signal while its task is in SUSPENDED state. If the signal has been received the task is destroyed and the execution continues with a newly created follow-up task.

Other task states

There are more task states mainly for task synchronisation, error handing, intermediate events, or user aborts. To learn more about task states see enumeration ch.ivyteam.ivy.workflow.TaskState in public API.

Offline Tasks

Offline tasks are designed for use on a mobile device without connection to the Axon.ivy Engine. Typically the task data and the task forms are loaded during the synchronization with the Axon.ivy Engine and then handled locally by an app on a mobile device (e.g. a smartphone or a tablet). When the form is completed, the mobile app will transfer the entered form data to the server as soon as the connection to the workflow server is back again. In turn, this will resume the task and continue the process execution.

Offline Task in a Process

An Offline Task is generated, when the process engine executes a User Task element whereon an Offline Dialog is configured.

The User Task element provides a different task handling than normal task switches do. On a User Task, when the form data (the actual dialog page) is requested, the corresponding task remains in state suspended. The task state will be changed to resumed when the form data is submitted. Compared to this, a normal Task must be resumed first and after that, an Html Dialog element that follows in process flow, will return the form data.

ActionUser Task elementTask Switch element followed by Html Dialog element
Task picked up from task list
  • Task state remains unchanged.

  • Dialog page from configured dialog is returned.

  • Task state changes to resumed.

  • Process flow continues to Html Dialog element. Dialog page from configured dialog is returned.

Form data submitted
  • Task state changes to resumed.

  • Form data is mapped to dialog data.

  • Dialog is closed, dialog data is mapped to process data.

  • Process execution continues in context of the workflow user that submitted the form.

  • Form data is mapped to dialog data.

  • Dialog is closed, dialog data is mapped to process data.

  • Process execution continues in context of the workflow user that resumed the task.

Table 9.6. Comparison of the execution sequence

Tip

Offline Tasks can also be processed using a normal web browser as client. From a user's perspective they can be processed almost like normal tasks.

Because of the different task handling of a User Task element, the session can be interrupted/terminated after the form data was loaded. Then the form can be processed offline. After reestablishing the connection and creating a new session, the form data can be submitted. This would not work with normal tasks, since they are reset, as soon as the corresponding session (the one that resumed the task) expires.

Note

An offline aware application must manage the loading of the form data for required tasks, the presentation of the forms to the user during offline stages and the submission of the form data when the connection to the engine is established again. The ivy mobile app has full support for offline tasks.

Warning

Because the processing of an Offline Task may happen in parallel by several users, the task assignment should be set with caution. The form-submission of the first user will resume the task and continue the process. Subsequent form-submissions - from any user - will not be processed but responded with an error message.

Process elements that follow a User Task element will be executed in the context of the same task. An error during the execution of these elements will result as an error response to the form submission and the whole user task is set back to suspended.

Tip

Placing an Html Dialog element after an (Offline) User Task element is not a good idea, since it will not be handled correctly by an app that submitted the offline form. Generally it's best practise to place a task switch (Task Switch element of another User Task) as soon as possible after a User Task element.

Offline Dialogs

An Offline Dialog is a special kind of Html Dialog that warrants to be suitable for offline usage.

Ivy treats Offline Dialogs as separate view technology. Only when a User Task element is configured to use an Offline Dialog, it will generate Offline Tasks. Otherwise, normal tasks will be generated. From a technical point of view, an Offline Dialog is the same as a normal Html Dialog. They both are User Dialogs built on top of the JSF technology.

Even though there is no technical restriction - like a validation or similar - an Offline Dialog must omit any features that requires an active server connection before form submission. So, not all JSF features can be used. It's in the responsibility of the dialog developer to ensure the offline capability when developing an Offline Dialog.

Restrictions for the design of offline capable dialogs:

  • The view should not rely on server side state e.g. session attributes, because it is executed solely on the client.

  • The dialog data fields defined as persistent are held in the client view state. So, these fields, together with fields submitted in the form, are available in the dialog logic (methods and events). All other data fields are only available in the start method to initialize the dialog. Anywhere else in the dialog logic, they will be set to null.

  • Owed to the offline capability, Ajax requests to the server are not possible. E.g. auto complete, lazy loading

  • The entered data should be validated before form submission (client side validation). If only server side validation is performed, the user will get a late feedback, expressed as synchronization error when switching from offline to online.

Note

The layout and the styling of an offline capable dialog should consider the client device where it will run. Most probably it will be embedded in an mobile app on a device with a small touch-screen.

Tip

To avoid Ajax on form submission, a p:commandButton can be configured with the attribute ajax="false":

    <p:commandButton value="Proceed" actionListener="#{logic.close}" ajax="false" />    

Geo Location

The mobile app sends the current position of the mobile device to the server. This information is then stored in a location services that are available on the user that has worked with the mobile app and the tasks that have been worked on the mobile app.

Get latest position of a task:

import ch.ivyteam.ivy.location.GeoPosition;
GeoPosition taskPosition = ivy.task.locations().search().findLatest().getPosition();

Get latest position of a user:

import ch.ivyteam.ivy.location.GeoPosition;
GeoPosition userPosition = ivy.session.getSessionUser().locations().search().findLatest().getPosition();

The location service can also be used to store additional locations:

import ch.ivyteam.ivy.location.GeoPosition;
import ch.ivyteam.ivy.location.ILocation;
ivy.session.getSessionUser()
  .locations()
  .add(ILocation
      .create(GeoPosition.inDegrees(47.171573, 8.516835))
      .withType("User Home")
      .withNote("My Home is my Castle")
  );

More information can be found in the Public API in the package ch.ivyteam.ivy.location. It defines the location service and types to create, store and manipulate location information and geo-positions.

Data Storage

Axon.ivy provides multiple possibilities to manage and store project specific data. This chapter provides an overview of all the possibilities with their advantages and disadvantages. Which one should be used depends from case to case.

Content Management

Stores static multi language content like labels, texts, titles, images.

More information can be found in the chapter Content Management.

Web Content Folder

Stores static web files (CSS, JavaScript, Images, JSF-Templates) used in HTML User Dialogs.

More information can be found in the chapter HTML content in the Web Content Folder.

Filesystem

Data can be stores in files. Access and management has to be implemented in the project itself.

HTML User Dialog Resources

Stores static web files (CSS, JavaScript, Images, etc.) that are only used in the HTML User Dialog.

Database

Stores and access data in an external database systems. An own database server is necessary and the database schema must be managed outside of Axon.ivy.

More information can be found in the chapter Db Step.

Persistency (Java Persistence API)

Stores and access data in an external database systems. An own database server is necessary. The database schema can be generated. JPA is a Java standard that is well documented and widely used.

More information can be found in the chapter Persistence.

Web Service

Stores and access data in external systems by using web services.

More information can be found in the chapter Web Service Call Step.

Global Variables

Stores simple name/value configuration pairs. A global variable can have a different value per environment. On the engine there is a UI to change the values of a global variable.

More information can be found in the chapter Global Variables.

Application Custom Properties

Stores simple name/value pairs. Good alternative for storing small amount of data instead using a database.

More information can be found in the Public API ICustomProperties.

User Properties

Stores simple name/value pairs per user. Can be used to store user settings.

More information can be found in the Public API IUser.

Summary

ConceptOverridingProject DependenciesEnvironmentsPublic APIWeb AccessableDesigner UIEngine UIKnowledge
Content Management yesyesnoyes(yes)yesnoNovice
Web Content FoldernoyesnonoyesyesnoNovice
FilesystemnononononononoAdvanced
Html User Dialog ResourcesnonononoyesyesnoNovice
DatabasenoyesyesnonoyesnoAdvanced
Web ServicenoyesyesnonoyesnoAdvanced
PersistencynoyesyesyesnoyesnoExpert
Global VariablesnoyesyesyesnoyesyesAdvanced
Application Custom PropertiesnononoyesnononoAdvanced
User PropertiesnononoyesnononoAdvanced

Table 9.7. 

Overrides

This chapter deals with the concept of overrides and describes the Overrides Editor and the New Override Wizard. Overrides can be used to selectively redefine components of required projects so that replacement components are used at runtime instead. This is often a desired feature if an existing (possibly abstract) project or application is to be tailored for a specific customer or installation.

The Concept of Overrides

Applications are often implemented as a general solution for a problem and consist of multiple (dependent) projects. For many installations or customer projects it is desirable that certain parts of such a generic solution may be redefined in the context of a specific installation or customer.

To permit this, Axon.ivy knows various concepts of context-sensitive redefinitions:

  • Regular redefinition (e.g. for Content Objects and/or Configurations): Simply define an already existing artifact with the same name again in a different project.

  • Redefinition with environments (e.g. for Databases, Web Services, Global Variables): Redefine values and properties of global artifacts depending on the execution context.

  • Redefinition with overrides (e.g. for Sub Processes): Define a replacement component for an already existing component.

This chapter only deals with the third category of artifact redefinitions (overrides).

By defining overrides on project level, the lookup of a certain component can be redirected to a replacement component. When a component is referenced in a process model of that project then the lookup for this component will yield a different component (i.e. the replacement) at runtime instead of the originally referenced component.

Warning

This happens completely independent of the original designers intention and will take place every time a component is looked up.

Case Scope

How is a component looked up? For the lookup of components at runtime, the so-called case scope is crucial. The case scope is determined by the project, in which the current case was started, e.g. where the start of the running business process was invoked. All component look-ups as well as configuration and content management references are processed within the case scope, i.e. the look up of such artifacts always starts at the project that defines the case scope.

Example: The Acme Web shop

As an example, imagine a web shop application. It contains the following (generic) business process:

The main process itself (Order) and each of the depicted sub processes (DoOrder, ProcessOrder, Shipment) are defined in own projects. All of those projects together form a (generic) web shop application, depicted below. The web shop project contains the business process and it's start; the Frontend project contains the DoOrder sub process; the backend project contains the ProcessOrder sub process; the Shipment project contains the Shipping sub process.

In this scenario, regardless of the task that is currently being executed (customer, back office, shipping), the case scope will always be the web shop project, because the business process is started from there.

We now define an additional project, Acme web shop. The new project is dependent on web shop and the intention is to bundle all Acme-specific overrides and adoptions in this project. The already existing projects plus this new project form together a more specific and customized Acme web shop application, with the following project dependency tree:

If the main business process is copied from the web shop project to the Acme web shop project, and if it is ensured that the process request is issued through the Acme web shop project instead of the web shop project, then all tasks of an order case will consequently have Acme web shop as their case scope.

Knowing this, we can now specifically override and redefine Content Objects, Configuration entries or Sub Processes from the original generic web shop application by redefining them inside the Acme web shop project. Afterwards, whenever a business process with case scope Acme web shop is started, then the overridden artifacts and components will be used instead of the original ones, due to the case-scope based lookup mechanism.

General Definition

The following figure illustrates the adaption of an application with overrides in a general way:

Adapting a generic application with overrides

Figure 9.9. Adapting a generic application with overrides

It can be seen that multiple adoptions (Client A, Client B) may be created for a generic main project. Also, each adaption may override different components.

Because Request 1 and Request 2 have different cases scopes, Request 1 (issued through the Client A project) will use the overridden Sub Process x.B' instead of the original x.B; Request 2, however, will use the original x.B Sub Process, because there is no redefinition within the case scope of the Client B project. Likewise the invocation of the Sub Process y/Q will result in the execution of the override y/Q' in Request 2, and the execution of the original y/Q in Request 1.

Note

If it should happen that the business process m/P2 is executed through the main project directly, then no overrides will be applied at all. Since such a "direct" invocation normally results in an unwanted case scope, it should be prevented. The easiest way to do so is the usage of a process facade as described below.

Process Facade

If the override mechanics are to work as intended, it must be ensured that processes are always and solely started from the adapted customer projects to ensure the proper case scope. This requires that all business processes (or rather their request start elements) must be copied to the adapter project.

To simplify this task and to reduce the work to the copying of a single file, it is recommended to employ the process facade design pattern.

Inside the main project of the generic application create a single process (e.g. Main) that holds the start elements of all the elementary business processes of the application. Factor the logic of those processes out into sub processes and call them from the facade process stubs, as illustrated below. With this approach, only one process (the facade) has to be copied to the top-level customer project.

Warning

When factoring out sub processes, please keep in mind that you should not use task switches in sub processes of required projects. As a general recommendation, any factored out sub process should roughly correspond to the contents of a task (or parts of such), but should not span multiple tasks.

Implementing a process facade with process stubs

Figure 9.10. Implementing a process facade with process stubs

The portal website, the workflow UI or whichever other means that are used to start the application's business processes should only show the processes from the copied facade process. As all the out factored Sub Processes will also be available from the adapter project, no further changes have to be made.

Overrides Editor

Overview

The Axon.ivy Overrides Editor shows the registered and active overrides for a specific project. The overrides are listed in 4 different sections: Sub Processes, Content Objects and Configurations.

Sub Process overrides require - for technical reasons - the registration of a mapping (this is done automatically by the New Override Wizard) which maps the original component's identifier to the replacement identifier. This mapping is displayed in the Override Editor and can be deleted by selecting an entry and subsequently clicking on the delete icon in the section's tool bar. When clicking on the wizard icon in the tool bar, a new override mapping of that category can easily be added by entering all necessary information into the opening wizard.

Overrides of Content Objects and Configurations, on the other hand, do not require a renaming and an extra mapping between the original and the overriding component. They are simply created by adding a new Content Object or Configuration entry with the name of a component that already exists in a required project. At runtime, the new component will be found first and thus shadow the original value. For this type of override no special actions are available from the editor; you should use the respective editors (Content Editor and Configuration Editor) to create or delete overrides. The editor shows the overrides of that type for reasons of a centralized overview and for convenience, rather than to provide an interface to edit them.

The Override Editor

Figure 9.11. The Override Editor

Accessibility

Axon.ivy Project Tree > double click on the Overrides node.

Features

Sub Process Overrides

This section shows all Sub Process overrides that are registered for the selected project. You can delete an existing override by pressing the delete icon in the section's tool bar. This will only delete the mapping (and thus the execution of the override) but not the replacement Sub Process itself. You can add new Sub Process overrides by clicking on the wizard icon in the tool bar (this can also be used to "restore" a previously deleted mapping).

Content Object Overrides

This section shows all Content Objects that are redefined in the selected project, i.e. the Content Objects for which there is an entry with the same URI in a required project. At execution time the redefined Content Object will be used.

You can delete overriding Content Objects directly from the list (multi-select a few lines and hit Delete) or use the Content Editor to add new overriding Content Objects.

Configuration Overrides

This section shows all Configurations that are redefined in the selected project, i.e. all Configurations for which there is an entry with the same name in a required project. At execution time the redefined Configuration will be used.

You can delete overriding Configuration entries directly from the list (multi-select a few lines and hit Delete) or use the Configuration Editor to add new overriding Configuration entries.

New Override Wizard

Overview

The New Override Wizard lets you create a new override. The wizard performs two tasks:

  1. It will create an independent copy (snapshot) of the original component with a new name in the current project.

  2. It will create and register a mapping <original,replacement> in the list of overrides that are known to the system. The list of those mappings can later be inspected and edited with the Override Editor.

Note

Please be aware that any Sub Process that is being overridden must have "use own data class" explicitly set in it's inscription. The wizard will not let you create an override of a process if this is not the case, because the "use default data class" setting will result in a different data class inside the target project where the override will be created.

If the wizard refuses to create an override for this reason then you can set an explicit data class in the values tab of the original process's inscription mask.

The New Override Wizard

Figure 9.12. The New Override Wizard

Accessibility

File > New > Override

Features

Original Type

Choose the type of component for which an override replacement should be created.

Original Identifier

Specify the identifier of the original component that should be overridden at runtime. Use the button next to the text field to select from the available Sub Processes. Please note that only components from required projects can be overridden, there is no point in defining an override for a component in the same project (see override concept).

Replacement Namespace

Chose a namespace for the replacement component.

Replacement Name

Enter the name of the replacement component.

Note

If you create an override for a Sub Process, then a copy of the data class of the original component will be created (snapshot) and will be associated with the replacement process. The name of the copied data class will be inferred from the replacement component's identifier (namespace + name).

Finally...

Select whether you want the respective component's editor to open on the replacement component once the override has been created.

Error Handling

Errors are used to model exceptional process paths. With an error the happy path of a process is left. An error is caught by an Error Boundary Event or Error Start Event if their Error Code pattern matches the thrown Error Code.

  • Errors are divided into technical errors (e.g. database connection problem) or business errors (e.g. approval declined).

  • An error is defined by an Error Code.

  • The error may be caught by an “Error Boundary Event” attached to the activity or subprocess, or by an “Error Start”.

  • An Error Boundary Event or Error Start Event with an empty Error Code catches every error.

Error Codes

Error codes are defined as strings. They can be refined by inserting a colon (:). Multiple sub Error Codes can be caught using wildcards (*). Trailing wildcards are optional so the string custom:error is the same as custom:error:*.

Example

If the error code booking:failed is thrown it can be caught with following error code patterns: booking:failed, booking , *:failed . Additionally it can be caught by an empty Error Code that catches all errors.

System Errors

System errors are thrown by process elements like Database Step or Web Service Call Step. Their error codes are set by default and are prefixed with ivy (e.g. ivy:error:database).

Throwing Errors

An error can be thrown explicitly by an Error End Event, or from code executed in IvyScript or Java. System errors (e.g. ivy:error:database) are implicitly thrown by the system.

Error End Event

The happy path of a process can be left by throwing an error with an “Error End”. (e.g. if an approval was declined). The Error End Event throws the error to upper process level, it can't be caught on the same process level.

Error End Events can also be used to re-throw a pre-defined ivy error with a specific error that has a meaning to the business. (e.g. if a webservice is not available)

Error handling in Html Dialog

When an error happens inside of an Html Dialog the handling is slightly different than the default error handling.

Default Html Dialog Error Handling

Basically any thrown error (e.g. an Java exception) is handled inside of the Html Dialog itself. Therefore there is no propagation to the caller process or between Ivy/JSF composites. It is important to handle errors locally in the Dialog Logic to let the user work uninterrupted on the same dialog.

Exit an Html Dialog by an Error End Element

It is possible to exit an Html Dialog by an Error End Element. This is useful to leave the happy path of the calling business process. The throwing Error End Element must be located in the Html Dialog Logic of an Html Dialog Page (not an Component).

IvyScript or Java Code

Unhandled Script exception

If an unhandled exception occurs while executing IvyScript or Java code then the calling process element throws an error with the Error Code ivy:error:script. On the error object the causing Java exception is available as technical cause.

Throwing an error programmatically

An error with a certain Error Code can be thrown using the following IvyScript code:

import ch.ivyteam.ivy.bpm.error.BpmError;

BpmError.create("mystock:empty").throwError();

An error with a certain Error Code can be thrown using the following Java code:

import ch.ivyteam.ivy.bpm.error.BpmError;

throw BpmError.create("mystock:empty").build();

Elements throwing System errors

The process elements Program Interface, Database, WebService and E-Mail throw system errors. If an exception or timeout occurs on these elements it can be caught using a matching Error Code or using a directly addressed Error Start Event. On the Error Start process element more information about the error can be accessed via the variable error and the legacy variable exception.

Catching Errors

Errors can either be caught by Error Boundary Events or Error Start Events.

An error is caught in the following order:

  1. By an Error Start Event directly addressed in the element's inscription mask. (If available on the inscription.)

  2. By an Error Boundary Event attached directly to the activity the error comes from.

  3. By an Error Start Event on the same process level if not thrown by an Error End Event.

  4. By an Error Handling on the next higher process level, starting there with step 2 until the top level process is reached.

  5. By a Project Error Process in the top-level project.

  6. If the error is not caught it is displayed to the user on the standard error page.

Note

Each process - including the embedded subprocess - is a separate process level.

Error Boundary Event

An “Error Boundary Event” catches errors which were thrown from the attaching activity or subprocess if the configured Error Code matches the thrown error.

Error Start Event

An “Error Start” catches unhandled errors which were thrown in the same process or inside a subprocess if the configured Error Code matches the thrown error.

Loop Prevention

To prevent endless process execution trough an inappropriate error handling, the ivy process engine detects loops during the error handling. If the engine detects a loop the error handling will be continued on the next higher process level with the new error code ivy:error:loop, to interrupt the cycle.

Loop detection is done on error catching elements (Error Start Event and Error Boundary Event). The engine checks if there was already an identical execution of the catcher at this process level. Identical means: Same process request, same throwing element (including its process callstack) and same catching element (including its process callstack).

Lets illustrate this with two use cases:

Use Case 1

The process element throws an BpmError. The Error Boundary Event will catch the error and call the process element again. In this case, the loop detection will interrupt the process when the Boundary Error Event was reached the second time. This would also be the case, when the throwing error element is located in a composite or callable process.

Use Case 2

In this case, the loop detection will interrupt the process 'callInCall1' after the second error handling. The process will be continue by the error handling on the caller process with the error code 'ivy:error:loop'. The process will end on the End Element named 'done'.

Project Error Process

A Project Error Process catches uncaught errors from the whole project. The name of a Project Error Process must start with Error and has to reside in the top-level process group Processes. It can contain one or more Error Start Events.

Note

The process data of the throwing process (i.e. the value of the in variable) is not available in the Error Start of a Project Error Process.

Error Object

The error object provides the following information about the error that was caught:

  • Unique Error ID

  • Error Code

  • Technical Cause (Java Exception)

  • Process element

  • Process call stack

  • User defined error attributes

For more information see the Public API of BpmError

Rule Engine

A Rule Engine is basically a software system that maintains and executes a given set of rules. More specifically, rules in a rule engine are usually described in a declarative way. Mostly in the notion of conditions and actions. Or in more developer friendly words, it's a bunch of if-then statements. For example, let's take a simple rule for computing the premium for a car insurance:

if owner.livesInDodgyArea then
    if car.price < 50000 
        premium += 100
    else if car.price < 100000 then
        premium += 200
    else 
        premium += 300

if owner.livesInBumpyStreetConditionArea then 
    if car.type == SPORTSCAR then
        premium += 500
    else if car.type != SUV AND car.type != TRUCK then
        premium += 100;

You can imagine that with this approach you end up pretty soon into the Spaghetti-code anti-pattern. You have to take care of all the dependencies and relations between all the facts on your own leading to a forever-growing if-then statement that is almost impossible to maintain.

In a rule engine, you create simple standalone rules and you let the rule engine decide what to fire when. The subtlety is that rules can be written in any order, the engine picks the ones for which the condition is true, and then evaluates the corresponding actions. So instead of the massive if statement, you write the following rules:

if owner.livesInDodgyArea AND car.price < 50000 then premium += 100
if owner.livesInDodgyArea AND 50000 < car.price < 100000 then premium += 200  
if owner.livesInDodgyArea AND car.price > 100000 then premium += 300  
if owner.livesInBumpyStreetConditionArea AND car.type == SPORTSCAR then premium += 500
if owner.livesInBumpyStreetConditionArea AND car.type != SUV OR car.type != TRUCK then premium += 100

Because of this simplifaction it might even be possible for non-developers to define or configure the rules (a little bit of tool support helps too).

In short, a rule engine helps you to decouple your production rules from the rest of the code and makes it much more maintainable for both developers and domain experts. But remember that everything has its flip side. Adding a rule engine means adding another level of complexity into your architecture (you replace plain code with a new system). And as the size of your rule sets grow, so does the potential impact to the performance. For more information about rule engines, please refer to one of the many available resources in the Internet.

Tip

In Axon.ivy, we integrate the open source rule engine Drools to give you the flexibility to use a rule engine if you want. We wrapped some of the most basic features of Drools into our own UI and API. If you need more than that, then simply use the normal full blown Drools API.

Decision tables and DRL files

We support two formats for defining rules: decision tables and rules written in the Drools Rule Language (DRL). A decision table is like the name says a table that can contain many rules. The columns usually make up the variables of the preconditions/actions whereas each row in the table specifies one rule. Let's see the decision table for our example:

Pre-ConditionAction
lives in dodgy areaprice minprice maxlives in bumpy street areatype of caraddition to basic premium
yes050000no-100
yes50000100000no-200
yes100000-no-300
no--yessports car500
no--yessedan100

Table 9.8. Decision table

Decision tables are simple to understand and maintain, especially for domain experts. On the other hand, the more variables and rules you use, the more your decision table bloats up and makes it hard to maintain.

The Drools Rule Language (DRL) on the other hand is more oriented towards developers. It is the native rule language of Drools. Let's see a rule in DRL:

rule "Luxury cars in dodgy areas cost a nice extra premium"

when
    c: Car( dodgyArea==true, price > 100000 )
then
    c.premium += 300;    
end

Use the context menu entry New, the menu File > New or the tool bar button New to create either a new decision table or a DRL rule file. The rules will be shown in the project tree below their root folder Rules.

Execute rules

Before we see how you can use the rule engine to do something, let's once more make very clear the difference between logic in source code and using a rule engine. In code you have to explicitly define which part of the code to call, you are in control and responsibility to do the right things in the right order. In a rule engine this is different, you simply tell the rule engine to execute and it does find out itself which rules apply and which rules to fire.

To run the rule engine you have to use the Public API of the rule engine, e.g. in a script step or in a Java class. Use ivy.rules.engine to access the execution part of the rule engine API. First you will need to create a so called rule base. A rule base is a container in which you can load multiple rule files.

IRuleBase myRuleBase = ivy.rules.engine.createRuleBase();
myRuleBase.loadRulesFromNamespace("my.rule.name.space");

Tip

In the designer, the rule files are hot deployed into rule base. So when you are running your process and you change a rule file that is loaded in a rule base, then the Designer automatically unloads the old version of the rule file and loads the new one.

Tip

Use the namespace to group rule files that belong together and use the corresponding API to load all rule files of the same namespace together. You can also load the rule files from your dependent projects. And you can override rules and rules files by having a rule or rule file with the same name in the overriding project.

Now, what you need too is an instance of the data model that you used in the pre-conditions and the actions of your rules. You can either give the root object of your data or a list of objects. So, create a session, load the data into it and execute:

myRuleBase.createSession().execute(out.myDataForTheRules);

You should now see the result of the actions applied in the data that you passed into the rule engine before.

Tip

You can use the Public API in JUnit tests directly. Use this to test standalone rules and even groups of rules or all your rules with a pre-defined input and assert if the output matches your expectations. You must extend from AbstractRuleEngineTest and be aware that only rule files inside the same project can be resolved.

Demo project

To help you learn how to use the rule engine integration, we created a small demo project called RuleEngineDemos that is bundled together with the Designer.

Extensions

This chapters shows how easily a process or the Axon.ivy engine itself can be customized with your own logic.

Extendible Process Elements

Axon.ivy comes with four generic process elements that can be used to address particular execution behaviour requirements none of the standard process elements can fulfill.

All generic process elements contain a tab in which a Java class can be selected. The Java class implements the actual execution behaviour. Some standard implementations are shipped with the Axon.ivy core, and with these elements developers are able to specify their own implementation as part of the project.

These generic elements are:

“PI (Programming Interface) Activity”

Executes generic Java code (may interact with a remote system).

“Program Start”

Triggers the start of a new process upon an (external) event.

“Wait Program Intermediate Event ”

Interrupts process execution until an (external) event occurs.

“Call & Wait ”

A combination of the PI and Wait process elements.

Tip

Sample implementations of custom process elements can be found on GitHub in our open source repository, e.g.

https://github.com/ivy-supplements/bpm-beans/tree/master/ldap-beans

New Bean Class Wizard

Overview

With the New Bean Class Wizard you can create a Java class that implements the interface of one of the extendible process elements. Optionally, it can also generate a UI editor for the configuration of the event for the corresponding bean. The generated Java class contains example code on how to implement the Java bean.

New Bean Class Wizard

Figure 9.13. New Bean Class Wizard

Accessibility

Process Editor > inscribe > Inscription Mask > ... > Java Class to execute >

Provide your own process elements

Instead of using the generic extendible process element with your Java class, you can go one step further and implement your own process elements, available in the process editor palette.

It is recommended to define a name and icon for your custom process element implementation. This makes the element easier to recognize and separates the technical implementation from the project you are currently working on.

To implement your own process elements in an Eclipse bundle and added this way to the Axon.ivy core, you need to implement the extension point IExtensibleStandardProcessElementExtension. This is not needed if your custom process element is only used in your project.

Once the element is available on the palette, you can customize it even further by providing detailed names (IProcessElementUiInformationExtension), palette appearance informations (IIvyProcessPaletteExtension) and/or classpath dependency configurations (IIvyProjectClassPathExtension).

Axon.ivy extensions bundles (Eclipse plugin)

In order to provide an Axon.ivy extension for the Designer or Engine you need to provide it as an Eclipse plugin.

Development

You can create your own Eclipse plugin in the Axon.ivy Designer by following these steps:

  1. Start Axon.ivy Designer

  2. Switch to the Plug-in Development Perspective. Menu: Window > Open Perspective > Other... > Plug-in Development

  3. Create a new Plug-in Project. Menu: File > New > Project .... In the appearing dialog:

    • Choose Plug-in Project.

    • Press the Next button.

    • Enter a project name.

    • Press the Next button.

    • Enter the Plug-in Properties.

      PropertyDescriptionExample
      Plug-In IDIdentifier of the plugin. Must be unique. This identifier must be specified in the *.extensions file in the bundle attributes.ch.ivyteam.ivy.example
      Plug-In VersionThe version of the plugin.1.0.0
      Plug-In NameThe name of the plugin. The name is used for documentation only.Example
      Plug-In ProviderThe provider of the plugin. The provider is used for documentation only.ivyTeam / Soreco Group

      Table 9.9. Plug-in Properties

    • Press the Finish button.

  4. In the appearing editor click on the Extensions tab. In the section All Extensions press the Add button. Un-tick the box Show only extension points from the required plug-ins. From the list of extension points choose the one you want to provide an extension for. Press the Finish button. You may need to confirm adding a new plug-in dependency. Save the changes.

  5. Select the added extension point from the list in the section All Extensions. Select the added sub entry. In the section Extension Element Details click on the link class*.

  6. A New Java Class dialog appears. Specify the name of your extension class in the Name text field and the package name in the Package text field.

  7. Write your extension class by implementing the extension point interface (see Extension points)

  8. Switch back to the META-INF/MANIFEST.MF file editor. Choose the Overview tab and click on the link Export Wizard. As Destination Directory choose the dropins directory of your Axon.ivy Designer or Engine installation. Press the Finish button. Your plugin is created into the dropins/plugins directory.

Installation

Follow these steps to install your extensions in an Axon.ivy Designer or Engine:

  1. Stop the running instance (if applicable).

  2. Copy your plugin (bundle) that contains your extension classes to the dropins directory inside the Axon.ivy Designer or Engine installation directory.

  3. Start the Axon.ivy Designer or Engine.

Tip

If your extension is not active as expected, consult the dropins/README.html.

Extension Point Reference

Axon.ivy supports the following extension points:

Extension PointDescription
ch.ivyteam.ivy.server.IServerExtensionA Server extension can be used to start and stop your code when the Axon.ivy Engine is started or stopped. Server extensions can be accessed from Process Start Event and Process Intermediate Event Beans and also from every process element using the ivy.extensions environment variable.
ch.ivyteam.ivy.process.element.IExtensibleStandardProcessElementExtensionThis extension point can be used to define your own process elements based on the process elements Program Interface (PI), Start Event, Intermediate Event and Call&Wait. The process element will appear in the community drawer of the process editor unless defined with an IIvyProcessPaletteExtension.
ch.ivyteam.ivy.designer.process.ui.editor.palette.IIvyProcessPaletteExtensionAdds new groups and process element entries to the process editor palette.
ch.ivyteam.ivy.designer.process.ui.info.IProcessElementUiInformationExtensionProvides labels (name, description) for your own process elements.
ch.ivyteam.ivy.java.IIvyProjectClassPathExtensionAdds libraries or classes from bundles to the ivy project class path. This extension point allows to add libraries or classes to the compile and the runtime class path. This is useful if you want to provide your own classes in a eclipse bundle and want to access these classes from ivyScript or use them as Program Interface (PI), Start Event, Intermediate Event and Call&Wait bean.
ch.ivyteam.ivy.designer.process.ui.inscriptionMasks.BpmnInscriptionEditor Provides advanced UI editor tabs that can be implemented in any supported technology stack (e.g. SWT instead of Swing).

Table 9.10. Axon.ivy Extension Points

Deployment

This chapter explains how an Axon.ivy Designer Project can be deployed to an Axon.ivy Engine. Before deploying an Axon.ivy project it is important to understand some major concepts and terms of the Axon.ivy Engine. The following chapter introduces these concepts and terms.

Application

On the Axon.ivy Engine, applications can be configured. An application spans up an environment in which roles, users, external databases, tasks, cases and process models exist. Applications are completely independent of each other. E.g. a user of one application can not work on a task of another application.

Process Model

Within an application multiple process models can be configured. A process model on the Engine corresponds to an Axon.ivy project on the Designer. The difference is that a process model may hold multiple different versions of the same Axon.ivy project. A process model version - as its name suggests - is a version of an Axon.ivy project. In fact this version represents the state of an Axon.ivy project at the time it was deployed on the Engine.

Process Model Version

A process model can have multiple versions called process model versions. These versions allow to change an Axon.ivy project without worrying about the compatibility of currently running cases on the Engine. How does this work? When an Axon.ivy project has been finished or reached a milestone, it is going to be deployed as the first process model version. Users can use this project, they start processes. Some of the processes may last long time (weeks, months, or even years). While these processes (e.g. cases) are running, the project may be enhanced and might have now incompatible changes to the first version. Now the changed project can not be deployed to the first version but to a new configured version. Consequently old cases must not be stopped, they will be still executed within the first process model version. Meanwhile new cases are started from the new deployed version.

A process model version has a release state. The release state of a process model version is responsible how the version is used by the system. The most important release state is the state RELEASED. Within a process model only one version can be in this state. All processes that are started in a process model are started in the released process model version! A complete list of release state can be found in the following list:

NameDescription
PREPAREDThe process model version has been created and the project may already have been deployed. However, the process model version is not yet used.
RELEASEDThe process model version is the currently released version. This means that all new processes are started in this version. Program Starts and Web Service Processes are only active for process model versions in this state.
DEPRECATEDThe process model version has previously been in state RELEASED, but then another version was released. Therefore, this version is now not in RELEASED state but in DEPRECATED state. All cases that were started in this process model version will continue to run in this version. As soon as all cases of this version have been ended, the state will change to ARCHIVED automatically.
ARCHIVEDThe process model version has previously been in state RELEASED, but then another version was released, and running cases has been finished in this process model. Consequently, this version is now not in RELEASED state anymore but has been ARCHIVED. Actually the engine administrator can change a process model version from state ARCHIVED back to state RELEASED if necessary.
DELETEDThe process model version has been deleted. All project data belonging to this version has been deleted.

Table 9.11. Release states of process model versions on Axon.ivy Engine

The following diagram shows all release states and state changes that are possible:

Configuration Example

The following table shows an example of how applications, process models and process model versions on an Axon.ivy Engine can be configured.

ApplicationProcess ModelProcess Model VersionDescription
Company1  Application for company1. All users of the company are automatically imported to this application from the company's active directory server.
 HRM Human Resource Management process model. Corresponds to the Axon.ivy project called "HRM".
  V1The first version of the HRM project was released in February 2008. This version is deprecated. There are still cases running in this version
  V2The second version of the HRM project was released in April 2008. This version is released. All new processes are started in this version.
  V3The third version of the HRM project was created in January 2009. This version is prepared, but not used productive. It will be released on the first of September 2009.
 Finance Finance process model. Corresponds to the Axon.ivy project Finance.
  V1The first version of the Finance project was released in August 2007. This version is released. All new process are started in this version.
Company2  Application for company2. The users of the company are managed by the Axon.ivy Engine.
 HRM Human Resource Management process model. Corresponds to the Axon.ivy project called "HRM".
  V1The first version of the HRM project was released in April 2008. This version is released, so that all HRM processes of company2 run and are started in this version.

Table 9.12. Configuration Example

Axon.ivy Project Deployment

To deploy an Axon.ivy project to the Axon.ivy Engine execute the following steps:

  1. Export all files of the project you want to deploy to a zip file using the Export wizard of Axon.ivy Designer (See next section).

  2. Copy the zip file with your project files to the Axon.ivy Engine.

  3. Start the Engine Administrator application on the Axon.ivy Engine

  4. Choose or create an application

  5. Choose or create a process model

  6. Choose or create a process model version

  7. Open the detail page of the process model version and find the section Deployment.

  8. Press the Deploy button to start the deployment wizard.

  9. On the first step of the deployment wizard choose the zip file with your project files and follow the wizard to deploy your project.

Tip

More information about the deployment of Axon.ivy projects or applications, process models and process model versions can be found in the Axon.ivy Engine Guide.

Export all Project Files to a ZIP-File

For the deploying of a project it is useful to export all files of a project to a zip file. This can be done with the export wizard of Axon.ivy Designer. Start the export wizard either by using the menu File > Export ... or by using the context menu Export ... in the Ivy Project Tree on a selected project.

Export Wizard

Figure 9.14. Export Wizard

On the export wizard select General > Archive File. Then press the Next > button.

Export Wizard

Figure 9.15. Export Wizard

Choose the project you want to deploy (export). Only select one project because the Deployment wizard can only handle one project in a zip file. Specify the zip (archive) file and press the Finish button. The created zip file can now be used to deploy your project to the engine.

Continuous Integration

Ivy Projects are designed to be built on a continuous integration (CI) server like Jenkins.

Maven build plugin

The project-build-plugin is a Maven plugin that can build Ivy Projects on a developer machine or on a continuous integration server. The plugin provides the following main features:

  • Compilation of Ivy Projects

  • Testing of unit tests against an Ivy Project or the Ivy core classes

  • Packaging of built Ivy Projects as IAR (ivy archive) artifacts

  • Installation of IAR artifacts into the local Maven repository

  • Deployment of IAR artifacts to an Axon.ivy Engine

Runtime

The Designer has a built in Maven runtime that allows to start Maven with zero initial configuration effort. A local maven build can be started as follows:

  1. Switch to the Java perspective

  2. Expand an Ivy Project in the Ivy Project Tree view

  3. Open the context menu of the file 'pom.xml' by right clicking it

  4. Navigate to 'Run as' > 'Maven install'

Configuration

Ivy Projects declare its ids and dependencies in the “Project Deployment Descriptor”. This deployment descriptor can be easily edited with the corresponding ivy editor and is stored as Maven Project Object Model (POM.xml). Therefore each Ivy Project has by default the pom.xml which is needed by maven to build it.

However advanced Maven users can adjust this default configuration and use additional Maven plugins or dependencies in the pom.xml. But not all POM entries should be modified, some are required or limited in usage in Ivy Projects:

  • <groupId/> and <version/> Must be set in every Ivy Project POM. It can not be inherited from a parent POM (even tough this is valid in plain Maven).

  • <packaging>iar</packaging> Provides the custom Ivy Project lifecycle, must not be modified.

  • Dependencies with <type>iar</type> will be manipulated by the “Deployment Descriptor Editor”. Therefore additional configurations like the <scope> could get lost trough the simple editor usage.

  • Values that can be manipulated with the simple “Deployment Descriptor Editor” can not contain Maven properties. For instance <version>${myVersionProp}</version> is prohibited.

  • The version must be qualified like <version>5.0.0-SNAPSHOT</version>. A version like <version>5-SNAPSHOT</version> is prohibited.

Technical documentation

  • The detailed plugin goal and parameter documentation is on Github.io

  • The source code of the ivy project build plugin is available on Github.com.

Continuous Integration Job with Jenkins

The following steps are needed to build an Ivy Project on a Jenkins CI server.

  1. Install Jenkins as described in the Jenkins Wiki

  2. Install a Maven runtime in Jenkins via Manage > Configure > Maven > Maven installation > Choose auto installation

  3. Create a new Jenkins Job. Select "Maven-Project" as job style.

  4. Provide a link to the source code of the Ivy Project in the Source-Code-Management section

  5. Configure the goals clean verify in the Build section

  6. Save the Job and Run it

Miscellaneous

This chapter deals with several concepts and features that are integrated into Axon.ivy to leverage user convenience and experience.

In a workspace with many large projects it is sometimes hard to find specific Ivy elements. Then a powerful search mechanism can save the day. To use the Axon.ivy search, just click on the symbol in the toolbar to open the search dialog. In the dialog that opens navigate to the Axon.ivy tab. At present, searching for CMS content objects, Data Classes / Entity Classes, Processes / Process Elements is supported by Axon.ivy.

The search page

Search string

Enter here the string you are searching for. You may use two wild-cards: The * (star) for any sequence of characters (may be empty too). and the ? (question mark) for a single character (e.g. a*b matches each entity that starts with "a" and ends with "b" and has 0, 1 or more characters in between whereas a?b matches all strings with a length of three that start with an "a", end with "b" and has one character in the middle)

Search For / Search In

Select for what kind of entities you are looking for. Depending on the chosen type, you can specify in which properties of the entity the search string (see above) is searched in. If you select more than one property, then be aware that the search string must occur only in one of the chosen properties.

Scope

You can decide whether you want to search in the full workspace or only in the enclosing projects (the projects that are selected in the Ivy Projects View). If you choose enclosing projects you may select whether you want to include searching in dependent or required projects (see the Project Deployment Descriptor chapter for more details about how you can define and use project dependencies). The tool tip text tells which projects are currently selected.

Recreate indices

The search indices in Ivy are automatically updated if you add, edit or delete entities. However, if you want to recreate the search indices hit this button and all indices are deleted and recreated from scratch in the background. Please be aware, that searching during the time of index creation may not return correct results.

Note

You may use as well other search facilities within this dialog to search for parts that are not covered by the Axon.ivy search page. e.g. if you write your own Java classes in the Axon.ivy Designer you may use the Java search.

The search result view

After clicking on the search button, the search results are collected in the search result view. Double-click on matching entries and the corresponding resource is opened in its editor.

You can change the presentation layout for your search results by selecting a layout from the result view's menu:

For standard searches, only Project and Namespace grouping is available.

Update Notification

When newer Axon.ivy versions are available a dialog appears after starting Axon.ivy Designer. The dialog contains information about the new versions and where those can be downloaded.

Use the checkboxes provided on the dialog if you don't want to see the dialog again either for the same versions or for any new versions.

If you want to check for new versions manually use the menu Axon.ivy > Check for Updates ...

Note

While checking for new versions the following statistic information are sent to the update server. These information are only used to improve the product.

  • Current designer version

  • Operating system information (name, version, architecture, number of processors)

  • Java memory information (maximum heap memory, maximum non heap memory)

  • JVM (Java virtual machine) information (version, vendor, name)

  • Host information (host name, SHA-256 hashes of IP address and MAC address to identify the host without being able to read the original IP address and MAC address itself)

Data Caching

Axon.ivy offers a feature to store data temporarily into a data cache in the engine's memory. If you want to read data that stays unchanged for some time, you do not need to re-read the data every time you need to access it. If this data is read by long-running queries from a database or by calling a slow web service, you can gain a lot of performance by caching this data. The database step and the web service step natively support Data Caching (see the Data Cache Tab for more information), for other data you can access the Data Cache API by IvyScript.

Caches

Data that is cached is always stored in a data cache entry. This can be the result of a database query or of a web service call if you use the Data Cache Tab on the database step or on the web service step. But you can also store any arbitrary object into a data cache entry by using the Public API. Entries are identified by a textual entry identifier.

Entries are organised into groups. An entry always belongs to exactly one group, you cannot store the same entry in more than one group. In other words, the identifier of an entry must be unique in its group. If two entries in the same group have the same identifiers, then they are identical. Like entries, groups are as well identified by a textual group identifier. Use groups to store cache entries with similar data. This simplifies the invalidation of related data, see chapter invalidation below.

A Data Cache is a container for multiple groups. The identifier of a group must be unique in its data cache. If two groups in the same Data Cache have the same identifiers, then they are identical.

Data Caches always have a scope. A scope defines the boundaries of a specific Data Cache and as well the life cycle of the Data Cache depends on its scope:

ScopeType of cached data Multiplicity of Data CacheData Cache life cycle startData Cache life cycle end
ApplicationGlobal data that is related to the application One per applicationApplication creation or engine startApplication deletion or engine stop
EnvironmentGlobal data that can vary for different environments, e.g. if you are using tenants or different configurationsOne per application and environmentEnvironment creation or engine startEnvironment deletion, application deletion or engine stop
SessionData that is related to interactions within the actual sessionOne per session and environmentSession startSession end

Access and Life Cycle

Cache entries and groups are created the first time they are accessed - the first time the process step with the data cache entry is executed - and they are destroyed when the scope of cache entries or groups reach the end of their life cycle. For the scope Session this is the logout of the user of the session or the session timeout, for the scopes Application and Environment this is when the application is terminated or inactivated.

Cache entries and groups are resolved by their identifiers. You can put different cache entries into one group by using the same group identifier for all entries. You can use the same data cache entry for multiple steps by using the same group and the same entry identifier for all entries. This is very useful for data that almost never changes, you can load the data into the cache once at the beginning of the scope's lifetime and read it from the cache from every step in all processes thereafter.

Invalidation

In order to take into consideration changes in the data that is handled by the cache entries, it is possible to invalidate cache entries and as well whole groups either on request or after a configurable period of time. Thereby, invalidation means that only the value of the data cache entry is deleted, but not the entry itself. The next time a step referring to this data cache entry is executed, the value of the data cache is loaded again.

You can invalidate an entry, a group and even the whole cache explicitly in the Data Cache Tab of inscription masks of the process steps that use data caching or by an IvyScript API call. Otherwise you may specify a period as a lifetime or fixed time of day for invalidation. The lifetime period starts when the group/entry is loaded the first time. A background job is responsible to invalidate entries/groups when their lifetime has ended. If you set a fixed invalidation time, the corresponding entry or group is invalidated once per day at that time. By invalidating a group, all its contained entries are invalidated and similarly invalidating the whole data cache does invalidate all groups and therefore as well all entries.

Eclipse Plugin Mechanism

You need a database frontend in Axon.ivy? Or editing support for any other programming or data declaration languages such as C/C++, PHP or XML? Or you have UML models to view? No problem at all.

Axon.ivy is based on the widely used Eclipse platform which offers a sophisticated plugin mechanism to integrate third-party modules. In these days, Eclipse which originally has been developed as an IDE for Java programmers evolved to a large and vibrant ecosystem and is used for a triad of different tools and systems in almost every work sector. Therefore a huge community exists that offers plugins (open source and commercial) and even web sites (Eclipse Marketplace) for browsing and searching these plugins arose in the past years.

And the conclusion, you can use all these plugins and integrate them into your Axon.ivy installation to interact seamlessly between your favourite plugin set and the built-in Axon.ivy features.

Note

Please follow the installation instructions of the specific plugin to integrate it into your Axon.ivy installation

System Events

Axon.ivy offers the concept of system events, which can be understood as messages that are broadcasted across the Axon.ivy installation. While Axon.ivy itself (e.g. the workflow subsystem) generates events that interested participants may subscribe to (e.g. to be informed when a case is created or finished), it is also possible for implementors to define their own events and to broadcast them to any component that might be interested. Since this mechanism is session- and workflow independent, it can also be used to implement inter-session communication (within the same Application).

Concept and general usage

System events are messages that are broadcasted across the Axon.ivy system and that will be delivered to any interested party. System events have a name and are categorized, and they may carry an optional parameter object. System events can only be sent within the same Application on an Axon.ivy Engine.

Currently two categories are defined: SystemEventCategory.THIRD_PARTY and SystemEventCategory.WORKFLOW. The category THIRD_PARTY can be used to send (and receive) system events that are generated by integrated third party applications (or processes). The category is reserved exclusively for this purpose; i.e. the Axon.ivy Engine will never generate any events of this type.

The Axon.ivy system itself currently only generates events of the category WORKFLOW. Inside this category, events with the following names are generated:

  • WorkflowSystemEvent.TASK_CREATED

  • WorkflowSystemEvent.TASK_CHANGED

  • WorkflowSystemEvent.CASE_CREATED

  • WorkflowSystemEvent.CASE_CHANGED

All of those events carry a parameter object of the type WorkflowSystemEventParameter which gives access to the identifiers of the workflow objects that have been created or modified. More system defined categories and events can be expected in the future.

To send system events, client and/or third party applications must first create a SystemEvent object and then get a hold of an IApplication object, which offers the method sendSystemEvent(SystemEvent event). Only events of the category THIRD_PARTY can be sent by process applications, attempts to send system events of different categories will result in an error.

To receive system events, clients must implement the interface ISystemEventListener and must then register themselves on an IApplication object using the method addSystemEventListener(EnumSet<SystemEventCategory> categories, ISystemEventListener listener). It is strongly recommended, that the similar remove method is used, as soon as clients are no longer interested in a specific event category.

Clients should only listen to system events they know the name of, all other events should be ignored. Clients should handle received events as fast as possible, because handling will block the delivery of events to other receivers. Also the received parameter object should never be modified (it shouldn't be modifiable in the first place), since this may affect the handling by other receivers which will consequently receive a modified event object.

In Java, the handling of system events generally results in code similar to the following:

/** 
 * Registers this participant for workflow system events.
 */ 
 public void registerForWorkflowEvents(IApplication application) 
 { 
    application.addSystemEventListener(EnumSet.of(SystemEventCategory.WORKFLOW));
 }

/** 
 * Unregister this participant for all system events.
 */ 
 public void unregister(IApplication application) 
 { 
    application.removeSystemEventListener(EnumSet.allOf(SystemEventCategory.class));
 }

/** 
 * Implementation of ISystemEventListener.handleSystemEvent(...) 
 * Events will only be delivered for the categories that this listener registered for 
 */ 
 public void handleSystemEvent(SystemEvent event) 
 { 
    String eventName = event.getName(); 
    if ("thirdparty.mysystem.myevent".equals(eventName)) 
    { 
        // do something 
    } 
    else if (WorkflowSystemEvent.TASK_CHANGED.equals(eventName)) 
    { 
        // do something 
    } 
    // else: ignore event 
 } 

 /**
  * Distribute a new system event to all interested/registered listeners of my event.
  * MyEventParameter can be of any (serializable) type, the type is part of the event definition,
  * clients will have to cast accordingly.
  */
 public void sendMyEvent(IApplication application, MyEventParameter param)
 {
    	SystemEvent event = new SystemEvent(SystemEventCategory.THIRD_PARTY, "thirdparty.mysystem.myevent", param);
    application.sendSystemEvent(event);
 }