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.

Rich Dialog User Context

Stores UI settings per Rich Dialog and user. E.g. store the last choosen entry of a combo box to preselect it when the dialog is opened again.

More information can be found in the chapter Rich Dialog User Context.

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
Rich Dialog User Contextnononoyesno(yes)noExpert

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 Rich Dialogs and/or 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, Rich Dialogs 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 Rich Dialog x.B' instead of the original x.B; Request 2, however, will use the original x.B Rich Dialog, 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: Rich Dialogs, Sub Processes, Content Objects and Configurations.

Rich Dialog and 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).

Rich Dialog Overrides

This section shows all Rich Dialog 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 Rich Dialog itself. You can add new Rich Dialog 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 (Rich Dialog or Sub Process).

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 Rich Dialogs or a 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 chapter explains how Axon.ivy can be extended by customers or 3rd party software development companies.

Extendable Process Elements

Axon.ivy contains four process elements that can be extended with your own implementation. These process elements are:

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 required interface for an extensible process element. It can also create an UI editor for the configuration of the event for the corresponding bean. The Java class that is created contains example code about how to implement the bean.

New Bean Class Wizard

Figure 9.13. New Bean Class Wizard


Accessibility

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

Features
Source Folder

The source folder the new Bean class should be created in.

Package

The Java package the new Bean class should be created in.

Name

The name of the new Bean class.

Interfaces

The interfaces the new Bean class should implement.

Generate comments

Should the new Bean class contain Javadoc comments?

Create UI Editor Class

Should an UI Editor class for the new Bean class be created?

Provide your own process elements

It is possible to provide your own process elements to Axon.ivy. The process elements you provide are based on the standard extensible process elements Program Interface (PI), Start Event, Intermediate Event and Call& Wait. The bean class of your process elements are hard coded and cannot be changed on the inscription masks.

To define such process elements use the extension point ch.ivyteam.ivy.process.element.IExtensibleStandardProcessElementExtension. After you have implemented this extension point, Axon.ivy knows about your process elements but they do not appear on the user interface.

To add your process elements to the process editor palette use the extension point ch.ivyteam.ivy.designer.process.ui.editor.palette.IIvyProcessPaletteExtension.

Now your process elements are visible on the palette, but the labels for the process elements are not yet defined. Use the extension point ch.ivyteam.ivy.designer.process.ui.info.IProcessElementUiInformationExtension to define the labels (short name, name and description) of your process elements.

Last but not least, you have to ensure that Axon.ivy can load the bean classes of your process elements. Use the extension point ch.ivyteam.ivy.java.IIvyProjectClassPathExtension to add the classes of your bundle to the Axon.ivy project runtime class path. You can now use your own process elements in Axon.ivy.

Tip

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

Axon.ivy Extension Mechanism

There are other ways to extend Axon.ivy than with the process elements mentioned above. You may want to start your own code during the startup of the Axon.ivy Engine, for example to connect to your ERP system. You may have a product that administrates your users and you want to use the same users also in Axon.ivy. For such kind of extension or integration Axon.ivy provides an extension mechanism that allows the execution of 3rd party code on some points. These points are called extension points.

Build an Axon.ivy extension bundle (Eclipse plugin)

To provide an Axon.ivy extension you have to build an Eclipse plugin for the Axon.ivy Designer or Engine.

You can build your Eclipse plugin in the Axon.ivy Designer with the following 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 plugin1.0.0
      Plug-In NameThe name of the plugin. The name is used for documentation onlyExample
      Plug-In ProviderThe provider of the plugin. The provider is used for documentation onlyivyTeam / Soreco Group

      Table 9.9. Plug-in Properties


    • Press the Finish button.

  4. In the appearing editor click on the Dependencies tab. In the section Required Plug-ins press the Add button. From the list of plugins choose the one that provides the extension point you are going to extend right now. Press the Ok button.

  5. Switch to the Extensions tab. In the section All Extensions press the Add button. From the list of extension points choose the one you want to provide an extension for. Press the Finish button.

  6. 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*.

  7. A New Java Class dialog appears. Enter the name of your extension class into the Name text field and the package where it should be located into the Package text field.

  8. Write your extension class implementing the interface that the extension point requires (see Extension points)

  9. 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

You have to do the following steps to install your extensions in Axon.ivy Designer or Engine:

  1. Stop the running instance if any.

  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.
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. The extension point allows to add libraries or classes to the compile and the runtime class path. This extension point 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.

Table 9.10. Axon.ivy Extension Points


Rich Dialog Client Side Libraries

Providing a Custom Certificate

This section shows you how to provide a custom certificate for your Rich Dialog clients.

Note

When a client starts a Rich Dialog for the first time, a security information dialog box pops up showing information about the publisher of Axon.ivy as well as the publisher's certificate. It may be confusing to the users of your application that a certificate from Axon.ivy is displayed instead of a certificate of the implementor/vendor of the software they're using. This section shows you how to avoid this situation.

Install a Java Developer Kit (JDK)

Install a Java Developer Kit (JDK) version 1.7. You can download it from http://java.oracle.com

Creating a custom certificate

Use the keytool from the previously installed JDK to create a custom certificate in a Java keystore. You may also want to sign your certificate by a certification authority.

See the JDK keytool documentation for details.

Signing the Rich Dialog client libraries

To sign your custom Rich Dialog client extensions with a custom certificate, proceed as follows:

  1. Configure the following properties:

    keystore.file, keystore.password, keystore.alias and dir.jdk

    in the file

    [Axon.ivy Designer Installation Directory] / clientlib / build.properties

    Save your changes.

  2. Open the Ant build file

    [Axon.ivy Designer Installation Directory] / clientlib / build.xml

    in Axon.ivy Designer.

  3. Open the Outline View (Use the menu Window > Show View > Others... > General > Outline)

  4. In the Outline View select the root entry in the tree. Right click to bring up the context menu and select Run As > Ant Build to run the Ant build script (build.xml). The Console View will show what the Ant build is doing and if there are errors during the build, they will shown in the Console View as well.

  5. If the Ant build was successful then a new directory has been created:

    [Axon.ivy Designer Installation Directory] / clientlib / customSigned

    This directory now contains the new Rich Dialog client libraries which have been signed with your certificate.

Axon.ivy Designer will automatically use libraries that are located in this new directory when a Rich Application is started, i.e. the provided client side libraries will automatically be deployed to the client when simulating the next time.

Note

If an error message like jarsigner error: java.lang.RuntimeException: keystore load: DerInputStream.getLength(): lengthTag=109, too big. appears, try setting the property keystore.type accordingly to the type used in the keystore.

Deploying custom signed Rich Dialog client libraries

To use the new custom signed client side extensions with another Axon.ivy Designer or Engine, simply copy the directory

Axon.ivy Installation Directory / clientlib / customSigned

with all its files to a directory with the same name of another Axon.ivy Designer or Engine Installation Directory.

Restrictions

Warning

Upgrading Axon.ivy Designer or Engine may install a new version of the standard client side libraries. If this is the case you have to resign your custom Rich Dialog client side libraries so that they are compatible with the newer standard client side libraries.

Axon.ivy Designer will fall back to the standard client side libraries if the custom signed client side library version does not match.

Axon.ivy Engine can only be started if the custom signed client side libraries have exactly the same version as the standard client side libraries.

Warning

All Rich Dialog client libraries located in the clientlib/customSigned directory must be signed by the same certificate. Otherwise the Rich Dialog client will not start because of security restrictions.

Warning

Because of performance optimizations (lazy loading) all client side libraries located in the clientlib/customSigned directory must be signed together. Do not mix new with previously signed libraries, otherwise the lazy loading index will be inconsistent, leading to errors on the client side when executing your application.

Providing custom ULC Widgets

Warning

Development of custom ULC widgets is only permitted with a valid ULC developer licence. Licences and more information about the ULC (Ultra Light Client) framework can be obtained from Canoo.

Licenced ULC developers may contribute their own ULC extensions to an Axon.ivy installation. This chapter explains how custom ULC widgets can be included by packing them into a Axon.ivy Rich Dialog Client Extension.

Using custom ULC Widgets

To use a custom ULC Widget the following steps are necessary:

  1. The server side classes of the custom ULC Widget must be located in a Axon.ivy Project, so that they can be loaded on server side.

  2. The client side classes of the custom ULC Widget must be located in a Axon.ivy Rich Dialog Client Extension that is installed in the directory clientlib/extensions.

Packing custom ULC Widgets to an Axon.ivy Rich Dialog Client Extension

To pack custom ULC Widgets to an Axon.ivy Rich Dialog Client Extension the following steps are necessary:

  1. The client side classes of the custom ULC Widgets must be available in *.jar files.

  2. In the Axon.ivy Designer Installation Directory / clientlib / extensions directory create a new sub directory with the name of your extension

  3. Create another sub directory called clientLibs inside the previously created extension sub directory. The directory structure now looks like:

    Axon.ivy Installation Directory / clientlib / extensions / myExtension / clientLibs

    In the example the extension is called myExtension.

  4. Copy the .jar files with the client side classes to the clientLibs sub directory created above.

  5. Now, execute the same steps that are necessary to provide a customer certificate. Instead of using the file Axon.ivy Designer Installation Directory / clientlib / build.xml use the file Axon.ivy Designer Installation Directory / clientlib / extensions / build.xml. See section above.

If the Ant build is successful then a new directory was created:

Axon.ivy Designer Installation Directory / clientlib / extensions / myExtension / signed

It contains the jar files with the custom ULC Widgets signed with your certificate. The build will also create an extension configuration file if no such file is already available:

Axon.ivy Designer Installation Directory / clientlib / extensions / myExtension / extension.any

It contains information about your Axon.ivy Rich Dialog Client Extension like title, vendor, homepage, etc.. But also the version of the extension. This version should be increased and the extension rebuilt every time the client side classes of the ULC Widget changes.

Axon.ivy Designer will automatically use the new created extension for the Rich Dialog clients.

Deploying Axon.ivy Rich Dialog Client Extensions

To deploy an Axon.ivy Rich Dialog Client Extension simple copy the extension directory (e.g. myExtension) that contains the extension.any file and the signed directory inclusive the signed jars to the directory clientlib / extensions of another Axon.ivy Designer or Engine Installation.

Note

It is possible to install multiple Axon.ivy Rich Dialog Client Extensions from different vendors with different certificates.

Restrictions

Warning

All Jar files of one extension must be signed by the same certificate. Otherwise the Rich Dialog client will not start because of security restrictions.

Warning

Because of performance optimizations (lazy loading) all jar files of one extensions must be signed together. Do not mix new with previously signed jar files, otherwise the lazy loading index is inconsistent.

Troubleshooting Java Web Start Cache Problems

Rich Dialog Clients are using a technology called Java Web Start. Java Web Start caches all libraries it downloads from Axon.ivy. This can cause conflicts if you are developing and do not increase the version of your client side classes if they change or if you changing certificates. The following command line tools can help to solve problems.

Use the following command line to clean the cache of Java Web Start. This will remove all cached libraries from the cache:

javaws -uninstall

Use the following command to inspect the libraries that are cached by Java Web Start:

javaws -viewer

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 and Rich Dialogs (but not Html Dialogs) 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. For Rich Dialog searches the results can also be displayed as thumbnails (see next section).

Thumbnail Results for Rich Dialog Search

When searching for Rich Dialogs, the results are by default presented as thumbnails.

The result page shows the Rich Dialogs that have been found as a result of the search with a scaled image of the Rich Dialog's screen shot (if one is available) and with the Rich Dialogs simple name. The tool tip (1) offers additional information, such as the Rich Dialog's fully qualified name and the project it is located in. Rich Dialog's that don't have a screen shot are shown with a small red square (2).

Double clicking on a thumbnail will open the associated Rich Dialog's interface editor.

The size of the displayed thumbnails can be adjusted with the slider at the bottom of the view (3). Use this slider to set your preferred thumbnail size, it will be saved and used for all later thumbnail search results. The default size for the Rich Dialog thumbnails is set at 160 x 120 pixels, the minimum and maximum sizes being 16 x 12 and 480 x 360 pixels, respectively.

Tip

To automatically create a screen shot for a Rich Dialog you must first enable the feature on a Rich Dialog's interface editor (default is yes) and then open and save the Rich Dialog with the Rich Dialog editor. The screen shot will be updated every time you make changes to the Rich Dialog.

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

Rich Dialogs can also send and receive SystemEvents via the broadcast mechanism. Read the section below to learn how.

How to send and receive System Events in Rich Dialogs

By using the event broadcast mechanism in combination with the System Events framework, Rich Dialogs can receive and send system events as broadcast events.

Note

Check out the Chat Demo in the IvyDemos project (available from your Axon.ivy Designer installation directory under applications/samples/IvyDemos) to see how System Events can be used to implement an inter-session information exchange.

The demo implements a simple chat application which makes usage of THIRD_PARTY system events to send or receive messages from chat users, respectively.

Receiving System Events

Before the Rich Dialog can receive any events, it must register itself for the respective categories. This is done by calling ivy.rd.subscribeToSystemEvent(SystemEventCategory). Typically this registration should happen in the start method process of the Rich Dialog as shown below. Explicit de-registration is not necessary, all Rich Dialogs will automatically be unregistered as soon as their panel is unloaded (i.e. when the Rich Dialog executes a Rich Dialog End element).

Furthermore, the Rich Dialog must declare all the names of the system events it is interested in as accepted broadcast events on it's interface. The category of the system event does not matter (they are defined in the registration call as described above). However, the name of the accepted broadcast event must be exactly identical to the name of the system events that the rich dialog is interested in. Also the type of the parameter must be identical to the type of the system event's optional parameter. If names or parameter type do not match, then the system event will not be delivered.

The handling of the declared accepted broadcast events is then implemented with Rich Dialog event handler processes as usual.

Warning

Due to technical reasons, the name of an accepted broadcast event has to be a valid Java identifier. Therefore the original name of a system event may be an illegal name for an accepted broadcast event (e.g. the system event name foo.bar.baz will not be accepted because it contains punctuation). In such cases the name of the accepted broadcast event should substitute all illegal characters with underscores, e.g. foo_bar_baz for the given example)

Sending System Events

Sending System Events from a Rich Dialog is fairly easy. There are two possibilities:

  1. On the Rich Dialog interface declare a fired event with scope SYSTEM. Then use the Fire Event process element to send the event. The category of the system event will always be THIRD_PARTY, the declared event's name will be used as the system event's name and the (optional) parameter will become the system event's parameter.

  2. Use IvyScript/Java to send a system event through the IApplication.sendSystemEvent(SystemEvent) API as described in the general usage section above.