1. Overview

1.1. Sponge

Sponge is an open-source, Java-based Action and Event Processing System.

This documentation applies to version 1.10.0 (release date: 2019-05-10).

Sponge is a software for implementing synchronous actions and asynchronous event processors. Event processors listen to certain events and perform specified operations when some conditions are met. Events are usually sent by external systems. Sponge allows to introduce temporal and logical conditions to processed events. Event processing may end up with sending of another, high-level event thus creating a hierarchical model of events. The supported event processors include filters, triggers, rules and correlators.

There are several ways to use Sponge:

Embed Sponge as a software library in your Java application using Maven dependency.

Download the Sponge standalone command-line program and use it in your environment.

Run the Sponge standalone command-line program in a Docker container.

Connect to an existing Sponge instance via the REST API or the Sponge mobile client application (currently under development).

Actions and event processors are grouped in knowledge bases. A knowledge base is defined in files which may be written in one of several supported scripting languages, i.e. Python, Ruby, Groovy, JavaScript as well as in Java or Kotlin.

Provides integration with TensorFlow, an open source machine learning framework. You may use the TensorFlow or Keras Python API indirectly from your Java application.

Allows rapid development of generic, opinionated mobile applications backed by a server-side business logic. The business logic has to be defined in Sponge actions and the end user runs the Sponge mobile client application (currently under development) that provides a generic GUI to call actions. The mobile application could be useful in cases when a mobile GUI doesn’t have to be customized.

Provides a comprehensive documentation and many examples.

1.3. Applications

Sponge could be used as a component in various types of applications. Here are some examples.

Task automation

Tasks could be programmed in any of the supported scripting languages as Sponge actions, published via the REST API and call remotely in the Sponge mobile client application.

Sponge as a part of an IoT gateway can locally process data provided by the devices in the field, thus only important data is sent to the central node where data is collected, stored and manipulated by enterprise applications.

Sponge may be used on edge devices (also known as smart devices), providing a computation platform that would be largely or completely performed on distributed device nodes. The requirement is that such device must have Java (JRE) installed. The example is Raspberry Pi with connected sensors.

Sponge may be used to process faults or events sent by the network using a protocol such as SNMP. The processing may include creating issues into the issue tracking system (Trouble Ticket system). Sponge may use all protocols supported by Apache Camel or provided by custom plugins.

Sponge may be used to monitor a web server by periodically sending an HTTP request to fetch a page. It may also provide more advanced checks by running for example Selenium scripts to verify a web application. When a problem is detected Sponge could send an email to the administrator.

1.4. Users

The potential users of a standalone command-line Sponge application are:

Java, Python, Ruby, Groovy and JavaScript developers,

DevOps engineers and system administrators with programming skills.

Because of Sponge is a Java-based solution, at least basic knowledge of Java is suggested. It becomes important when browsing Javadoc API, using Java libraries, analyzing exception stack traces, etc. Moreover, to effectively use Sponge for problems that require integration, a knowledge of Apache Camel becomes important.

The Sponge mobile client application is intended for end users with no programming knowledge required.

1.5. License

The Sponge standard libraries are released under the Apache 2.0 license. However some optional Sponge libraries and the Sponge standalone command-line application are licensed under the GNU General Public License, Version 3 because they use optional, third-party libraries that require it.

1.6. Considerations

Because of Sponge doesn’t introduce its own notation for knowledge bases, it provides a shorter learning curve for users that know one of the supported scripting languages. However it could lack more sophisticated features and could be less optimized for certain uses than other solutions. For example you specify event conditions by writing any code in a scripting language that is to be executed when a matching event happens. On one hand it gives you a flexibility and on the other hand it doesn’t provide optimizations that could be possible if, for example, a strict temporal reasoning notation is used.

Sponge doesn’t provide persistence of events out of the box.

1.7. Author’s note

We have been using Open Source Software in our commercial products for many years. Therefore we appreciate the impact of Open Source on the IT industry and believe that its constant development is important. In order to contribute to the Open Source community we share and maintain projects such as Sponge.

— Marcin PaśCo-Founder & CTO at Softelnet

2. Quickstart

2.1. Concepts

This chapter describes basic concepts behind Sponge. For more information see User Guide.

2.1.1. Processors, actions and event processors

Processors are the basic objects that you define in Sponge to implement your knowledge base behavior. Actions provide a synchronous behavior. Event processors listen to certain events and perform specified operations when some conditions are met. Sponge allows to introduce temporal and logical conditions to processed events.

Event processors

Filter

Filters allow only certain events to be processed by other event processors (triggers, rules and correlators).

When Sponge process is running you may send HUP signal to that process in order to reload knowledge bases.

Reloading of running knowledge bases

kill -HUP <pid>

Where <pid> is the PID of the Java executable running the Sponge. It isn’t the PID of the shell script sponge.

See User Guide for limitations of reloading knowledge bases.

Terminating the Sponge process running in the background

kill -TERM <pid>

2.2.2.2. Windows

First steps:

Unpack the archive

Run Sponge using the configuration file

cd bin
sponge.bat -c ..\config\py\triggers_hello_world.xml

Output console shows

Hello World!

Press CTRL+C to exit the Sponge standalone command-line application.

Run Sponge using the knowledge base file

sponge.bat -k ..\kb\py\triggers_hello_world.py

Press CTRL+C to exit.

Run another example

sponge.bat -k ..\kb\py\rules_heartbeat.py

Press CTRL+C to exit.

When running on Windows, the Sponge standalone command-line program doesn’t support reloading of running knowledge bases by sending operating system signal to the background process.

2.2.2.3. Interactive mode

The Sponge standalone command-line program can be invoked in the interactive mode, providing command-line access to the knowledge base interpreter.

Invoke Sponge in the interactive mode

./sponge -k ../examples/standalone/trigger_simple.py -i

Send a new event from the console

> sponge.event("alarm").send()

The sponge variable is a facade to the Sponge engine.

Because of Sponge may print messages and exceptions to the console concurrently, the prompt could be lost in between the lines (for example in case of an exception stack trace). In that case press Enter key to make a prompt visible.

The output shows that the event has been processed by the trigger

Sound the alarm!

Multi-line statements should be entered by adding a backslash (\) to the end of all lines except the last one, e.g.:

> def printHello():\
> print("Hello")

You may exit the program by entering exit, quit or pressing CTRL-D.

2.2.3. Docker

The standalone command-line program may also be installed as a Docker container.

Invoke bash shell in the Sponge bin directory in a Docker container

docker run -it openksavi/sponge:latest

Print Sponge help

./sponge -h

Exit the container

exit

The Docker container provides Oracle Java 8 as well as mc for convenience.

If you want to mount a host directory containing for example Sponge knowledge bases or configuration files you may use Docker volumes or mount features.

Press CTRL+C after seeing the message "Hello World!" to exit the Sponge loop.

2.3. Examples

This chapter provides introductory examples of Sponge. For detailed information see User Guide.

Sponge is a polyglot system. It allows creating a knowledge base in one of the several supported scripting languages.

The shell commands that execute the examples require installation of the Sponge standalone command-line application and are specific to Linux/MacOS/Unix. For more information how to run the examples see the next chapter.

2.3.1. Hello World action example

Let’s start with the time-honored Hello World example. We will define a HelloWorldAction action that accepts one string argument (your name) and returns a greeting text. The same action will be implemented in different scripting languages in the following chapters.

If the Sponge REST API server is configured, you could call this action remotely.

The action configuration callback method. The method body defines the optional action metadata. The metadata could be used by a client code, for example a generic UI for calling actions or a REST API client.

3

Sets up the action label and the description.

4

Sets up the action argument metadata. There is only one argument named name of String type.

5

Sets up the action result metadata.

6

The action callback method that will be invoked when the action is called.

7

The knowledge base startup function.

8

Logs the result of the action call. The first parameter is always an action name. The other parameters depend on an action definition.

The HelloWorldAction action is enabled automatically before executing onStartup(). Enabling means that an instance of HelloWorldAction class is created and then HelloWorldAction.onConfigure method is invoked to configure this action.

The full source code of the example can be found in the file actions_hello_world.py.

The onConfigure method as well as metadata are optional for actions. It is helpful only when a generic access to actions is needed or for documentation. The minimalistic version of this example that doesn’t define metadata is much simpler.

Sets up HelloWorldTrigger to listen to helloEvent events (i.e. events that have name "helloEvent"). The event name could be also specified as a regular expression. For example "helloEvent.*" would configure this trigger to listen to all events whose name starts with "helloEvent".

4

The trigger onRun method will be called when an event helloEvent happens. The event argument is a reference to the event instance.

5

Prints the value of the event attribute "say".

6

The knowledge base startup function.

7

Send a new event helloEvent that has an attribute "say" with the text value "Hello World!".

The trigger HelloWorldTrigger is enabled automatically before executing onStartup(). Enabling means that an instance of HelloWorldTrigger class is created and then HelloWorldTrigger.onConfigure method is invoked to configure this trigger.

The full source code of this example can be found in the file triggers_hello_world.py.

Running this example in the standalone command-line application

./sponge -k ../examples/script/py/triggers_hello_world.py

The output console shows

Hello World!

Press CTRL+C to exit the Sponge standalone command-line application.

All callouts placed in the source code in the examples below remain the same, because they are functionally equivalent.

The full source code of this example can be found in the file triggers_hello_world.js

Running this example in the standalone command-line application

./sponge -k ../examples/script/js/triggers_hello_world.js

Press CTRL+C to exit.

2.3.3. Heartbeat rule example

This example presents a more advanced use case of Sponge.

The rule HeartbeatRule will fire (i.e. execute its onRun method) when it detects a time gap between heartbeat events that is longer than 2 seconds. This scenario could be used in a monitoring system to verify that a particular service is running.

Setup HeartbeatRule to listen to heartbeat events (i.e. events that have name "heartbeat") and detect a situation that when heartbeat event happens, then there will be no new heartbeat event for 2 seconds. So it detects a time gap between heartbeat events.
To first occurrence of event heartbeat is assigned an alias h1, to the next h2. They are required because the same event type is used more than once. :none sets an event mode for the second occurrence of heartbeat that tells that there should happen no such event.

4

Add the event condition for the event h2 that correlates events that have the same source (specified as an event attribute). The rule.firstEvent property is a reference to the first event accepted by this rule (in this case h1).

5

Set a duration of this rule to 2 seconds. After that time (counting since the occurrence of h1) the state of the rule will be verified and if the specified situation happens, the rule will fire.

6

The onRun method will be called when a specified situation takes place. The event argument is a reference to the last event in the sequence, so in this case it is null because there is no second event. The complete sequence of events will be returned by the method getEventSequence(). A single event instance is returned by the method getEvent(eventAlias).

7

Send a new alarm event that will be processed on a more abstract level.

8

A trigger that listens to alarm events and prints that the alarm has been activated. In the real use case the rule could, for example, send an email or SMS.

The full source code of this example can be found in the file rules_heartbeat.py.

Running this example in the standalone command-line application

./sponge -k ../examples/script/py/rules_heartbeat.py

After a few seconds the output console shows

Sound the alarm!

Press CTRL+C to exit the Sponge standalone command-line application.

This example doesn’t detect a situation when there hasn’t been any heartbeat event since the startup of the Sponge. To remedy that issue you could use the startup event. See the chapter on Startup system event in the User Guide.

The full source code of this example can be found in the file rules_heartbeat.js.

Running this example in the standalone command-line application

./sponge -k ../examples/script/js/rules_heartbeat.js

After a few seconds the output console shows

Sound the alarm!

Press CTRL+C to exit.

3. User Guide

3.1. Introduction

Sponge is a polyglot system that allows creating knowledge bases in several scripting languages.

For the purpose of clarity, examples in this chapter are written in Python (Jython) as one of the supported scripting languages and in Java. All examples written in Python may have equivalent ones written in any of the other supported script languages.

3.2. Architecture

The figure below presents the architecture of the Sponge system.

Figure 2. The system architecture

The Sponge engine consists of the following components.

Table 1. Engine components

Engine component

Description

Configuration Manager

The module providing an access to a configuration.

Plugin Manager

The module that manages plugins.

Knowledge Base Manager

The module that for manages knowledge bases.

Event Scheduler

The scheduler of future events that are to be added into the Input Event Queue.

Input Event Queue

The input queue of events that are sent to Sponge. Events can get to this queue form different sources: plugins, Event Scheduler or knowledge bases.

Filter Processing Unit

The module providing the filtering of events. It is also a registry of enabled filters.

Main Event Queue

The queue of events that passed all filters and are to be processes by other event processors in the Main Processing Unit.

Main Processing Unit

The module that manages the processing of events by triggers, rules and correlators. It is also a registry of such event processors.

Output Event Queue

The queue of ignored events, i.e. events that haven’t been listened to by any trigger, rule or correlator. Events rejected by filters don’t go to the Output Event Queue. The default behavior is to log and forget ignored events.

The specification of a configuration XML file is provided by the schema file config.xsd.

The configuration file is looked up using the default strategy provided by Apache Commons Configuration, e.g. first in the file system as a relative or absolute path and then in the classpath. If not found, the configuration file is looked up in the file system relative to the Sponge home directory.

3.3.1.1. Properties configuration

The properties configuration section (properties) allows setting configuration properties. Configuration properties may be used in other places in the configuration file. Moreover the properties can be used as Java system properties if the attribute system is set to true. Java system properties passed to the JVM take precedence over the ones defined in the properties configuration section of the configuration file. So, for example passing -Dsponge.home=/opt/sponge to the JVM will override the corresponding property configuration.

Properties can also be used as Sponge variables in the engine scope. In that case you have to set the attribute variable to true. The type of such variables is always String.

Table 2. Predefined properties

Property

Description

sponge.home

The Sponge home directory. The Sponge home directory may also be provided as a Java system property sponge.home.

sponge.configDir

The configuration file directory. This property is read only. It may be null if there is no Sponge configuration file.

Properties may be loaded from an optional properties file located in the same directory as the configuration file, which name follows the convention <XML configuration file basename>.properties. This properties file is supposed to be encoded in UTF-8.

3.3.1.2. Engine configuration

The engine configuration section (<engine>) contains engine configuration parameters and an optional name of the Sponge engine. The most important parameters are mainProcessingUnitThreadCount, asyncEventSetProcessorExecutorThreadCount and eventQueueCapacity.

Table 3. Engine parameters

Parameter

Description

Default value

mainProcessingUnitThreadCount

The number of Main Processing Unit worker threads. You could increase this parameter if your knowledge bases require concurrent processing of many events by triggers, rules or correlators.

10

asyncEventSetProcessorExecutorThreadCount

The number of threads used by an event set processor thread pool (executor) for asynchronous processing of events (by rules and correlators). You could increase this parameter if your knowledge bases create many instances of asynchronous rules or correlators. In such case, for better performance, this parameter should be equal to or greater than mainProcessingUnitThreadCount.

10

eventQueueCapacity

The capacity (i.e. maximum size) of the Input Event Queue. Value -1 means infinity. You should set eventQueueCapacity to a reasonable value taking into account the amount and intensity of events your system needs to process, the typical event size, the number of threads in thread pools, etc. When the system is overloaded with events and this parameter is set to the value other than infinity, every attempt to send a new event will throw QueueFullException. However, when the system is overloaded and this parameter is to too high or infinite, you risk OutOfMemoryError.

-1

eventClonePolicy

Event clone policy (shallow or deep).

shallow

autoEnable

Auto-enable script-based processors.

true

durationThreadCount

The number of threads used by the event set processors duration thread pool (executor). The default implementation uses these threads only to send control events. In most cases there should be no need to change this parameter, because sending a new event is relatively fast.

2

eventSetProcessorDefaultSynchronous

The event set processor default synchronous flag. If this parameter is set to true then all rules and correlators that have no synchronous flag specified in their configuration would be assumed as synchronous. If an event set processor is synchronous it means that an event will be processed sequentially (in one thread) for all instances of this event set processor. If an event set processor is asynchronous then an event will be processed by the instances of this event set processor concurrently (in many threads). The default behavior is asynchronous. In most cases you wouldn’t need to change this parameter.

false

executorShutdownTimeout

The thread pool (executor) shutdown timeout (in milliseconds). You could, for example, increase this parameter to guarantee a graceful shutdown if event processors need more time to finish processing when the engine is shutting down. The actual shutting down of the entire engine may take longer than executorShutdownTimeout because this parameter is applied separately to several thread pools in the engine.

60000

3.3.1.3. Knowledge bases configuration

The knowledge bases configuration section (<knowledgeBases>) lists all script knowledge bases that are to be loaded into the engine.

Each <knowledgeBase> tag contains:

Table 4. Knowledge base configuration

Tag

Type

Description

name

Attribute

The name of the knowledge base.

label

Attribute

The knowledge base label.

type

Attribute

The type of the script knowledge base corresponding to the scripting language. Allowed values: python, ruby, groovy, javascript. The type is required only for knowledge bases that specify no files so their type can’t be inferred from the file extensions.

class

Attribute

The class of the non script knowledge base. In that case you don’t have to specify a type and you must not specify files. A knowledge base class should define a non-parameterized constructor.

description

Element

The description of the knowledge base.

file

Element

The filename of the knowledge base. A single knowledge base may use many files but all of them have to be written in one language.

The file element may have the following optional attributes.

charset - sets the file encoding.

required - if set to false, the non existing files are ignored. The default value is true so when the file doesn’t exist, the exception is thrown.

3.3.1.4. Plugins configuration

The unique name of the plugin (mandatory). A text without white spaces and special symbols. Also used as a variable name in order to access a given plugin in the knowledge base.

label

Attribute

The plugin label.

class

Attribute

The name of the plugin class (Java class or a class defined in the scripting language in the script knowledge base (mandatory).

knowledgeBaseName

Attribute

The name of the knowledge base containing the class of the plugin (optional). If not set then the default Java-based knowledge base is used.

description

Element

The plugin description.

configuration

Element

The specific configuration of the plugin.

You may provide a custom plugin configuration section inside a <configuration> element. The contents of this plugin configuration depend on the given plugin implementation. Usually it would be a hierarchy of plugin specific sub tags.

3.3.2. Engine Builder API

The Engine Builder API is provided by DefaultSpongeEngine.builder() static method that returns the EngineBuilder instance. This API follows a builder design pattern.

3.4.2. Shutting down

When a Sponge instance is no longer needed it should be shut down by invoking shutdown() or requestShutdown() method. It instructs the engine to do some clean up, stop all managed threads, free resources, etc. The shutdown() uses the current thread to stop the engine. The requestShutdown() uses a new thread to stop the engine, thus allowing to shutdown the engine from the within, e.g. form an event processor.

Shutting down doesn’t guarantee that all events sent to the engine will be processed. However, all events that have already been read from the Input Event Queue (by the Filter Processing Unit) will be fully processed by the engine, if the processing doesn’t exceed shutdown timeouts (specified by the executorShutdownTimeout configuration parameter). All newer events remaining in the Input Event Queue will not be processed at all.

3.5. Knowledge bases

A knowledge base is used mainly to define processors. A knowledge base may be written in one of the supported scripting languages. An alternative way of defining knowledge bases is to write them directly in Java or Kotlin. However, using a scripting knowledge base has advantages such as that a modified script knowledge base doesn’t need recompilation.

There is a global namespace for all processors, regardless of the knowledge base they are defined in. When there is more than one processor of the same name in the engine, only the last enabled one will be registered. However, you can’t enable a processor if an another one that has the same name and is of a different type has already been enabled.

Script knowledge base files are looked up in the file system as a relative or absolute path, then in the classpath, then in the file system relative to the XML configuration file parent directory and then in the file system relative to the Sponge home directory. A knowledge base filename may contain wildcards (for files only, not directories), according to the glob pattern. Wildcards are not supported for classpath resources. This default behavior can be changed by providing a custom implementation of KnowledgeBaseFileProvider and passing it to the setKnowledgeBaseFileProvider method on the engine. For example the SpringEngineBuilder uses the SpringKnowledgeBaseFileProvider that supports the Spring classpath*: notation for loading resources.

The order of loading knowledge bases preserves the order specified in the configuration. Likewise the order of loading files of the same knowledge base preserves the order specified in the configuration.

Scripting knowledge bases are read by interpreters. For every knowledge base there is one instance of an interpreter.

Definitions of callback functions that will be invoked in particular situations.

3.5.2. Callback functions

Table 6. Callback functions

Function

Description

onInit()

Called once on the initialization of a knowledge base after the knowledge base files have been read.

onBeforeLoad()

Called just before onLoad and also every time a knowledge base is reloaded. The implementation could for example register data types before scanning processors.

onLoad()

Called on the loading of a knowledge base and also every time a knowledge base is reloaded. Before invoking an onLoad callback method, the engine scans to auto-enable processors (if this functionality is turned on). You may manually disable some of the auto enabled processors in this callback function if necessary.

onAfterLoad()

Called just after onLoad and also every time a knowledge base is reloaded.

onStartup()

Called once after the startup of the engine (after onInit() and initial onLoad()).

boolean onRun()

Called just after an onStartup() callback function. If this function returns true for every knowledge base, then the engine will start its threads and perform an endless loop in order to process events. This is the default behavior. Otherwise, the engine will assume a run once mode and it will invoke a shutdown without starting an event processing. The latter option allows a user to, for example, just run a script and stop the engine. It could be useful when using a standalone application to perform simple, synchronized tasks. In case of scripting knowledge bases onRun() may return null which will be treated as if it returned true.

onShutdown()

Called once before the shutdown of the engine.

onBeforeReload()

Called before every reloading of the knowledge base.

onAfterReload()

Called after every reloading of the knowledge base.

Sponge follows a convention that the names of all callback functions and methods start with on, e.g. onStartup for a knowledge base or onConfigure for a processor.

You shouldn’t place more than one callback function that has the same name in the same knowledge base (event in different files of that knowledge base). If there is more than one callback function that has the same name in the same knowledge base only the last loaded function will be invoked. Furthermore it could depend on the specific scripting language.

When Sponge is starting, callback functions are invoked in the following order:

executing all knowledge base files as scripts, i.e. executing the main body of the script files,

onInit(),

onBeforeLoad(),

onLoad(),

onAfterLoad(),

onStartup(),

onRun().

Before onStartup() is invoked you will not be able to send events or access plugins. That is because the engine hasn’t started fully yet.

When a knowledge base is reloaded, the callback functions are invoked in the following order:

onBeforeReload() executed in the previous version of the reloaded knowledge base,

executing all knowledge base files as scripts, i.e. executing the main body of the script files,

onBeforeLoad() executed in the new version of the reloaded knowledge base,

onLoad() executed in the new version of the reloaded knowledge base,

onAfterLoad() executed in the new version of the reloaded knowledge base,

onAfterReload() executed in the new version of the reloaded knowledge base.

3.5.3. Global variables

The following predefined global variables are available in all knowledge bases.

Table 7. Global variables

Global variable

Description

sponge

The facade to the Sponge engine operations that provides methods to send events, manually enable or disable event processors etc. This variable represents an instance of a Java class implementing org.openksavi.sponge.kb.KnowledgeBaseEngineOperations.

For each plugin a global variable will be created. The name of this variable is the plugin name (i.e. the value configured as the plugin name attribute in the configuration).

3.5.5. Engine facade

Table 8. Important engine facade properties and methods

Property / Method

Description

kb

The knowledge base to which belongs the script using this variable. This value represents an object of a Java class implementing org.openksavi.sponge.kb.KnowledgeBase (for script knowledge base it is org.openksavi.sponge.kb.ScriptKnowledgeBase).

interpreter

The knowledge base interpreter that has read the script using this variable. Generally it is an implementation of org.openksavi.sponge.kb.KnowledgeBaseInterpreter. In the case of a scripting knowledge base it returns an implementation of org.openksavi.sponge.kb.ScriptKnowledgeBaseInterpreter.

engine

The engine. This is the reference to the actual implementation of the SpongeEngine interface.

logger

The logger instance associated with the knowledge base. The name of this logger has the following format: sponge.kb.<language>.<knowledgeBaseName>.global, e.g. sponge.kb.python.kb1.global. Please note, that event processors and plugins have their own loggers (they are referenced as self.logger).

3.5.6. Loading knowledge base from an additional file

Sponge gives the possibility to define a knowledge base in a few files. In order to do that, in the configuration file in the <engine> section you may define which files should be loaded by adding <file> tags to <knowledgeBase>. Additional files could also be loaded from a knowledge base level.

sponge.kb.load("triggers.py")

If the same name is used for a new processor, the previous definition will be replaced with the new one. However, this behavior could depend on the specific scripting language.

3.5.7. Reloading

Sometimes a situation may happen that there will be a need for a dynamic modification of processors, for example to add a new rule or modify an existing one. It is possible to do it without the need of shutting down and then starting the system again.

When variables are used in a knowledge base and you don’t want them to be changed after reloading of the knowledge base, you should place their definitions in onInit() callback functions rather than simply in the main script or in onLoad(). That is because the main script and onLoad() are always executed during reloading but onInit() function is not.

When reloading the system, the configuration file is not loaded again. If the changes in this file (e.g. registering a new plugin) are to be visible in the system, the only way is to restart.

When the Sponge engine is being reloaded, the previously defined processors will not be removed from the registry. When a processor definition has changed in the file being reloaded, it will be auto-enabled (i.e. registered) once more with the new definition. If auto-enable is off, then sponge.enable method must be invoked. In that case sponge.enable should be placed in the onLoad() callback function.

If auto-enable is on (this is the default setting), then all processors will be enabled after reloading, even processors that have been manually disabled before.

There is a limitation in reloading a knowledge base that defines event set processors (i.e. rules or correlators). When there are existing instances of event set processors, they will be dismissed.

Depending on the specific interactions and taking into account differences in the third-party implementations of scripting languages, reloading sometimes may lead to problems or side effects and it should be used carefully. For example if onLoad callback function definition is removed in the Python script file before reloading, the instance of this function that had been loaded before will still be present in the interpreter and will be invoked. That is because the scripts being reloaded run in the same interpreter instance.

3.5.8. Use of many knowledge base files

As mentioned before, Sponge provides the possibility to read a knowledge base from many files. Dividing a knowledge base into a few files allows in an easy way to separate some functionalities.

The order in which the files are loaded is important. The files will be loaded in such order in which they were placed in the configuration.

3.5.9. Synchronization of processes in a knowledge base

Sponge is a multi-threaded system. Sponge engine operations are thread-safe. However, attention should be paid that processors defined in a knowledge base access any shared resources in a thread-safe way. This could be achieved in various ways using Java or scripting language mechanisms.

Maven configuration

3.5.11. Scripting knowledge bases interoperability

There are some limitation in the interoperability between scripting knowledge bases:

You shouldn’t pass knowledge base interpreter scope variables from one knowledge base to an another. Even if they are written in the same scripting language. This is because each knowledge base has its own instance of an interpreter.

Data structures used for communicating between different knowledge bases should by rather Java types or simple types that would be handled smoothly by Java implementations of scripting languages. For example you shouldn’t use a script-based plugin in knowledge bases other than the one in which this plugin has been defined.

Using more than one knowledge base written in the same scripting language may, in certain situations, also cause problems, due to the internal implementations of scripting language interpreters.

3.5.12. Useful knowledge base commands

Make and send a new event.

sponge.event("alarm").set("severity", 10).send()

Print registered (i.e. enabled) triggers.

print sponge.engine.triggers

Print registered rule groups.

print sponge.engine.ruleGroups

Print instances of the first rule group.

print sponge.engine.ruleGroups[0].rules

Print registered correlator groups.

print sponge.engine.correlatorGroups

Shutdown using a new thread.

sponge.requestShutdown()

Print the engine statistics summary.

print sponge.engine.statisticsManager.summary

For more information see Sponge Javadoc.

3.5.13. Predefined knowledge base libraries

Sponge provides a few predefined script files that may be used as one of files in your compatible (i.e. written in the same language) knowledge bases. For example you may use the Jython library in your XML configuration file: <file>classpath*:org/openksavi/sponge/jython/jython_library.py</file>. The classpath* notation is available only for Spring aware engines and allows to use Ant style (*) specifications for directories and files.

3.5.14. Knowledge base versioning

You may specify a version number for a knowledge base as an integer. It could be useful for example to enforce version checking when calling actions via the REST API. You should set the version in the onLoad callback function. After editing the knowledge base file and before reloading the engine, you could increase the version number.

Example of setting the knowledge base version.

defonLoad():
sponge.kb.version = 1

3.5.15. Naming conventions

3.5.16. Categories

Processors may be assigned to registered categories. Categories could be used in a client code to group processors independently of knowledge bases. This feature be useful if you don’t want to group processor by knowledge bases which requires more resources because each knowledge base has its own interpreter.

A registered category can be manually assigned to a processor a the processor configuration callback method onConfigure or dynamically by selecting processors that are to assigned to the category. The latter option could assign for example all processors defined in several knowledge bases to a single category in one line. Dynamic selection overwrites manual assigning.

Processors and plugins defined in non-Java languages can extend respectively Java-based processors and Java-based plugins. However this functionality is limited in different scripting languages.

Table 9. Support for extending Java-based processors and plugins in non-Java knowledge bases

Entity

Python

Ruby

Groovy

JavaScript

Kotlin

Action

Yes

Yes

Yes

No

Yes

Filter

Yes

Yes

Yes

No

Yes

Trigger

Yes

Yes

Yes

No

Yes

Rule

No

No

No

No

No

Correlator

Yes

Yes

Yes

No

Yes

Plugin

Yes

Yes

Yes

No

Yes

3.6. Data types

3.6.1. Supported data types

Data types are represented by instances of classes. The type classes are located in the org.openksavi.sponge.type package. Types are used for example as action arguments and result metadata.

Table 10. Basic type properties

Property

Description

name

A type location name. It is required if a type is used to specify an action argument metadata. In that case a type name is a name of an action argument. It is also required if a type is used to specify a record type field. A type location name has to be set in a type constructor.

label

An optional type location label. For example an action argument label or a record field label.

description

An optional type location description.

annotated

A flag that tells if a value of this type is annotated, i.e. wrapped by an instance of AnnotatedValue. Defaults to false. An annotated value allows passing a value label, a value description and value features along with the value, e.g. AnnotatedValue(imageBytes).withLabel("Image1").withFeatures({"filename":imageFilename}), where the first property is the annotated value, the second is the label and the third is the features map that may be used in a client code.

format

An optional format.

defaultValue

An optional default value.

nullable

Tells if a value of this type can be null. The default is that a value must not be null, i.e. it is not nullable.

features (or feature)

Optional features as a map of names to values (as the Object type).

optional

A flag specifying if a value in a location corresponding to this type is optional. Defaults to false. It is used in action arguments.

provided

The provided type specification as an instance of the ProvidedMeta class. Defaults to null. A provided value has to be an instance of ProvidedValue.

A type properties should be set using the builder-style methods, e.g. StringType("id").withLabel("Identifier").

Table 11. Provided type specification properties

Property

Description

value

A flag specifying if this type value is provided.

valueSet

Metadata specifying if a value set is provided. Defaults to null. A value set is a list of allowed values. This list of values may be limited (the default setting) or not limited. A limited set means that the type value can be chosen only from the value set. To configure a not limited value set use withValueSet(ValueSetMeta().withNotLimited()).

depends

A list of type names of name paths that this type depends on.

readOnly

A flag specifying if this type is read only. Defaults to false.

overwrite

A flag specifying if the provided value of this type should overwrite the value set in a client code. Defaults to false. This flag should be handled by a client code.

A date/time type. This type requires a DateTimeKind parameter, which is is an enumeration of DATE_TIME (a value of this type in Java has to be an instance of LocalDateTime), DATE_TIME_ZONE (an instance of ZonedDateTime), DATE (an instance of LocalDate), TIME (an instance of LocalTime), INSTANT (an instance of Instant). To ensure interoperability with other systems, defining a type format (especially for DATE and TIME) is recommended and in some cases required. A type format uses the ICU/JDK date/time pattern specification. The Sponge REST API uses a type format (if defined) to serialize a value of DateTimeType to JSON.

DynamicType

An dynamic type representing dynamically typed values. A value of this type has to be an instance of DynamicValue.

A list type. This type requires a DataType parameter, which is is a type of list elements. For example: ListType().withElement(ObjectType().withClassName("org.openksavi.sponge.examples.CustomObject")).

MapType

A map type. This type requires two DataType parameters: a type of keys and a type of values in the map. For example: MapType().withKey(StringType()).withValue(ObjectType().withClassName("org.openksavi.sponge.examples.CustomObject")).

NumberType

A number type, that include both integer and floating-point numbers. Provides optional properties minValue, maxValue, exclusiveMin and exclusiveMax, e.g.: NumberType().

ObjectType

An object. This type requires a class name (typically a Java class name). For example: ObjectType().withClassName("org.openksavi.sponge.examples.CustomObject"). It also supports an array notation: ObjectType().withClassName("org.openksavi.sponge.examples.CustomObject[]").

RecordType

A record type. This type requires a list of named record field types. A value of this type has to be an instance of Map with elements corresponding to the field names and values. E.g.: RecordType("book").withFields([StringType("author").withLabel("Author"), StringType("title").withLabel("Title")]). A record type supports inheritance.

StreamType

A stream type. Supports only output streams. It can be used only as a result of an action. A value of this type has to be an instance of OutputStreamValue.

3.6.2. Registering data types

A data type can be registered in the engine in order to be accessed later by its registered type name.

Table 13. Engine facade methods for registered types

Method

Description

addType(String registeredTypeName, DataTypeSupplier<T> typeSupplier)

Registers a data type by providing a type supplier that will create a new instance of the registered type each time, e.g. sponge.addType("Author", lambda: RecordType([StringType("firstName").withLabel("First name"), StringType("surname").withLabel("Surname")])).

getType(String registeredTypeName)

Returns a new instance of the registered data type.

getType(String registeredTypeName, String locationName)

Returns a new instance of the registered data type setting the returned type location name as well.

Map<String, DataType> getTypes()

Returns the unmodifiable map of registered data types.

Data types should be registered in the onBeforeLoad, because it is invoked before scanning processors.

3.6.3. Record type inheritance

A record type supports inheritance by setting its base type. Fields in a base type can’t be overwritten in a sub-type. Other record type properties are merged. Properties in a sub-type take precedence if they are not set to default values.

3.7. Events

Events are the basic element of processing in Sponge. They have properties such as id, name and send time. The name of an event is also the type of this event. All events than have the same name belong to the same type. Event names should follow Java naming conventions for variable names. Events may have any number of attributes. These attributes will be available, for example, in event processors.

Sponge supports only point-in-time events.

3.7.1. Properties and methods

Table 14. Event properties and methods

Property / Method

Description

id

A property that is a global unique identifier of an event (String). This is a shortcut for getId()/setId(text) methods.

name

A read-only property that is the name (type) of an event. This is a shortcut for the getName() method. A name of an event shouldn’t be changed after the event has been created. A name must not be empty nor contain white spaces or reserved characters (:). You should also avoid using names that are regular expressions.

time

A property that is a send time of an event, i.e. a time of adding an event to the Input Event Queue. The time is represented as java.time.Instant. This is a shortcut for getTime()/setTime(instant) methods.

set(attributeName, value)

A method that allows setting an attribute of an event.

Object get(attributeName)

A method that returns a value of an attribute or throws IllegalArgumentException if it does’t exist.

Object get(attributeClass, attributeName)

A method that returns a value of an attribute (assuming it is an instance of attributeClass) or throws IllegalArgumentException if it does’t exist.

Object get(attributeName, defaultValue)

A method that returns a value of an attribute or defaultValue if it does’t exist.

boolean has(attributeName)

A method that checks if an event has an attribute.

all

A property that returns a map of all attributes. This is a shortcut for the getAll() method.

Event clone()

A method that clones an event.

Properties id and time are automatically set when adding an event to the Input Event Queue and there is no need for setting them manually.

3.7.2. Typical event processing

In order to process an event there must be an event processor listening to events of that type (the types of events are recognized by their names). So this steps should be taken:

Creating a new event instance and sending it to the system (e.g. sponge.event("alarm").set("location", "Building 1").send().

The event goes directly to the Input Event Queue or is scheduled to be inserted to the Input Event Queue later. Scheduling is performed by the Event Scheduler.

From this queue the events are taken by the Filter Processing Unit. The list of filters defined for this event type is taken and then each of them is invoked. If all filters accept the event, it will be put to the Main Event Queue in which it will await to be processed by other event processors.

Then the event is collected by the Main Processing Unit. The list of event processors listening to this type of events is selected and then each of them is given the event to process.

After processing by the Main Processing Unit the event goes to the Output Event Queue if and only if it hasn’t been processed (i.e. listened to) by any of event processors.

3.7.3. Event cloning

The event is cloned each time when the periodically generated events are sent to the Input Event Queue.

The standard implementation of events allows choosing the cloning policy shallow or deep. These policies differ in the way of cloning of events attributes. When using the former, the references to attributes are copied - each event processor works on the same attribute instances. The policy deep executes the procedure of deep cloning, so each next generated event will contain individual copies of the attributes.

3.7.4. Custom events

The default implementation of an event is the AttributeMapEvent class. However, Sponge allows to use custom event implementations of the Event interface.

3.7.5. System events

System events are sent automatically by the engine. An event sent in your code shouldn’t have the same name as any of the system events. Currently there is only one system event.

Table 15. System events

Event name

Description

startup

The startup event will be sent as the first event when the engine is starting up.

3.7.5.1. Startup system event

The startup system event could be useful to define rules or correlators that detect lack of other events since the startup of the engine.

The following rule detects a situation when there is no heartbeat event for 5 seconds since the startup of Sponge.

3.7.6. Control events

Control events are used by the engine internally. The names of control events have a prefix $. You shouldn’t give to your events a name that starts with this character.

3.7.7. Creating and sending events

Creating an event means creating an instance of an event class. Sending an event means that the created event will be put into the Input Event Queue, to be processed by filters and then by triggers, rules and correlators.

Event can be created and sent using the EventDefinition fluent API (e.g. sponge.event("helloEvent").set("say", "Hello World!").send()). The method sponge.event returns EventDefinition.

An event may be sent as:

A single instance – the event will be placed in the Input Event Queue only once.

Many instances periodically – new instances of an event will be placed in the Input Event Queue periodically, each of them with its own id and send time.

Table 16. EventDefinition methods

Method

Description

EventDefinition set(String name, Object value)

Sets the event attribute.

EventDefinition modify(EventDefinitionModifier modifier)

Modifies the underlying event.

send()

Sends an event immediately.

sendAfter(delay)

Sends an event after a specified time (given in milliseconds or as a Duration). Note that the order of inserting events to the Input Event Queue may be different than the order of invocations of sendAfter, even with the same delay. It depends on the internal implementation of the Quartz library.

sendAfter(delay, interval)

Periodically sends events after a specified time (given in milliseconds or as a Duration) every interval (given in milliseconds or as a Duration).

sendAt(at)

Sends an event at the specified time (given in milliseconds as the number of milliseconds since 01/01/1970 or as an Instant).

sendAt(at, interval)

Periodically sends events starting at the specified time (given in milliseconds as the number of milliseconds since 01/01/1970 or as an Instant) every interval (given in milliseconds or as a Duration).

sendAt(crontabSpec)

Sends events at time specified by Cron compatible time entry.

Event make()

Only returns the newly created event without sending.

3.7.8. Examples of sending events

Sample sending of events from the level of a knowledge base:

sponge.event("e1").sendAfter(Duration.ofSeconds(1))

Sends the event named "e1" after 1 second from now.

sponge.event("e2").sendAfter(2000, 1000)

Sends the event named "e2" after 2 seconds from now. New events will be periodically generated and sent every second.

sponge.event("e2").set("color", "red").set("severity", 5).send()

Sends an event with attributes "color" and "severity" immediately.

sponge.event("alarm").sendAt("0-59 * * * * ?")

Sends an event at the time specified by Cron notation.

3.7.9. Event priorities

A priority may be assigned only to control events, that are used internally by the engine. For standard events the priority always equals to 0 and cannot be modified.

A priority defines a level of the importance of an event. Events are added to and taken from queues with respect to their priorities. Priority is a positive or negative integer and the higher the number is, the higher is the priority of an event and the event will be processed before the others.

3.8. Processors

Processors are the basic objects that you define in Sponge to implement your knowledge base behavior.

Types of processors:

Actions - processors that provide functionality similar to functions. They don’t listen to events.

Filters - event processors used for allowing only certain events to be later processed by other event processors.

Triggers - event processors that execute a specified code when an event happens.

Event set processors - event processors that process sets of events.

Rules - event set processors that detect sequences of events.

Correlators - event set processors that detect any set of events and could be also used for implementing any complex event processing that isn’t provided by filters, triggers or rules.

3.8.1. Creating processors

In order to define your processor in a script knowledge base, you have to create a class extending the base class pointed by a specific alias (e.g. Filter for filters). In order to define your processor in a Java knowledge base, you have to create a class extending a specific class (e.g. JFilter for filters).

A name of a processor is a name of a class defining this processor.

3.8.2. Enabling processors

The operation of registering a processor in the engine is called enabling. Registered processors are available to the engine to perform specific tasks. For example, after enabling an event processor starts listening to events it is interested in.

Processors could be enabled:

by auto-enable (this is the default setting for script-based processors),

manually.

3.8.2.1. Auto-enable

Sponge automatically enables all processors (i.e. actions, filters, triggers, rules and correlators) defined in a script knowledge base. This is done just before invoking the onLoad callback function in the knowledge base. Processor classes whose names start with the Abstract prefix are considered abstract and will not be automatically enabled.

As previously mentioned, the auto-enable feature scans only for processors defined in the scripting knowledge base. Enabling Java-based processors has to be done manually.

For non script knowledge bases (Java or Kotlin based) the auto-enable feature will scan only for processor classes nested in a corresponding knowledge base class. Other processors have to be enabled manually.

Example of processor inheritance and auto-enable

# This abstract action will not be automatically enabled.classAbstractCalculateAction(Action):
defcalculateResult(self):
return1# This action will be automatically enabled.classCalculateAction(AbstractCalculateAction):
defonCall(self):
returnself.calculateResult() * 2

You may turn off auto-enable by setting the autoEnable engine configuration parameter to false (for example in the Sponge XML configuration file). In that case you have to enable processors manually.

3.8.2.2. Manual enabling

In most cases enabling processors manually should be done in the onLoad callback function.

To manually enable any script-based processors in a script knowledge base you may use: sponge.enable() to enable one processor and sponge.enableAll() to enable many processors.

Example of enabling a script-based processor

defonLoad:
sponge.enable(TriggerA)

Example of enabling script-based processors

defonLoad:
sponge.enableAll(Trigger1, Trigger3)

To manually enable any Java-based processors in a script knowledge base you may use sponge.enableJava(), sponge.enableJavaAll() or sponge.enableJavaByScan(). The default name of a Java-based processor is its full Java class name.

The enableJavaByScan method enables Java-based processors by scanning a given packages in search of all non abstract processor classes. The scanning is performed by the Reflections library. The method parameters are compatible with the Reflections(Object…​) constructor.

3.8.3. Disabling processors

Processors could be disabled only manually. To disable any script-based processors in a script knowledge base you may use sponge.disable() to disable one processor and sponge.disableAll() to disable many processors.

Example of disabling a script-based processor

defonLoad:
sponge.disable(EchoAction)

To disable any Java-based processors in a script knowledge base you may use sponge.disableJava(), sponge.disableJavaAll() or sponge.disableJavaByScan().

Example of disabling a Java-based processor

defonLoad():
sponge.disableJava(SameSourceJavaRule)

3.8.4. Properties and methods

Table 17. Processor properties and methods

Property / Method

Description

onConfigure()

The configuration callback method that will be invoked when a processor is being enabled. This method is mandatory.

onInit()

The initialization callback method that will be invoked after onConfigure(), each time a new working instance of the processor is created.

withName(String name)

Sets a processor name. The name can be read using self.meta.name. Because names of processors are created automatically, this method shouldn’t be invoked directly.

withLabel(String label)

Sets a processor label. The label can be read using self.meta.label. A label is not used internally but could be useful in a client code.

withDescription(String description)

Sets a processor description. The description can be read using self.meta.description. A description is not used internally but could be useful in a client code.

withVersion(Integer version)

Sets a processor version. The version can be read using self.meta.version. A version is not used internally by the engine. However it is used to enforce version checking when calling actions via the REST API.

withFeatures(Map<String, Object> features)

Adds processor features. The features can be read using self.meta.features. The processor features is simply a map of String to Object associated with a processor definition (not instance). It could be used to provide a custom behavior in a client code.

withFeature(String name, Object value)

Adds a single processor feature.

withCategory(String category)

Sets a processor category. The category can be read using self.meta.category. A category is not used internally but could be useful in a client code.

meta

The read-only property that provides a processor metadata.

logger

The read-only property that provides a processor logger. This is a shortcut for getLogger() method. A processor logger name has the following format: sponge.kb.<language>.<knowledgeBaseName>.<processorName>, e.g. sponge.kb.python.kb1.EchoAction for a python-based processor, sponge.kb.python.kb1.org.openksavi.sponge.examples.PowerEchoAction for a Java-based processor enabled in a Python-based knowledge base.

adapter

The read-only property that provides a processor adapter. This is a shortcut for getAdapter() method. A processor adapter is an internal object, associated with the processor, that is used by the engine. There should be no need to use this property in the client code.

Processors provide builder-style, fluent methods to set metadata properties, e.g. self.withLabel("Label").withDescription("Description"). In many scripting languages properties can be accessed using a dot notation rather than a direct method call. For example a processor metadata may be read using self.meta or self.getMeta().

3.9. Actions

Actions provide functionality similar to synchronous functions. They may be used in many knowledge bases that are written in different languages.

The alias for the base class for script-based actions is Action. The base class for Java-based actions is JAction.

3.9.1. Properties and methods

Sets action arguments metadata as data types. The action argument types can be read using self.meta.args. Each argument data type must have a name. The same name should be used as a name of a corresponding argument in an onCall method. Arguments that have the optional flag set in their type are optional. Optional arguments can be specified only as last in the argument list and are not required to be passed when calling an action.

withArg(DataType argType)

Sets a single action argument metadata as a type.

withNoArgs()

Defines that an action has no arguments.

withResult(DataType resultType)

Sets an action result type. The action result type can be read using self.meta.result.

withNoResult()

Defines that an action has no result, i.e. it’s result is of VoidType.

withCallable(boolean callable)

Sets a callable flag for the action. A callable action must have an onCall method defined. Defaults to true. The reason an action would be not callable is if it is used with provided arguments.

Object onCall(dynamic arguments)

A dynamic callback method that should be defined in a callable action. It will be invoked when an action is called, e.g.: sponge.call(actionName, [argument list]). The behavior of this method is dynamic, i.e. custom actions define onCall methods with the arbitrary number of named arguments, for example def onCall(self, value, text). This is the reason that the Action interface doesn’t force any implementation of onCall. The result is an Object.

void onProvideArgs(ProvideArgsContext context)

A callback method that provides argument values along with argument value sets (i.e. possible values of an argument). The provided arguments are explained later in this document.

The onConfigure method in actions is not mandatory.

Example in a script language

The code presented below defines the action named EchoAction that simply returns all arguments.

The definition of the action JavaEchoAction. The action is represented by the Java class of the same name.

2

The action onCall callback method.

Java action manual registration in the Python knowledge base

sponge.enableJava(JavaEchoAction)

3.9.2. Arguments and result metadata

Actions may have metadata specified in the onConfigure method. Metadata may describe action arguments and a result. Metadata are not verified by the engine while performing an action call but could be interpreted by a client code or Sponge plugins. For example they could be useful in a generic GUI that calls Sponge actions. Metadata can be specified using the builder-style methods.

3.9.3. Provided arguments

An action argument can be provided, i.e. its value and possible value set may be computed and returned to a client code any time before calling an action. A provided argument gives more flexibility than the defaultValue in the argument data type. Nested values of action arguments can be provided as well. In that case both a type being provided and a dependency path have to be named and can’t contain collections (lists or maps) as intermediate path elements.

The onProvideArgs(ProvideArgsContext context) method is used to provide action argument values.

Table 19. ProvideArgsContext properties

Property

Description

Set<String> names

A not null set of argument names (or name paths) that are to be provided. A name path is a dot-separated sequence of names of parent types, e.g. "book.author.surname".

Map<String, Object> current

The not null map of argument names (or name paths) and their current values passed from a client code. The map is required to contain values of those arguments that the arguments specified in the names depend on.

Map<String, ProvidedValue> provided

An initially empty map of argument names (or name paths) and their provided values (value sets) that is to be set up in an onProvideArgs callback method implementation.

This feature makes easier creating a generic UI for an action call that reads and presents the actual state of the entities that are to be changed or only viewed by the action and its arguments.

A provided argument can be readOnly. In that case its value in the onCall method should be ignored. A read only argument type has to be nullable.

A provided argument can depend on other arguments but only those that are specified earlier. In the example argument actuator5 depends on actuator1. Its possible value set contains the value of actuator1.

Arguments configured as provided have to be calculated in the onProvideArgs callback method and set in the provided map. For each provided argument its value and possible value set can be produced as the instance of the ArgValue class. The optional withValue method sets the provided value. The optional withAnnotatedValueSet method sets the value set along with annotations (e.g. labels) where each element is an instance of the AnnotatedValue class. The optional withValueSet method sets the possible value set with no annotations.

The parameter names in the onProvideArgs is a set of argument names that are to be provided. The current parameter is a not null map of argument names and their current values passed from a client code. The current value means the value used in a client code, for example entered by a user into an UI before calling the action. This map is required to contain values of those arguments that the arguments specified in the names depend on.

3.9.4. Implementing interfaces

Actions may implement additional Java interfaces. It could be used to provide custom behavior of actions.

3.10. Event processors

Event processors are processors that perform asynchronous operations using events they listen to.

Instances of event processors, depending on their type, may be created:

only once, while enabling, so they are treated as singletons,

many times.

Table 20. Event processors

Event processor

Singleton

Filter

Yes

Trigger

Yes

Rule

No

Correlator

No

When configuring an event processor, each event name can be specified as a regular expression thus creating a pattern matching more event names. The regular expression has to be compatible with java.util.regex.Pattern.

The trigger will listen to all events whose name starts with "a", as specified by the regular expression.

Event processors shouldn’t implement infinite loops in their callback methods because it would at least disrupt the shutdown procedure. If you must create such a loop, please use for example while sponge.engine.isRunning(): rather than while True:.

3.10.1. Filters

Filters allow only certain events to be processed by the engine. Filters are executed in the same order as the order of their registration (i.e. enabling).

You could modify event attributes in filters if necessary.

The alias for the base class for script-based filters is Filter. The base class for Java-based filters is JFilter.

Sets a name (a name pattern) or names (name patterns) of filtered events. The event names can be read using self.meta.eventNames. Setting an event or events is mandatory. It should be set in the onConfigure callback method. You may use only one of these methods in a processor.

boolean onAccept(event)

This method checks if an incoming event should be further processed. If onAccept method returns false, then the event will be discarded. Otherwise it will be processed by the other event processors. This method is mandatory.

Every filter should implement abstract onConfigure and onAccept methods.

Example in a script language

The code presented below creates a filter which filters only events whose name is "e1". Other events are not processed by this filter. Events e1 successfully pass through the filter only if they have an attribute "color" set to the value "blue". The others are rejected.

Class methods defined in a Python class have an instance object (self) as the first parameter.

The definition of the filter ColorFilter. The filter is represented by the class of the same name.

2

The filter configuration callback method.

3

Sets up ColorFilter to listen to e1 events (i.e. events named "e1").

4

The filter onAccept method will be called when an event e1 happens. The event argument specifies that event instance.

5

Logs the event.

6

Assigns the value of the event attribute "color" to the local variable color.

7

If color is not set or is not "blue" then rejects that event by returning false.

8

Otherwise accepts the event by returning true.

The filter ColorFilter will be enabled automatically. The enabling creates one instance of ColorFilter class and invokes ColorFilter.onConfigure method to set it up. Since that moment the filter listens to the specified events.

Example in Java

The filter presented below checks if an event named "e1" or "e2" or "e3" has an attribute "shape" set. If not, an event is ignored and will not be processed further.

Sets a name (a name pattern) or names (name patterns) of the events that cause this trigger to fire. The event names can be read using self.meta.eventNames. Setting an event or events is mandatory. It should be set in the onConfigure callback method. You may use only one of these methods in a processor.

onRun(event)

The callback method used for processing the event, called when the specified event (or one of the events) happens. This method is mandatory.

boolean onAccept(event)

This optional callback method checks if an incoming event should processed by this trigger. The default implementation returns true.

Every trigger should implement abstract onConfigure and onRun methods.

Example in a script language

The code presented below defines the trigger named TriggerA listening to events named "a".

The definition of the trigger TriggerA. The trigger is represented by a class of the same name.

2

The trigger configuration callback method.

3

Sets up TriggerA to listen to a events (i.e. events that have name "a").

4

The trigger onRun method will be called when an event a happens. The event argument specifies that event instance.

5

Logs the event.

The trigger TriggerA will be enabled automatically. The enabling creates an instance of TriggerA class and invokes TriggerA.onConfigure method to set it up. Since that moment the trigger listens to the specified events.

Example in Java

The code presented below defines the trigger named SampleJavaTrigger listening to events named "e1".

The definition of the trigger SampleJavaTrigger. The trigger is represented by a Java class of the same name.

2

The trigger configuration callback method.

3

Sets up SampleJavaTrigger to listen to e1 events (i.e. events that have name "e1").

4

The trigger onRun method will be called when an event e1 happen. The event argument specifies that event instance.

5

Logs the event.

Java trigger manual registration in the script knowledge base

sponge.enableJava(SampleJavaTrigger)

3.10.3. Rules

Sometimes there is a need to perform certain actions when a sequence of events has happened, additionally fulfilling some conditions. To handle such event relationships (both temporal and logical), Sponge provides rules. It is important for the behavior of the rules that events that happened first must be sent first into the engine.

The alias for the base class for script-based rules is Rule. The base class for Java-based rules is JRule.

A rule group is a set of rule instances, each created automatically for every event that could be accepted as the first event of this rule.

3.10.3.1. Properties and methods

The callback method that is invoked only once, when a rule is being enabled. In this method it should be established for what type of events the rule listens. Optionally event conditions for incoming events or rule duration could be set. This method is mandatory.

onInit()

The initialization callback method that is invoked while creating every new rule instance but after onConfigure.

Sets String-based specifications of events whose sequence causes the rule to fire. The complete event specifications can be read using self.meta.eventSpecs. Setting an event or events is mandatory. It should be set in the onConfigure callback method. You may use only one of these methods in a processor.

withDuration(Duration duration)

Sets a duration that may be used to set the time how long a rule lasts (represented as a Duration). The instance of a rule will be active only for a given period of time since the arrival of the first event. Until that time the instance of the rule will fire for each suitable event sequence that happens.

withSynchronous(Boolean synchronous)

Sets a synchronous flag for a rule. If a rule is synchronous it means that an event will be processed sequentially (in one thread) for all instances of this rule. If a rule is asynchronous then an event will be processed by the instances of this rule concurrently (in many threads). If the synchronous flag is not set then the default value as specified by eventSetProcessorDefaultSynchronous configuration parameter will be used. In most cases there should be no need to change this flag.

withConditions(String eventAlias, List<_event condition_> conditions)

Adds conditions for an event specified by an alias (or event name if aliases are not used). A condition is a method of this class or a closure/lambda that is invoked to verify that a new incoming event corresponds to this rule. The name of the condition method is irrelevant.

withCondition(String eventAlias, event condition condition)

Adds a single condition for an event.

withAllConditions(List<_event condition_> conditions)

Adds conditions for all events. This method must be invoked after the event specifications.

withAllCondition(event condition condition)

Adds a single condition for all events. This method must be invoked after the event specifications.

Sets a flag indicating that the rule should listen to ordered (ordered rule) or unordered (unordered rule) sequences of events. Defaults to true, i.e. the rule would listen to ordered sequences of events.

RuleEventSpec makeEventSpec(args) methods

Each makeEventSpec method creates a new event specification. The preferred way is to use String-based specifications of events in the rule configuration.

onRun(event)

The callback method invoked when a sequence of events specified by this rule has happened and all the conditions have been fulfilled. The argument event is the reference to the final event that caused this rule to fire. There could be many sequences of events fitting the rule definition. In order to access the events which fulfilled the conditions and caused the rule fire, the getEvent(eventAlias) method should be used. The onRun method is mandatory.

Event getEvent(String eventAlias)

Returns the instance of the event that already happened and that has a specified alias. This method may be used inside onRun method. If an event hasn’t happened yet, this method throws an exception. This method may return null only when an event that supposed not to happen didn’t occur as specified.

firstEvent

This property is a reference to the first event that has been accepted by this rule. It is a shortcut for the Event getFirstEvent() method. It could be used for example in event condition methods (including the one for the first event itself).

eventSequence

Returns a sequence of events that happened, as a list of event instances. The sequence may contain null values when an event that supposed not to happen didn’t occur as specified. This method may be used inside onRun method.

Every rule should implement the abstract onConfigure and onRun methods.

Because of rules are not singletons the onConfigure() method is invoked only once, while enabling the rule. So it should contain only basic configuration as stated before. The onInit() method must not contain such configuration because it is invoked every time the new instance of the rule is created.

A duration is relative to an internal clock of the engine, that is related to the time of events. When a duration timeout occurs, the engine sends a control event (DurationControlEvent) to the Input Event Queue so that the control event, before finishing the rule, goes the same route as all events. This is to ensure that no events will be skipped by a rule if the system is highly loaded. Note that this may cause the rule to last longer in terms of an external clock.

3.10.3.2. Event specification

Event specification for the rule consists of:

Event name

A name (or name pattern) of the event (mandatory).

Event alias

An optional alias for the event. The alias is a unique (in the scope of the rule) name assigned to the event. Aliases are mandatory if there is more than one event of the same type (i.e. having the same name). When each of the events is of different type, there is no need to specify an alias. In such case aliases will be defined automatically and equal to the name of the corresponding event.

Event mode

Specifies which sequences of events suitable to this rule should be used for running the rule (i.e. invoking the onRun callback method). Event modes are defined in the EventMode Java enumeration.

Table 24. Rule event modes

Event mode

Description

first

The first suitable event. This is the default event mode when none is specified for an event.

last

The last suitable event for the duration of the rule.

all

All suitable events for the duration of the rule.

none

An event that cannot happen in the sequence.

Event specification should be formatted as text "eventName [eventAlias [:eventMode"]] or "eventNamePattern [eventAlias [:eventMode"]]. White characters between all elements are allowed. For example the specifications "event1 e1 :first", "event1", "event1 e1" define the suitable first event named "event1". The specification "[Ee]vent.* e" define all events which name starts with "Event" or "event".

3.10.3.3. Ordered rules

For ordered rules:

The first event in the sequence, i.e. the event that would initiate the rule, must always have the mode first.

If the mode of the last (final) specified event is last or none, a duration must be set. Otherwise the rule would never fire.

The following examples of complete event specifications assume that the ordered rule has a duration that spans over all incoming events listed in the second column. The integer value in the brackets is the id of the event. An element null means that the event hasn’t happened. Incoming events: e1[1], e2[2], e2[3], e3[4], e2[5], e3[6], e3[7].

This rule hasn’t been fired because the evente3wasn’t supposed to happen.

3.10.3.4. Unordered rules

Behavior:

The matching of unordered events is done starting from the left in the list of events the unordered rule listens to.

Every event that is relevant to the unordered rule causes a new instance of the rule to be created. This implicates that the event mode for an event that actually happens as the first is used by the engine only as a suggestion. So the actual order of events that happen has a significant impact on the behavior of unordered rules.

If at least one specified event has none mode, you probably should set a duration for such a rule to avoid superfluous instances of the rule.

Unordered rules is a new feature that should be treated as an experimental one.

3.10.3.5. Event conditions

A rule may define conditions for events that have to be met to consider an incoming event as corresponding to the rule:

of the form of a any class method that takes one argument (Event) and returns boolean, e.g.:

boolean conditionA(Event event);
boolean check1(Event event);

as a closure or a lambda (depending on the language) that takes two arguments (Rule, Event) and returns boolean, e.g.:

as an instance of an implementation of the interface EventCondition (takes two arguments (Rule, Event) and returns boolean), e.g. as a Java lambda expression:

(rule, event) -> {
returntrue;
};

An event condition in Java is represented by the interface EventCondition.

A condition in the form of a closure or a lambda specifies two arguments: a rule instance (determined at the runtime) and an event instance. Take care not to mix up the rule argument with this (in Java) or self (in Python) as they are references to different objects.

The condition methods tell if an incoming event (corresponding to the sequence of events specified by the rule) should be considered suitable.

Example in a script language

The code presented below defines a rule named SameSourceAllRule listening to an ordered sequence of events ("filesystemFailure", "diskFailure").
The two events have to have severity greater than 5 and the same source. Moreover the second event has to happen not later than after 4 seconds since the first one. The method onRun() will be invoked for every sequence of events that match this definition.

The definition of the rule SameSourceAllRule. The rule is represented by a class of the same name.

2

The rule configuration callback method.

3

Defines that the rule is supposed to wait for sequences of events "filesystemFailure" (alias "e1") and "diskFailure" (alias "e2") and take into consideration the first occurrence of "e1" event and all occurrences of "e2" event.

4

Sets the condition checking an event severity for all events.

5

Sets conditions checking "e2" event source.

6

Setting the duration of the rule. The duration must be set for this rule because the final event has all mode. The rule lasts for 8 seconds. So, for 8 seconds since the occurrence of the first matching e1 a tree of event instances will be constantly built with the root containing the instance of initial e1 event. Each matching e2 event will cause the rule to fire immediately for the current event sequence. After reaching the duration time this rule instance will be discarded.

7

The onRun method will be called when the proper sequence of events happens and all the conditions have been fulfilled. The event argument specifies the last event in that sequence.

8

Logs message and the sequence of events.

9

An event condition method severityCondition.

10

Accept only events that have severity greater than 5.

11

An event condition method diskFailureSourceCondition.

12

Assigns the first event (e1) to the local variable event1.

13

Accept e2 events that have the same source as the first event e1 and that happened not later than after 4 seconds since the corresponding e1 event.

The rule will be enabled automatically. Then, in case of occurrence of e1 event that has severity greater than 5, a new instance of a rule SameSourceAllRule will be created.

The definition of the rule SameSourceAllRule. The rule is represented by a Java class of the same name.

2

The makeEventSpec method is used here to create event specifications instead of a formatted String. The same setting could be achieved by withEvents("filesystemFailure e1", "diskFailure e2 :all"). Java-based rules have convenience methods that accept varargs.

3.10.4. Correlators

Correlators could be viewed as a generalized form of rules. They detect correlations between events and could be used for implementing any complex event processing that isn’t provided by filters, triggers or rules.

Correlators listen to the specified events regardless of their order and provide manual processing of each such event. It means that they require more programming than the other processors, however provide more customized behavior. For example they need explicit stopping by calling the finish method. An instance of a correlator is created when the correlator accepts an incoming event as its first event.

A correlator instance, when started, may be finished:

manually by invoking the finish method from inside the onEvent method,

automatically when duration is set and the duration timeout takes place.

The alias for the base class for script-based correlators is Correlator. The base class for Java-based correlators is JCorrelator.

A correlator group is a set of instances of the correlator.

3.10.4.1. Properties and methods

The configuration callback method that is invoked when the correlator is being enabled. In this method it should be established for what type of events this correlator listens. Optionally a correlator duration could be set. This method is mandatory.

withEvents(List<String> eventNames) or withEvent(String eventName)

Sets a name (a name pattern) or names (name patterns) of of events that this correlator listens to. The event names can be read using self.meta.eventNames. Setting an event or events is mandatory. It should be set in the onConfigure callback method. You may use only one of these methods in a processor.

withDuration(Duration duration)

Sets a time how long a correlator lasts (represented as a Duration). The instance of a correlator will be active only for a given period of time since the arrival of the first accepted as first event. After that time on the instance of this correlator the onDuration callback method will be invoked.

withSynchronous(Boolean synchronous)

Sets a synchronous flag for a correlator. For details see a description of this flag for rules.

withMaxInstances(int maxInstances)

Sets a maximum number of concurrent instances allowed for this correlator. If this value is not set, there will be no limit of concurrent instances. In that case you will probably need to implement onAcceptAsFirst() method.

withInstanceSynchronous(boolean instanceSynchronous)

Sets an instance synchronous flag. If true (the default value), one instance of the correlator will process only one event at a time. If false, one instance of the correlator will process many events concurrently. In that case the correlator has to be thread safe.

boolean onAcceptAsFirst(Event event)

Checks if the event should be accepted as the first event of a correlator, therefore starting a new working instance. The method onAcceptAsFirst is invoked after onConfigure. This method is optional. The default implementation returns true.

onInit()

The initialization callback method that is invoked while creating a new correlator instance but after onAcceptAsFirst if it returns true. This method is optional.

onEvent(Event event)

The callback method invoked when an event that a correlator listens to happens. This method is mandatory.

firstEvent

This property is a reference to the first event that has been accepted by this correlator. It is a shortcut for the Event getFirstEvent() method. It could be used for example in the onEvent callback method.

onDuration()

The callback method invoked when the duration timeout occurs. This method should be implemented if a duration timeout is set. After invoking this callback method, finish is invoked automatically.

finish()

The final method that should be invoked in onEvent(Event event) method when the correlator has done its work. Only by invoking finish this instance of the correlator is closed and its resources are released.

Every correlator may implement the onAcceptAsFirst method and should implement the abstract onEvent method. If a duration is set up, the onDuration callback method should be implemented as well.

Because of correlators are not singletons the onConfigure method is invoked only once while enabling the correlator. So it should contain only basic configuration as stated before. The onInit method must not contain such configuration because it is invoked later, every time a new instance of the correlator is created.

Example in a script language

The code presented below defines the correlator named SampleCorrelator that listens to events "filesystemFailure" and "diskFailure".
The maximum number of concurrent instances allowed for this correlator is set to 1. A filesystemFailure event will be accepted as the first event only when there is no instance of this correlator already running. When the filesystemFailure event is accepted as the first, a new instance of this correlator will be created. Each instance of this correlator adds to its internal event log list eventLog any suitable event. When 4 fitting events are collected the correlator instance will finish.

The definition of the correlator SampleJavaCorrelator. The correlator is represented by a Java class of the same name.

Java correlator manual registration in the Python knowledge base

sponge.enableJava(SampleJavaCorrelator)

3.11. Plugins

Plugins are used for expanding Sponge with new functionalities and use them in knowledge bases. Typically they provide access to and from external systems.

The alias for the base class for script-based plugins is Plugin. The base class for Java-based plugins is JPlugin.

Each of these base classes extends the BasePlugin class that provides empty implementations of callback methods. If the created plugin requires own configuration parameters (e.g. in the XML configuration file) the onConfigure method should be implemented.

Each plugin is also an engine module and that means that in inherits from the BaseEngineModule class.

Plugins could be written in Java or in a supported scripting language as a part of a scripting knowledge base. However plugins written in a scripting language must be used only in the same scripting knowledge base they were defined in. That is because there are limitations of scripting languages interoperation. Only plugins written in Java could be used in any scripting knowledge base.

3.11.1. Properties and methods

Table 27. Plugin properties and methods

Property / Method

Description

name

The property that is a name of a plugin. This is a shortcut for getName()/setName(text) methods. Because of names of plugins are created automatically, the setter shouldn’t be used in a client code.

onConfigure(Configuration configuration)

The configuration callback method that will be invoked after a plugin has been loaded. This method allows reading an XML configuration for the plugin.

onInit()

The initialization callback method that will be invoked after a configuration of a plugin.

onStartup()

The callback method that will be invoked once after the startup of the engine.

onShutdown()

The callback method that will be invoked once before the shutdown of the engine.

onBeforeReload()

The callback method that will be invoked before every reloading of a knowledge base.

onAfterReload()

The callback method that will be invoked after every reloading of a knowledge base.

logger

The read-only property that provides a plugin logger. This is a shortcut for getLogger() method. A plugin logger name has the following format: sponge.kb.plugin.<pluginName>. Example logger name: sponge.kb.plugin.scriptPlugin.

The unique name of the plugin. This name may be used in knowledge bases as a variable referencing this plugin instance.

The class name of the plugin. It could be a Java class name or a scripting language class name. If the plugin is defined in a scripting knowledge base than you must specify that knowledge base name as an XML tag <knowledgeBaseName>.

Custom configuration for a plugin. That section could be any XML that is understood by this plugin.

The above configuration defines a plugin implemented by org.openksavi.sponge.examples.EchoPlugin class. This plugin may be used in the knowledge base as a global variable named echoPlugin (according to the name attribute). There are additional configuration parameters defined for this plugin. These parameters could be read in the onConfigure() method of the plugin class, called before starting the plugin.

3.11.3. Plugin life cycle

Creates the plugin class instance. The class must have a no-parameter constructor.

Configures the plugin by invoking the method onConfigure().

Initializes the plugin by invoking the method onInit().

Invokes the callback method onStartup() when starting the engine.

After starting all plugins the methods onStartup() defined in all knowledge bases are invoked.

In case of reloading a knowledge base, the method onBeforeReload() of each plugin is invoked before the method onBeforeReload() of knowledge bases. Invoking the methods onAfterReload() goes in reverse (first the methods onAfterReload() of all knowledge bases and then the methods defined in plugins).

Before Sponge shuts down, methods onShutdown() of all knowledge bases are invoked and then the method onShutdown() is invoked for each plugin.

3.14. Integration

3.14.1. Spring framework

Sponge engine can be configured as a Spring bean. That configuration provides standardized access to an embedded Sponge engine for example in J2EE environment.

To provide access to the Spring ApplicationContext in the knowledge base, the SpringPlugin instance should be created, configured as a Spring bean and added to the Sponge engine. The Spring plugin shouldn’t be defined in Sponge XML configuration file.

The engine configured as the Spring bean. The SpringSpongeEngine implementation is used here in order to startup and shutdown the engine by Spring. DefaultSpongeEngine could also be used here but it wouldn’t provide automatic startup and shutdown.

3.14.2.2. URI format

Where engineRef represents the name of the SpongeEngine implementation instance located in the Camel registry.

3.14.2.3. Options

Name

Default value

Description

action

"CamelProducerAction"

Could be used only on the producer side of the route. It will synchronously call the Sponge action that has a name specified by the value of this option. However if there is the header named CamelSpongeAction in the Camel In message, it would override the value of this option.

managed

true

If set to true the Sponge engine will be started automatically when the endpoint starts and will be shut down when the endpoint stops.

3.14.2.4. Sponge support for Camel

3.14.2.4.1. CamelPlugin

CamelPlugin provides an interface to the Camel context so it may be used in a knowledge base.

CamelPlugin may be configured in three different ways.

Explicitly as a Spring bean and assigned to the engine using the Engine Builder API. This is the preferred way.

If you use an implicit configuration and you get an error stating that camel variable is not defined, it signifies that a Camel context is not configured yet or Sponge engine is not used in any Camel route.

Only one CamelContext may be used with one instance of Sponge engine, bound by a single CamelPlugin.

3.14.2.4.2. Spring-based support

Spring bean named "spongeProducerTemplate" allows you to configure a Camel producer template used by CamelPlugin to send Camel messages. If none is present in a Spring configuration, then a default will be used.

Spring bean named springPlugin is the instance of SpringPlugin that could be registered in the engine and used in knowledge bases as the spring variable.

Spring bean named camelPlugin is the instance of CamelPlugin that could be registered in the engine and used in knowledge bases as the camel variable.

3.14.2.5. Producer

Using sponge component on the producer side of the route will forward a body of a Camel message to the specified Sponge engine.

Sponge in a producer mode could be placed in many routes in one Camel context.

3.14.2.5.1. Camel producer action

Camel producer action will be invoked by Sponge synchronously when a Camel exchange comes to the Sponge engine. The result returned by this action is placed as the body of the Camel IN message. So it can be used by the next endpoint in the route if there is any.

To avoid any misconception please note that events in the Output Event Queue are not sent to the Camel route.

3.14.2.5.2. Default Camel producer action

The default Camel producer action is provided by a Java action CamelProducerAction. If the body of the Camel message is a Sponge event or event definition, than the event is sent to the Sponge immediately. Otherwise this action creates and sends a new event that encapsulates the body. The event is then returned, so it is placed as the body of the Camel In message. The default name of the new event is the name of the corresponding Camel route.

3.14.2.5.3. Custom Camel producer action

You could provide a custom implementation of a Camel producer action in two ways:

define your own implementation of CamelProducerAction in a knowledge base,

define an action in a knowledge base that takes an instance of Exchange as an argument and specify it in the producer endpoint URI or in the message header, e.g.:

Python knowledge base

classCustomAction(Action):
defonCall(self, exchange):
return"OK"

Camel route that sets the action in the endpoint URI

from("direct:start").routeId("spongeProducer")
.to("sponge:spongeEngine?action=CustomAction")
.log("Action result as a body: ${body}");

Camel route that sets the action in the header

from("direct:start").routeId("spongeProducer")
.setHeader("CamelSpongeAction", constant("CustomAction"))
.to("sponge:spongeEngine)
.log("Action result as a body: ${body}");

3.14.2.6. Consumer

Using sponge component on the consumer side of the route will forward messages sent from the specified Sponge engine to a Camel route.

Sponge in a consumer mode could be placed in many routes in one Camel context.

The variable camel is a reference to the instance of CamelPlugin that is associated with the Camel context.

Output console

Send me to Camel

You may also send a message to the Camel endpoint directly, e.g.:

camel.sendBody("direct:log", event.get("message"))

This allows you, for example, to create a flexible message flow using Camel routes and Sponge as a dispatcher.

3.14.2.7. Routes in scripting languages

ScriptRouteBuilder class introduces fromS methods (meaning from Script) that delegate to the corresponding from methods in order to avoid using from since it could be a reserved keyword in scripting languages (e.g. in Python). So when defining Camel routes in Python you should use this class instead of standard RouteBuilder, e.g.:

The RestApiAuthTokenService implementation class name. The default implementation is JwtRestApiAuthTokenService that uses JSON Web Token (JWT) for Java JJWT library. Note that tokens used by the default implementation are signed but not encrypted.

authTokenExpirationDurationSeconds

Long

The duration (in seconds) after which an authentication token will expire. The value null or less than or equal to 0 means infinity. Defaults to 30 minutes.

Depending on the REST Camel component, you should add a corresponding dependency, e.g. camel-jetty for Jetty, camel-servlet for a generic servlet. For more information see the Camel documentation.

3.14.3.1. Operations summary

The following table contains a summary of the REST API operations. For a complete list of operations see the specification generated using Swagger and Swagger2Markup.

Table 30. The REST API operations summary

Name

URI

Description

Get the Sponge version

version

Returns the Sponge version.

Login

login

User login. Used in a token-based authentication scenario.

Logout

logout

User logout. Used in a token-based authentication scenario.

Get knowledge bases

knowledgeBases

Returns the knowledge bases which the user may use (i.e. may call actions registered in these knowledge basses).

Get actions

actions

Returns the metadata of actions that are available to the user. If you want to get metadata for specified actions, set the request property name to an action name or a Java-compatible regular expression. If you want to get only actions that have argument and result metadata specified in their configuration, set the request property metadataRequired to true (defaults to false). Actions will be sorted by a category sequence number, a knowledge base sequence number and an action label or name. The sequence number reflects the order in which categories or knowledge bases have been added to the engine. The optional request property registeredTypes is a flag for requesting registered types used in the actions in the result (defaults to false).

Call an action

call

Calls an action.

Provide action arguments

actionArgs

Returns provided arguments, i.e. values along with value sets of action arguments. The request accepts the following properties: name - the action name, argNames - the optional list of argument names that are to be provided (if null, all provided arguments will be produced), current - the optional map of argument names and their current values passed from a client code.

Send a new event

send

Sends a new event.

Reload knowledge bases

reload

Reloads all knowledge bases. Depending on the configuration, this operation may not be published. It should be available only to administrators.

The OpenAPI specification of the REST API is included in the Appendix A of the Sponge Reference Documentation.

You can define a custom REST API operation (using the ActionDelegateRestApiOperation class in the route builder) that delegates a REST API request to an action call (e.g. to allow implementing an operation body in a scripting language but keeping a static REST interface).

3.14.3.2. OpenAPI specification

The generated OpenAPI specification is currently limited. For example it doesn’t support inheritance e.g. for Sponge data types. Therefore it is most useful for simple customized operations.

3.14.3.3. JSON/Java mapping

The REST API uses the Jackson library to process JSON. A transformation of action arguments and result values is determined by types specified in the corresponding action arguments and result metadata.

The default Jackson configuration for the REST API sets the ISO8601 format for dates.

A BinaryType value is marshalled to a base64 encoded string. This encoding adds significant overhead and should be used only for relatively small binary data.

3.14.3.4. Requests and responses

Each request may contain base properties.

Table 31. Base request properties

Name

Required

Description

id

No

A request identifier. If it is present, the response will contain the same id property with the same value. This feature provides some compatibility with the JSON-RPC protocol.

username

No

A user name that may be used in a user/password authentication mode. In that case, if there is no user name present, the anonumous user is assumed.

password

No

A user password that may be used in a user/password authentication mode.

authToken

No

An authentication token that may be used in a token-based authentication mode.

3.14.3.5. Session

For each request the REST API service creates a thread local session. The session provides access to a logged user and a Camel exchange for a thread handling the request. The session can be accessed in an action via the REST API server plugin.

3.14.3.6. Security

The REST API provides only simple security out of the box and only if turned on. All requests allow passing a user name and a password. If the user name is not set, the anonymous user is assumed. A user may have roles.

You may set a security strategy by providing an implementation of the RestApiSecurityService interface. You may find a few examples of such implementations in the source code. In production mode we suggest using Spring Security and configure Camel security. An advanced security configuration has to be set up in Java rather than in a Sponge XML configuration file. You may implement various authorization scenarios, for example using HTTP headers that are available in a Camel exchange.

3.14.3.6.1. Authentication mode

Only a username/password authentication mode is currently supported by the default REST API service implementation.

Table 32. Authentication modes

Name

Description

Username/password

Every request has to contain a username and a password. Invoking the login operation switches to the authentication token mode.

Authentication token

Every request has to contain an authentication token, returned by the login operation. It may not contain neither username nor password.

3.14.5. Sponge REST API client for Dart

The Sponge REST API client for Dart simplifies connecting to a remote Sponge REST API service from applications written in Dart. It could be used in a Flutter mobile application or an AngularDart web application to connect to a Sponge based back-end. The REST API client uses POST methods.

3.14.6. Running external processes

Sponge provides the ProcessInstance API to run an external executable as a subprocess of the Sponge Java process. This feature is used by some of the plugins, for example by the Py4J integration plugin to execute an external Python script.

The process working directory. If null (the default value) then the current directory will be used.

env

name, value

Zero or more additional environment variables for the subprocess.

waitSeconds

Long

The maximum number of seconds to wait after the start of the process. The thread that started the process will be blocked until the time elapses or the subprocess exits. If null (the default value), the thread will not wait.

inputRedirect

InputRedirect

The standard input redirect type (see the following tables). There are convenience methods inputAs…​ available.

outputRedirect

OutputRedirect

The standard output redirect type (see the following tables). There are convenience methods outputAs…​ available.

errorRedirect

ErrorRedirect

The standard error redirect type (see the following tables). There are convenience methods errorAs…​ available.

charset

String

The the charset of the subprocess streams used if the redirect type is STRING.

waitForPositiveLineRegexp

String

The Java regular expression of a line from the process output text stream. The thread that started the process will wait (blocking) for such line. If set to null, the thread will not wait for a specific line (or waitForNegativeLineRegexp if set).

waitForNegativeLineRegexp

String

Sets the Java regular expression of a line from the process output text stream that signals an error and should cause throwing an exception.

waitForLineTimeout

Long

The timeout for waiting for a specific line from the process output stream (in seconds). If null, the thread could wait indefinitely. If the timeout is exceeded, the exception will be thrown.

inputString

String

The input string that will be set as the process standard input. Applicable only if the input redirect type is STRING.

inputBinary

byte[]

he input bytes that will be set as the process standard input. Applicable only if the input redirect type is BINARY.

Table 34. Standard input redirect type

Value

Description

PIPE

Indicates that subprocess standard input will be connected to the current Java process over a pipe. This is the default handling of subprocess standard input.

INHERIT

Sets the destination for subprocess standard input to be the same as those of the current Java process.

STRING

Sets the subprocess input as the ProcessConfiguration.inputString string.

BINARY

Sets the subprocess input as the ProcessConfiguration.inputBinary bytes.

FILE

Sets the subprocess input as the ProcessConfiguration.inputFile file specified as the filename.

STREAM

Sets the subprocess input as a stream. This is a special case of PIPE that makes easier writing to and closing the subprocess standard input ProcessInstance.getInput() after start. Then you should invoke manually ProcessInstance.waitForReady().

Table 35. Standard output redirect type

Value

Description

PIPE

Indicates that subprocess standard output will be connected to the current Java process over a pipe. This is the default handling of subprocess standard output.

INHERIT

Sets the destination for subprocess standard output to be the same as those of the current Java process.

STRING

Writes all subprocess standard output to the ProcessInstance.outputString string. The thread that started the subprocess will wait for the subprocess to exit.

BINARY

Writes all subprocess standard output to the ProcessInstance.outputBinary byte array. The thread that started the subprocess will wait for the subprocess to exit.

FILE

Writes all subprocess standard output to the ProcessInstance.outputFile file. The thread that started the subprocess will wait for the subprocess to exit.

CONSUMER

Sends a subprocess standard output as text lines to a line consumer (if set). It also logs the subprocess standard output to the logger (as INFO).

Table 36. Standard error redirect type

Value

Description

PIPE

Indicates that subprocess error output will be connected to the current Java process over a pipe. This is the default handling of subprocess error output.

INHERIT

Sets the destination for subprocess error output to be the same as those of the current Java process.

STRING

Writes all subprocess error output to the ProcessInstance.errorString string. The thread that started the subprocess will wait for the subprocess to exit.

FILE

Writes all subprocess error output to the ProcessInstance.getErrorFile file. The thread that started the subprocess will wait for the subprocess to exit.

EXCEPTION

Throw an exception if the error output is not empty. The thread that started the subprocess will wait for the subprocess to exit.

CONSUMER

Sends a subprocess standard error as text lines to a line consumer (if set). It also logs the subprocess error output to the logger (as WARN).

The preferred way to configure redirects is to use inputAs…​, outputAs…​ and errorAs…​ methods.

3.14.7. Python (CPython) / Py4J

Sponge may communicate with external programs written in the reference implementation of the Python programming language - CPython using Py4J, and vice versa. A Python program and a Sponge Java process communicate through network sockets.

Py4J by default uses the TCP port 25333 to communicate from Python to Java and TCP port 25334 to communicate from Java to Python.

Maven configuration

3.14.7.1. Py4J plugins

Sponge provides two plugins for integration with CPython.

Local network sockets used by Py4j should be secured, for example using TLS. Please be aware that all Sponge operations are accessible in other processes that communicate with the Sponge with Py4J enabled by a plugin. See https://github.com/softelnet/sponge/tree/master/sponge-py4j/examples/py4j//java_server_tls for an example of TLS security, based on Py4J examples. Note that in a production environment you should customize this simple configuration, possibly by providing your own configured instance of GatewayServer or ClientServer to the plugin.

Table 37. Py4J plugin common configuration parameters

Name

Type

Description

facadeInterface

String

A Java interface that is a facade to the Py4J entry point object configured on the CPython side.

The configuration of the CPython script that can be run as a subprocess of the Sponge Java process when the plugin is starting up. Typically such script would init the Py4J connection on the CPython side. The plugin automatically adds to the environment variables for the subprocess: PY4J_JAVA_PORT, PY4J_PYTHON_PORT and optionally PY4J_AUTH_TOKEN.

pythonScriptBeforeStartup

boolean

If true, the CPython script will be started before this plugin startup (the default value), otherwise it will be started after this plugin startup.

generateAuthToken

boolean

If true, the plugin will generate the Py4J auth token (for both sides). The default value is false. This option is useful when combined with the pythonScript.

authToken

String

The manual or generated Py4J auth token (for both sides).

randomPorts

boolean

If true, the plugin will use random ports (for both sides). The default value is false. This option is useful when combined with the pythonScript.

# Note that this code is interpreted by Jython in Sponge, not CPythonclassPythonUpperCase(Action):
defonCall(self, text):
result = py4j.facade.toUpperCase(text)
self.logger.debug("CPython result for {} is {}", text, result)
return result

3.14.7.2. Executing an external Python script

The plugin may run a CPython script as a subprocess.

Example of an XML configuration for executing an external Python script

<plugins><pluginname="py4j"class="org.openksavi.sponge.py4j.GatewayServerPy4JPlugin"><configuration><pythonScript><executable>python3</executable><argument>${sponge.configDir}/cpython_script.py</argument><waitSeconds>60</waitSeconds><waitForOutputLineRegexp>The CPython service has started.</waitForOutputLineRegexp><outputRedirect>CONSUMER</outputRedirect></pythonScript><pythonScriptBeforeStartup>false</pythonScriptBeforeStartup></configuration></plugin></plugins>

3.14.8. ReactiveX

The ReactiveX plugin (ReactiveXPlugin) provides support for using ReactiveX in knowledge bases, e.g. for processing stream of Sponge events using reactive programming. The plugin uses RxJava library. The current version of the plugin is very simple. For example it hasn’t got any configuration parameters.

The default name of the ReactiveX plugin (which can be used in knowledge bases) is rx.

The main object provided by this plugin is an instance of a hot observable (rx.observable) that emits all non system Sponge events. The plugin registers a Java-based correlator that listens to Sponge events and sends them to the observable.

3.14.9. MIDI

The MIDI plugin (MidiPlugin) allows processing MIDI messages by the Sponge and provides communication with MIDI devices. It wraps MIDI messages in Sponge events. The plugin supports ShortMessage, MetaMessage and SysexMessage MIDI messages wrapping them respectively in MidiShortMessageEvent, MidiMetaMessageEvent and MidiSysexMessageEvent Sponge events. Although the MIDI support in the Sponge provides a set of methods that use the javax.sound.midi API, the goal of this plugin is not to be a complete interface to the MIDI system but a bridge between MIDI messages and Sponge events.

The default name of the MIDI plugin (which can be used in knowledge bases) is midi.

Table 39. MIDI plugin configuration parameters

Name

Type

Description

sequencerConnectedToSynthesizer

Boolean

If true then the default MIDI sequencer will be connected to the default synthesizer (e.g. to generate sound while playing MIDI files). The default value is false.

loadAllInstruments

Boolean

If true then all instruments in the default soundbank will be loaded at startup. The default value is true.

midiShortMessageEventName

String

A name of a MIDI ShortMessage Sponge event sent by this plugin to the engine. The default value is "midiShort".

midiMetaMessageEventName

String

A name of a MIDI MetaMessage Sponge event sent by this plugin to the engine. The default value is "midiMeta".

midiSysexMessageEventName

String

A name of a MIDI SysexMessage Sponge event sent by this plugin to the engine. The default value is "midiSysex".

The trigger SameSound sends all MIDI short messages received from the input MIDI device to the MIDI synthesizer to generate sounds. It is achieved through the use of the sound method in the midi plugin.

3

The trigger Log only logs a MIDI message info and a note for note on MIDI messages.

4

Connects a default input MIDI device in the system (e.g. a MIDI keyboard) to the MIDI plugin in order to receive all MIDI messages generated by this device and send them to the Sponge engine as Sponge events.

5

Sets the instrument (by name) in the MIDI synthesizer for the MIDI channel 0. Note that this example assumes that the input MIDI device will generate MIDI messages for the same channel.

An event flow in the Sponge engine introduces an additional performance overhead that in some situations may be not acceptable when dealing with real-time physical MIDI instruments.

Maven configuration

Maven users will need to add the following dependency to their pom.xml:

3.14.10. MPD

The MPD plugin (MpdPlugin) provides an integration with an MPD (Music Player Daemon) server. The integration allows knowledge bases to manage an MPD server and gives the possibility to process MPD based events.

The plugin is located in the sponge-mpd Sponge external module. The reason that the MPD support is located in the external Sponge module is that it is released under GNU GPL Version 3 license which is not compatible with the Sponge Apache 2.0 license. This requirement is forced by the license of the underlying library JavaMPD.

Due to the integration issues with the external library, this plugin may not work correctly if connecting to newer versions of the MPD server. As an alternative you may use the mpc command line interface wrapped as an external process. For an example see the MPD/MPC demo project.

The default name of the MPD plugin (which can be used in knowledge bases) is mpd.

Maven configuration

3.14.11. Raspberry Pi - Pi4J

The Pi4J plugin (Pi4JPlugin) allows using the Pi4J library in Sponge knowledge bases. The Pi4J library provides a friendly object-oriented I/O API and implementation libraries to access the full I/O capabilities of the Raspberry Pi platform. The current version of the plugin is very simple. For example it hasn’t got any configuration parameters.

The default name of the Pi4J plugin (which can be used in knowledge bases) is pi4j.

The Pi4J documentation states that You must now have WiringPi installed on your target Raspberry Pi system separately from Pi4J. WiringPi is now included be default in the latest Raspbian builds.

The following example shows how to turn on/off a Grove LED connected to the Raspberry Pi GPIO. The hardware setup for this example includes Raspberry Pi 3, a ribbon cable, a ribbon cable socket, a breadboard, a 4-pin male jumper to Grove 4 pin conversion cable and a Grove LED. Before setting up the hardware make sure that your Raspberry Pi is not powered! The Grove LED should be connected to GPIO via a 4-pin connector: the black wire goes on PIN#14 (Ground), the red wire goes on PIN#02 (DC Power 5V), the yellow wire goes on PIN#12 (GPIO18/GPIO_GEN1), the white wire goes on PIN#06 (Ground).

<sponge><properties><!-- Due to the problem https://github.com/Pi4J/pi4j/issues/319, the dynamic linking option is turned on, where Pi4J is dynamically linked
to WiringPi rather than the default static linking. --><propertyname="pi4j.linking"system="true">dynamic</property></properties><knowledgeBases><knowledgeBasename="kb"><file>pi4j_led_blink.py</file></knowledgeBase></knowledgeBases><plugins><pluginname="pi"class="org.openksavi.sponge.rpi.pi4j.Pi4JPlugin"/></plugins></sponge>

Maven configuration

Maven users will need to add the following dependency to their pom.xml:

3.14.12. Raspberry Pi - GrovePi

The GrovePi plugin (GrovePiPlugin) allows accessing the GrovePi hardware in Sponge knowledge bases. GrovePi is an electronics board for Raspberry Pi that may have a variety of sensors and actuators connected to. The plugin uses Java 8 GrovePi library. The current version of the plugin is very simple. For example it hasn’t got any configuration parameters.

The default name of the GrovePi plugin (which can be used in knowledge bases) is grovepi.

If using this plugin in an embedded Sponge, you have to manually install the Java 8 GrovePi library in you local Maven repository because it isn’t available in the Central Maven Repository.

3.14.13. TensorFlow

Sponge provides integration with TensorFlow. TensorFlow could be used for machine learning applications such as neural networks. The machine learning is a subset of Artificial Intelligence.

Although there could be many ways of using TensorFlow from Java, this integration uses the Py4J library wrapped in the Py4J plugin to communicate between a Sponge Java process and a Python program running TensorFlow. The TensorFlow Python API has been chosen over the Java API, because, at the time of writing, the TensorFlow APIs in languages other than Python were not covered by the API stability promises. For use cases that require low latency times, the usage of Py4J may be insufficient. An alternative approach is to use TensorFlow serving, designed for production environments.

Maven configuration

Maven users will need to add the following dependency to their pom.xml:

3.14.13.1. The Digits recognition REST API service example

This example shows how to expose the TensorFlow machine learning model trained for the MNIST database as a REST API service to recognize handwritten digits. For the complete source code see https://github.com/softelnet/sponge/tree/master/sponge-tensorflow. Please note that the Python language is used both in the Sponge knowledge base (Jython version 2.7) and in the script running TensorFlow (CPython version 3).

The Sponge knowledge base that contains definitions of actions that will be exposed in the Sponge REST API service. The DigitsPredict action takes a binary representation of a PNG file and passes it to the running Python script file by invoking ImageClassifierService.predict(byte[] image) method. This method will be invoked on the remote object running in the Python process.

The Python script file (compatible with CPython) that defines the ConvNet model trained on the MNIST database to recognize handwritten digits. This example uses Keras neural networks API that runs on top of TensorFlow.

The Python script file (compatible with CPython) that loads the model. If the model file data/digits_model.h5 exists, it will be loaded. Otherwise a new model will be trained and saved. This model is then used by the Python-based ImageClassifierService implementation that is exposed by the Python-side Py4J gateway.

The auxiliary Python script file (compatible with CPython) that manually creates, trains and saves the model. It overrides the model file. Additionally the script plots the training and validation loss side by side, as well as the training and validation accuracy.

The Sponge REST API configuration used for this example is not secure. In the production environment you should use HTTPS as well as user authentication.

3.14.14. GSM modem

Sponge provides access to a GSM modem device. The GammuGsmModemPlugin uses Gammu. The requirement is that Gammu utility has to be installed. The current implementation of the GammuGsmModemPlugin is limited. It only sends SMSes. However you may invoke gammu in a knowledge base using the Sponge ProcessInstance API.

The default name of the plugin (which can be used in knowledge bases) is gsm.

Each of these two categories has its pros and cons which makes it better for a certain use. For example scripting languages work well when flexible modification of source code is required.

Libraries written in Java or supported scripting languages may be used, however make sure that they are compatible with the implementations of these languages.

The following chapters describe the best practices for typical use cases.

3.15.1. Events

Sponge is used for developing applications based on event processing. That is why you should start with defining event types. Events should contain enough information (in the form of attributes) so that event processors could provide demanded logic. Moreover, if necessary, you should consider using event chaining, i.e. sending events of a more abstract level based on correlations of low level events.

3.15.2. Plugins

If there is a need for creating an interface to an external system, the best way is to use existing or create a new plugin. Once written plugin could be used in other Sponge based applications.

In most cases a CamelPlugin, by providing access to all Camel components, should be sufficient when integrating with various systems.

If there is a need for creating a custom plugin, in most use cases we suggest creating it in Java rather than in a scripting language.

3.15.3. Processors

When defining a processor that is not a singleton, its class implementation should provide lightweight creating of new instances.

3.15.4. Filters

Filters are especially important when an application cooperates with an external system. If such system sends events, it is a good practice to check if an event contains all expected information and if event attribute values are valid. This type of selection could be done in filters. Filters may also prevent from idly processing events that should be ignored by the application logic at an early phase as they can have an impact on the whole performance.

3.15.5. Triggers

Triggers should be implemented in a way to support concurrent processing of many events by different threads. You should avoid class level (static) variables and restrict, if possible, to data transfered in events.

3.15.6. Rules

Rules should be used when triggers functionality is not sufficient.

3.15.7. Correlators

Correlators should be used when filters, triggers and rules functionality is not sufficient for the problem you try to solve.

3.15.8. Actions

Actions should be created only when there is a need to provide some functionality that is to be used in many knowledge bases that are written in different scripting languages and only when you don’t want to write it in Java.

Maven configuration

3.16.4. Groovy

3.16.4.1. Limitations

In Groovy you cannot define a class or a function twice in the same file. If you want to prepare a processor to reload, you have to put it in a separate file and use sponge.reloadClass() method. That separate file could be modified and reloaded.

For every knowledge base file there is a new Groovy Script instance created. For example when reloading, a new Groovy Script instance is created for each knowledge base file and they are placed in a list (in a reverse order) to be used by the Sponge Groovy interpreter internally.

It seems that a Groovy-based knowledge base must have at east one function (may be empty). Otherwise you may get org.codehaus.groovy.runtime.metaclass.MissingMethodExceptionNoStack exception.

Maven configuration

Maven users will need to add the following dependency to their pom.xml:

3.16.5. JavaScript

Current support for JavaScript in Sponge is deprecated because Nashorn engine is planned to be removed from future JDK releases.

3.16.5.1. Limitations

3.16.5.1.1. Custom class attributes and methods

There is a limitation for using custom class attributes and methods in processors written in JavaScript implementation Nashorn. In that case you should set a class field target in the onInit() method as in the following example. All class fields and methods that are new (i.e. not inherited from the base classes) must be defined in target.

3.16.5.1.2. Abstract processors

The support for abstract processors is not implemented for processors written in JavaScript.

3.16.5.1.3. Dynamic onCall callback methods in actions

Dynamic onCall callback methods are not supported. Every JavaScript action has to implement the abstract Object onCall(Object self, Object[] args) method. Arguments are passed to an action only as an array.

<?xml version="1.0" encoding="UTF-8"?><spongexmlns="https://sponge.openksavi.org"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="https://sponge.openksavi.org https://sponge.openksavi.org/schema/config.xsd"><properties><!-- News that have less words in the title than specified by this parameter will be rejected by filters. --><propertyname="newsFilterWordThreshold"variable="true">3</property><!-- Max size of a buffer that stores latest news. --><propertyname="latestNewsMaxSize"variable="true">5</property></properties><knowledgeBases><!-- Main knowledge base (implemented in Python) that uses 3 files. These files will be loaded by the same interpreter. --><knowledgeBasename="main"><!-- Plugin implemented in Python. --><file>kb/main_plugins.py</file><!-- Main event processors. For the sake of clarity registration of event processors is placed in the next file. --><file>kb/main_event_processors.py</file><!-- Knowledge base callback functions: onInit, onLoad, etc. --><file>kb/main_functions.py</file></knowledgeBase><!-- Actions knowledge base (implemented in JavaScript). --><knowledgeBasename="actions"><file>kb/actions.js</file></knowledgeBase><!-- News generator knowledge base. --><knowledgeBasename="newsGenerator"><file>kb/news_generator.py</file></knowledgeBase></knowledgeBases><plugins><!-- Plugin defined in Java. --><pluginname="echoPlugin"class="org.openksavi.sponge.examples.project.news.MultiEchoPlugin"><configuration><count>2</count></configuration></plugin><!-- Plugin defined in Python. Stores the last news entry". --><pluginname="storagePlugin"class="StoragePlugin"knowledgeBaseName="main"><configuration><storedValue>no news yet</storedValue></configuration></plugin></plugins></sponge>

3.18.1.2. Camel RSS News project

This example is an enhancement over the News project example. It is placed in sponge-examples-project-camel-rss-news (see sources).

The main change here is that news are acquired as RSS feeds from news services: BBC and CNN. Reading RSS feeds and transformation to Sponge events is performed in a Camel route. Sponge acts as a producer in this Camel route. This example shows Sponge as a consumer in other Camel routes as well.

This example also presents integration with Spring framework. A service provided as a Spring bean is accessed from the script knowledge base.

Knowledge bases main and actions that existed in the News project example are not changed. This is because the main processing is independent of the input and output interfaces, protocols or data structures. Internal events (in this case news events) are normalized.

Event flow:

RSS feeds are read from external sources, transformed to Sponge events and sent to the Sponge engine. This is done in Camel routes.

The main knowledge base related event flow is the same as in the previous example.

After the time configured as a property durationOfReadingRss Camel routes that read RSS feeds from external sources are stopped. It simulates lack of new news. This is done in the simulator knowledge base.

When alarm event happens, not only AlarmTrigger (as described in the previous example) handles that event, but here also ForwardAlarmTrigger trigger, defined in the consumer knowledge base. It sends an alarm message to:

all Camel endpoints that use the Sponge engine as a consumer in their routes,

<?xml version="1.0" encoding="UTF-8"?><spongexmlns="https://sponge.openksavi.org"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="https://sponge.openksavi.org https://sponge.openksavi.org/schema/config.xsd"><properties><!-- News that have less words in the title than specified by this parameter will be rejected by filters. --><propertyname="newsFilterWordThreshold"variable="true">3</property><!-- Max size of a buffer that stores latest news. --><propertyname="latestNewsMaxSize"variable="true">5</property><!-- RSS endpoint URI parameters. --><propertyname="rssEndpointParameters"variable="true">?sortEntries=false&amp;consumer.delay=1000</property><!-- Duration of reading RSS feeds from sources (in seconds). --><propertyname="durationOfReadingRss"variable="true">5</property></properties><knowledgeBases><knowledgeBasename="config"><!-- Extended configuration (more complex data structures than in properties section). --><file>kb/config.py</file></knowledgeBase><!-- Main knowledge base (implemented in Python) that uses 3 files. These files will be loaded by the same interpreter. --><knowledgeBasename="main"><!-- Plugin implemented in Python. --><file>kb/main_plugins.py</file><!-- Main event processors. For the sake of clarity registration of event processors is placed in the next file. --><file>kb/main_event_processors.py</file><!-- Knowledge base callback functions: onInit, onLoad, onStartup, etc. --><file>kb/main_functions.py</file></knowledgeBase><!-- Actions knowledge base (implemented in JavaScript). --><knowledgeBasename="actions"><file>kb/actions.js</file></knowledgeBase><!-- A knowledge base that simulates lack of new news after a specified time by stopping corresponding Camel routes. --><knowledgeBasename="simulator"><file>kb/simulator.py</file></knowledgeBase><!-- As a consumer in Camel routes. --><knowledgeBasename="consumer"><file>kb/consumer.py</file></knowledgeBase></knowledgeBases><plugins><!-- Plugin defined in Java. --><pluginname="echoPlugin"class="org.openksavi.sponge.examples.project.camelrssnews.MultiEchoPlugin"><configuration><count>2</count></configuration></plugin><!-- Plugin defined in Python. Stores the last news entry. --><pluginname="storagePlugin"class="StoragePlugin"knowledgeBaseName="main"><configuration><storedValue>no news yet</storedValue></configuration></plugin></plugins></sponge>

Python-based extended configuration - config.py

# Set configuration variables.# For the sake of clarity setting of configuration variables is done in the main level of the script. This code typically would be# in onInit() callback function. However, because these are constants, a potential reload (causing this code to be executed once more)# wouldn't cause any problems.
sponge.setVariable("rssSources", {"BBC":"http://rss.cnn.com/rss/edition.rss", "CNN":"http://feeds.bbci.co.uk/news/world/rss.xml"})

Python-based knowledge base that sends messages to Camel as a consumer - consumer.py

# Sends alarm messages to Camel endpoints in two ways.classForwardAlarmTrigger(Trigger):
defonConfigure(self):
self.withEvent("alarm")
defonRun(self, event):
# Emit the alarm message to all Camel endpoints that use the engine as a consumer.
camel.emit(event.get("message"))
# Send the alarm message to a specific endpoint.
camel.sendBody("direct:log", event.get("message"))
sponge.getVariable("alarmForwarded").set(True)

Running

The resulting archive target/sponge-demo-api.war is the web application providing the Demo REST API service. The archive target/sponge-scripts.zip contains Sponge script files and the Digits recognition example files (see the TensorFlow integration chapter) that will be accessed by the web application.

Assuming that Tomcat is installed in /opt/tomcat and the Sponge script files and the Digits recognition example files are extracted into the /opt/tomcat/sponge directory, you should add the following properties to the catalina.properties file:

The sample file password.txt contains the hashed, insecure password password for the user admin. The user admin has access to more actions that the anonymous user. This simple password can be used only for development and tests. In the case of publishing the service, this file should contain the hashed, secret and strong password.

3.18.1.4. IoT on Raspberry Pi

The IoT on Raspberry Pi project shows how to use Sponge to read sensors, set actuators, take pictures, send SMS messages, send emails and execute OS commands.

The Sponge standalone command line application is installed on a Raspberry Pi with a GrovePi extension board. Sponge provides a synchronous REST API to remotely call actions (that for example change state of actuators). It also sends sensor data (temperature, humidity and light) to an MQTT broker using Apache Camel. The project allows processing sensor data on two levels: locally on the Raspberry Pi edge device by Sponge (to avoid sending too much data to a management system) or by an external system that connects to the MQTT broker.

Prerequisites

The Linux distribution used for this example is Raspbian. All command are invoked by the user pi. For SMS sending the gammu utility should be installed.

$ sudo apt-get install gammu

Installation

First you should download and unpack the Sponge standalone command line application into the /home/pi/local/app/ directory. The directory /home/pi/local/app/examples/sponge-iot-rpi (containing the example knowledge base files) should be copied to /home/pi/local/ in order to modify the configuration files in a fresh copy.

Configuration

The sponge_iot.properties file allows the configuration of the service name, the phone number that will receive SMS notifications, the email address for notifications, the temperature threshold to trigger sending an SMS notification, the email client settings and the MQTT broker settings.

REST API Actions

The subset of Sponge actions is published via the Sponge REST API. The published actions have their metadata configured. These actions could be used by the Sponge mobile client application to manage the IoT device using a GUI.

Table 45. The published actions

Name

Description

SetGrovePiMode

Sets the GrovePi mode (auto or manual). In the auto mode the device behavior is automated according to the following rules. The LCD display shows the current temperature and humidity. The red LED is turned on if there is dark in the room. The blue LED light depends on the position of the rotary angle sensor. The auto mode is implemented by triggers and correlators. In the manual mode the actuators (LCD, LEDs, etc.) can be managed manually via the published actions.

ManageLcd

Provides management of the LCD properties, i.e. the display text and color.

ManageSensorActuatorValues

Provides management of the sensor and actuator values. Reads the temperature and humidity sensor, the light sensor, the rotary angle sensor and the sound sensor. Sets the values of the LEDs and the buzzer.

3.18.2. Scripting examples

Each of these examples is also used in the corresponding JUnit class as a test case with assertions. Note that not all of these examples will work in the standalone application because some of them require additional setup.

The Sponge core implementation. This artifact includes a shaded Guava library.

sponge-jython

Yes

The support for Python-based scripting knowledge bases using Jython.

sponge-jruby

Yes

The support for Ruby-based scripting knowledge bases using JRuby.

sponge-groovy

Yes

The support for Groovy-based scripting knowledge bases.

sponge-nashorn

Yes

The support for JavaScript-based scripting knowledge bases using Nashorn.

sponge-kotlin

Yes

The support for Kotlin-based non scripting knowledge bases.

sponge-signal

Yes

The wrappers for Operating System signals.

sponge-camel

Yes

The Apache Camel integration.

sponge-spring

Yes

The Spring framework integration.

sponge-py4j

Yes

The CPython integration that uses Py4J.

sponge-midi

Yes

The MIDI integration.

sponge-rpi-pi4j

Yes

The Pi4J (for Raspberry Pi) library integration.

sponge-reactivex

Yes

The ReactiveX integration.

sponge-rest-api-client

Yes

The Sponge REST API client.

sponge-rest-api-server

Yes

The Sponge REST API server.

sponge-tensorflow

Yes

The TensorFlow integration.

sponge-standalone

Yes

The standalone version of Sponge.

sponge-standalone-extensions

Yes

Dependencies for external libraries used by the standalone command-line application.

sponge-logging

Yes

The Sponge logging used by the standalone application.

sponge-test

Yes

The Sponge test support.

sponge-rpi-grovepi

No

The GrovePi (for Raspberry Pi) library integration.

sponge-examples-projects

No

Complete example projects.

sponge-distribution

No

Contains documentation, release configuration, project pages etc.

sponge-integration-tests

No

Sponge integration tests.

The following optional artifacts are not part of the standard Sponge distribution and are available under a variety of licenses (incompatible with Apache 2.0) but can be used to extend Sponge functionality.

Table 50. Sponge optional external Maven artifacts

ArtifactId

License

Central Maven Repository

Description

sponge-mpd

GNU GPL 3.0

Yes

MPD (Music Player Daemon) integration.

3.20. Standalone command-line application

For a brief introduction to the Sponge standalone command-line application see Quickstart.

If you need additional libraries (e.g. Camel components) you should place JAR files into the lib directory. You should use only compatible versions of these libraries.

3.20.1. Command-line options

Option

Description

-c <arg>

Use given Sponge XML configuration file. Only one configuration file may be provided.

-k [name=]files

Use given knowledge base by setting its name (optional) and files (comma-separated). When no name is provided, a default name 'kb' will be used. This option may be used more than once to provide many knowledge bases. Each of them could use many files.

-s <file>

Use given Spring configuration file. This option may be used more than once to provide many Spring configuration files.

-m

Create an Apache Camel context.

-i [name]

Run in an interactive mode by connecting to a knowledge base interpreter. You may provide the name of one of the loaded knowledge bases, otherwise the first loaded knowledge base will be chosen.

-e

Applicable only in an interactive mode. Print all exceptions (e.g. also thrown in event processors running in other threads). Helpful for development purposes.

-h

Print help message and exit.

-v

Print the version information and exit.

-D property=value or -Dproperty=value

Set the Java system property.

3.20.2. Default parameters

Standalone command-line application sets its own default values for the following engine configuration parameters. You may change them in an XML configuration file.

Parameter

Value

mainProcessingUnitThreadCount

10

asyncEventSetProcessorExecutorThreadCount

Same as mainProcessingUnitThreadCount

eventQueueCapacity

100000

Examples

# Change directory to Sponge bin/.
# Run with the specified Sponge XML configuration file.
./sponge -c ../examples/script/py/triggers_hello_world.xml
# Run with the knowledge base named 'helloWorldKb' using the specified knowledge base file.
./sponge -k helloWorldKb=../examples/script/py/triggers_hello_world.py
# Run with the knowledge base named 'kb' using the specified knowledge base file.
./sponge -k ../examples/script/py/triggers_hello_world.py
# Run with two knowledge bases.
./sponge -k filtersKb=../examples/script/py/filters.py -k heartbeatKb=../examples/script/js/rules_heartbeat.js
# Run in an interactive mode.
./sponge -k filtersKb=../examples/script/py/filters.py -i
# Run in an interactive mode and setup printing all exceptions to the console.
./sponge -k filtersKb=../examples/script/py/filters.py -i -e
# Run one knowledge base that use two files. Take caution not to use the same names for functions or classes in the files belonging to the same knowledge base.
./sponge -k ../examples/standalone/multiple_kb_files/event_processors.py,../examples/standalone/multiple_kb_files/example2.py

3.20.3. Environment variables

Optionally you may set the environment variable SPONGE_HOME.

Linux/MacOS/Unix

cd sponge-1.10.0
export SPONGE_HOME=`pwd`

Windows

cd sponge-1.10.0
set SPONGE_HOME=%cd%

3.20.4. Standalone plugin configuration parameters

Table 51. Standalone plugin configuration parameters

Name

Type

Description

spring

XML element

Spring configuration. A Spring context is created only when there is a spring configuration element present.

engineBeanName

String

The optional engineBeanName attribute of the spring element defines a Spring bean name that will reference the engine instance in the Spring context. The default value is spongeEngine.

camel

Boolean

The optional camel attribute of the spring element may be used to create a Camel context.

spring/file

String

Spring configuration files. The Spring context implementation used here is GenericGroovyApplicationContext, that allows to load XML and Groovy configuration files.

3.20.5. Spring

You may provide Spring configuration files using a command-line option or defining StandalonePlugin plugin in Sponge XML configuration file. This plugin allows to specify Spring configuration files that will be loaded. The name of this plugin must be "standalonePlugin".

3.20.7. Logging and exception reporting

3.20.7.1. Non interactive mode

If you experience too many logs in the console while running a non-interactive standalone command-line application, you may want to change a logging configuration in config/logback.xml. For example to change a console threshold filter level from INFO to ERROR:

3.20.7.2. Interactive mode

In an interactive mode a predefined console logger appender (configured in config/logback.xml) is turned off programmatically.

Exceptions thrown from other threads of the Sponge engine are not printed into the console. You may change that behavior by specifying -e command-line option.

3.20.8. REST API

You may enable the Sponge REST API in the standalone command line application but such configuration will provide no user management and a very limited security. Thus it can be used only in a secure network or for test purposes.

Manual start of the REST API (autoStart must be turned off) is required because the REST API server must start after the Camel context has started.

Used for integration with MPD (Music Player Daemon) server. Note that this library is GPL licensed therefore it is not fully compatible with the Apache License, Version 2.0.

The complete list of these libraries may be found in the THIRD-PARTY.txt and licenses.xml files of the standalone distribution.

4. Mobile client application

4.1. Introduction

The Sponge mobile client application is a Flutter application that provides a generic GUI to call remote Sponge actions. It can be run on both Android and iOS. It could be used as the one app to rule them all for Sponge services that publish actions via the Sponge REST API. All business logic has to be placed in Sponge actions, so the only requirement to have the application working is to create your own Sponge service by writing a knowledge base that define actions that will be visible and ready to run in the application or to connect to an existing Sponge service.

The application is currently under development. It will be released as an open source.

The application is especially useful when data and functionality are more important than visual aspects of a GUI, e.g. for prototyping, rapid application development, low cost solutions, etc. The reason is that the application provides only a generic and opinionated GUI whose customization is limited.

Figure 3. The mobile application architecture

One of many use cases of the application is to connect to IoT devices that have Sponge installed and execute available actions. Different types of such devices provide different sets of actions that are specific to a device features. For example one device could have a camera and provide an action to take a picture. Another device could have a temperature sensor and provide its readings or have a machine learning model and provide predictions.

You could build a customized GUI using your own Flutter code that will call the same Sponge actions. In that case the generic Sponge mobile client would be used as a Flutter library in your Flutter project.

4.2. Functionalities

The following chapters show the key functionalities of the mobile application.

4.2.1. Connections

Figure 4. Selecting a connection to the Sponge instance in the action list screen

You may configure many connections to Sponge REST API services. The application allows you to connect to a single service at the same time.

Figure 5. List of connections to Sponge instances

You may add, edit and remove connections to Sponge instances as well as activate a connection. To remove a connection swipe the corresponding element.

Figure 6. Editing a connection to a Sponge instance

A Sponge address is the URL of the Sponge instance.

4.2.2. Action list

Figure 7. The action list

The main screen shows the list of actions defined in the connected Sponge engine. Only actions that have argument and result metadata are available. This is because the application uses a generic access to the actions utilizing their data types, labels, descriptions, features and so on. The number in the action icon is the number of action arguments.

To call an action or set action attributes tap the triangular icon on the right side of the action label.

The floating button allows to refresh the action list from the server. The refresh button clears all entered action arguments and received results.

The application currently doesn’t supports all Sponge data types.

4.2.3. Navigation

Figure 8. The navigation drawer

The navigation drawer allows switching between the available main views.

4.2.4. Action call

Figure 9. The action call that manages the Raspberry Pi LCD display

Actions may have read only, provided arguments only to show a data from the server (see the Current LCD text attribute). The REFRESH button retrieves the current values of read only, provided arguments from the server.

The definition of the action that manages the Raspberry Pi LCD display

The action call screen shows all action arguments, for example a drawing.

Figure 18. The action call that shows argument dependencies

Action arguments may depend on each other. Argument dependencies are supported in the action call panel and allow creating simple, interactive forms where some arguments are provided by the server, some entered by the user, some read only and some depend on the values of others. The important thing is that all that configuration is defined in an action in a knowledge base placed on the server side, not in the mobile application.

The definition of the action that provides arguments with dependencies

4.3. Advanced use cases

4.3.1. Context actions

Context actions can be specified for actions, record arguments and list elements (see the list-details) to provide related, customized sub-actions. Context actions should be specified as the contextActions feature statically for a type or an action or dynamically for an annotated value. The latter option takes precedence.

Default (e.g. "ContextAction"). In case of a context action for an action, all action arguments as a record (represented as a map) will be passed as the first argument of the context action. In case of a record argument or a list element, the record or the list element value will be passed as the first argument respectively.

Explicit substitution (format "ContextAction(targetArgName=sourceArgName,…​)", e.g. "ContextAction(contextArg1=arg1,contextArg2=arg2)"). In case of a context action for an action, the argument contextArg1 of the context action will be set to a value of the argument arg1 of the parent action and the argument contextArg2 of the context action will be set to a value of the argument arg2.

Implicit substitution (format "ContextAction(sourceArgName,…​)", e.g. "ContextAction(arg1)"). In case of a context action for an action, the first argument of the context action will be set to a value of the argument arg1 of the parent action.

No arguments passed (e.g. "ContextAction()").

The target argument must be have the same type as the source value.

A source argument name could be a path if a source value is a record, e.g. "ContextAction(contextArg1: arg1.field1)". The this keyword can be used as the source argument name. In case of a context action for an action it is a record of all parent action arguments. In case of a record argument or a list element it is the value itself.

If a context action requires more arguments than passed from a parent action, a new action call screen will be shown. If a context value is an annotated value, the screen will show a header containing a label of the annotated value.

The context actions feature is not propagated in an annotated value feature to sub-actions.

4.3.2. List-details

Figure 24. The list-details action in the action list

Figure 25. The list-details main action screen

Figure 26. The CRUD and context actions

Figure 27. The create/add sub-action

Figure 28. The read/view sub-action

Figure 29. The update/modify sub-action

Figure 30. The delete/remove sub-action

Figure 31. The context action returning a binary result

Figure 32. The context action with no result

Figure 33. The context action with an additional argument

Figure 34. The result of the context action with an additional argument

The main action should have a list argument that is provided with the overwrite option. The action shouldn’t be callable. The list argument type can be annotated and the provided list elements may have labels (AnnotatedValue().withLabel()) and descriptions. The list argument may have the following features: createAction, readAction, updateAction, deleteAction. Their values are the sub-action names that will be called to perform the CRUD operations.

The CRUD actions should not be visible in the actions list so they should have the visible feature set to False.

In the default scenario read, update and delete actions should have the first argument corresponding to the value of the list element. In most cases the argument visible feature should be set to False to hide it. Its type should be the same as the list element’s type. The value of the list element will be passed as this argument. In the case of a create action, no argument corresponding to any list element is necessary.

The result of a create, an update and a delete action is ignored and should be set to withNoResult.

After calling a CRUD action the main action arguments are refreshed.

4.4. User experience

Figure 35. The application dart theme

The application may be switched to the dark theme in the settings.

4.5. Supported Sponge concepts

4.5.1. Data types

Table 58. Data types

Type

Description

AnyType

Not supported.

BinaryType

Editing (as an action attribute) is supported only for image/png mime type with drawing characteristic feature. Viewing is supported for image formats supported by Flutter and other binary content supported by the open_file Flutter plugin that is used by the application.

BooleanType

Supported.

DateTimeType

Viewing supported. Editing is currently limited to the DATE_TIME date kind.

DynamicType

A limited support. This functionality is experimental.

IntegerType

Supported.

ListType

Editing is supported with limitations (see the Advanced use cases chapter). Viewing not supported. This functionality is experimental.

MapType

Not supported.

NumberType

Supported.

ObjectType

Not supported.

RecordType

Editing supported. Viewing support is limited to fields that have simple data types. This functionality is experimental.

StreamType

Not supported.

StringType

Supported.

TypeType

Not supported.

VoidType

Only viewing supported.

4.5.2. Data type formats

Table 59. Supported data type formats

Format

Description

phone

A phone number format. Applicable for StringType.

email

An email format. Applicable for StringType.

url

A URL format. Applicable for StringType.

console

A console format. Text is presented using a monospaced font. Applicable for StringType.

Should be set in an action that represents a user logout in the user management functionality.

intent: signUp

Action

Should be set in an action that implements a user sign up in the user management functionality.

intent

Type

A type with an intent is handled by the application in a specific way.

intent: username

Action argument type

Indicates that the action argument represents a username. Applies only for actions that implement the user management functionality. This intent may be omitted if an action argument name is username.

intent: password

Action argument type

Indicates that the action argument represents a password. Applies only for actions that implement the user management functionality. This intent may be omitted if an action argument name is password.

multiline

StringType

If True, a string will be multilined in the GUI. Defaults to false.

maxLines

StringType

A maximum number of lines in the GUI.

obscure

StringType

If True, a string will be obscured in the GUI, e.g. in case of passwords. Defaults to false.

callLabel

Action

An action call button label in the action call screen. Defaults to RUN if the feature is not set. If set to None, the button will not be shown.

refreshLabel

Action

An action refresh button label in the action call screen. Defaults to REFRESH if the feature is not set. If set to None, the button will not be shown. The refresh button fetches provided action arguments from the server. Only arguments provided with read only or overwrite flags will be refreshed.

clearLabel

Action

An action clear button label in the action call screen. Defaults to CLEAR if the feature is not set. If set to None, the button will not be shown. The clear button resets the action argument values.

cancelLabel

Action

An action cancel button label in the action call screen. Defaults to CANCEL if the feature is not set. If set to None, the button will not be shown.

contextActions

Action, RecordType, ListType action argument

Context actions. For more information on context actions and sub-actions see the Advanced use cases chapter.

createAction

ListType action argument

A create sub-action for a list element.

readAction

ListType action argument

A read sub-action for a list element.

updateAction

ListType action argument

A update sub-action for a list element.

deleteAction

ListType action argument

A delete sub-action for a list element.

width

BinaryType drawing

An image width.

height

BinaryType drawing

An image height.

strokeWidth

BinaryType drawing

A drawing stroke width.

color

BinaryType drawing

A drawing pen color.

background

BinaryType drawing

A drawing background color.

A data type property can be dynamically overwritten by a corresponding feature in an AnnotatedValue. The feature name has to be exactly the same as the data type property name. The overwrite is handled by the Sponge mobile client application.

4.6. Included demos

The access to actions in the mobile application is generic. However the application may include demos that use a customized UI.

4.6.1. Handwritten digit recognition

Figure 36. The navigation drawer if connected to a Sponge instance that supports a digit recognition

If the current connection points to a Sponge instance that has the required action that performs a handwritten digit recognition, this demo is enabled in the navigation drawer.

Figure 37. The digit recognition demo - the information dialog

Figure 38. The digit recognition demo - drawing a digit

The digit recognition demo screen allows drawing a digit that will be recognized by the Sponge action. After each stroke the remote action call is made and the result is shown in the circle.

A.3.30. ValueSetMeta

Appendix B: Release notes

Unless noted otherwise in the release notes, Sponge releases (starting with version 1.5.0) that have the same major.minor numbers are compatible.

1.10.0 (2019-05-10)

Added new callback functions in knowledge bases: onBeforeLoad and onAfterLoad.

Added support for registering data types (EngineOperations methods addType, getType and getTypes). A registered data type instance has its property DataType.registeredType set to a name of a registered type.

Added support for RecordType inheritance.

Added support for automatic use of auth tokens in the REST API client.

Added a thread local session to the REST API service. The session provides access to a logged user and a Camel exchange.

Fixed the bug that caused a NullPointerException when sending a record with a null value of a field in the REST API request.

Moved the sponge-mpd dependency (GPL) from sponge-standalone-extensions to sponge-distribution and changed the sponge-standalone-extensions license to Apache.

Dependencies upgrade.

1.8.0 (2019-03-04)

API change: Action arguments and result metadata are now specified as data types. ArgMeta and ResultMeta classes have been removed.

API change: The processor metadata properties, data type properties have to be specified using the builder-style methods, e.g. self.withLabel("Label").withDescription("Description"). The builder-style methods in the metadata classes follow the naming convention with<Property>, e.g. BinaryType().mimeType("image/png") is now BinaryType().withMimeType("image/png").

API change: Renamed ArgProvidedMeta to ProvidedMeta, ArgProvidedValue to ProvidedValue and ProvidedMeta.depends to ProvidedMeta.dependencies.

API change: The Action.onProvideArgs(names, current, provided) has been changed to Action.onProvideArgs(ProvideArgsContext context).

1.7.0 (2019-02-01)

API change: A provided argument specification in now placed in the ArgProvidedMeta class, not directly in the ArgMeta as before.

API change: Removed LabeledValue and ArgProvidedValue.valueSetDisplayNames because of a new support for an annotated value set.

API change: Removed inputString, inputBinary, inputFile, outputFile, errorFile, outputLineConsumer, errorLineConsumer methods from the ProcessConfigurationBuilder. The preferred way to configure redirects is to use inputAs…​, outputAs…​ and errorAs…​ methods.

Added new methods callIfExists in the EngineOperations.

Added Processor.version and verification of an action qualified version in the REST API.

Dependencies upgrade.

1.6.0 (2019-01-11)

A noteworthy new feature: There is a possibility to provide action argument values and possible value sets in the action configuration. It makes easier creating a generic UI for an action call that reads and presents the actual state of the entities that are to be changed by the action and its arguments.

Added a new REST API operation actionArgs that fetches the provided action arguments from the server.

A type for an action argument or result metadata may specify one or more tags.

Dependencies upgrade.

1.3.3 (2018-07-12)

Added a new attribute to the REST API actions operation that allows specifying an action name or a regular expression.

Added a new ActionType type that allows using a result of one action to be a type for another action argument or result.

Fixed the error that may happen while loading optional knowledge base files if a directory doesnt’t exist.

The sponge-core artifact now shades the Reflections artifact (that use Guava).

1.3.2 (2018-07-06)

Fixed the error that may happen while loading optional knowledge base files using the SpringKnowledgeBaseFileProvider.

Libraries upgrade (most notably Spring to version 5).

1.3.1 (2018-07-04)

Java-based processors may be enabled and disabled by scanning Java packages (enableJavaByScan).

Knowledge base files may be specified using wildcards.

Support for custom roles in the REST API. Roles are represented as String rather than an enumeration.

Support for complex data types in the REST API. A type specification in action metadata has changed.

Minor bug fixes.

Dependencies upgrade.

1.3.0 (2018-06-20)

API change: Action.onCall callback method behavior has been changed to dynamic. Custom actions define onCall methods with the arbitrary number of named arguments, for example def onCall(self, value, text): in Python.

Added support for specifying non script knowledge bases in an XML configuration.

A type of a script knowledge base is no logger required in an XML configuration when knowledge base files are specified.

The sponge-core artifact now shades Guava, so the sponge-core-shaded artifact has been removed since it is no longer needed.

Downgraded Jython to version 2.7.0 to prevent stability issues.

Added sponge-external group of projects in order to provide functionalities that require licenses incompatible with Apache 2.0.

Added MPD support (in sponge-external, because it is GNU GPL 3.0 licensed).

Dependencies upgrade.

1.0.6 (2017-12-01)

The new, shaded version of sponge-core named sponge-core-shaded for an embedded use in custom applications that experience version conflicts in Guava or Quartz. Now you have to explicitly add a dependency to either sponge-core or sponge-core-shaded in your pom.xml.

Support for integration with CPython using Py4J.

The new startup system event.

Dependencies upgrade.

1.0.5 (2017-10-12)

API change: The EngineOperations method callAction has been renamed to call.

The new attribute required in the knowledge base file configuration that, if set to false, allows specifying optional (non existing) knowledge base files.

Script knowledge base files are looked up also in the file system relative to the XML configuration file parent directory.

Abstract processor classes in script knowledge bases are now compatible with the auto-enable mechanism. The support for abstract processors is implemented for Python, Groovy, Ruby but not for JavaScript.

CamelProducerAction may be also set in the Camel In message header named CamelSpongeAction.

SpringEngine may be configured not to start immediately (see SpringEngineBuilder.autoStartup method).

Improvements in the interactive mode of the standalone command-line application (now JLine is used, supporting for example a command history).

Fixed the error preventing substitution of properties defined via the EngineBuilder API in the properties section in the XML configuration files.

1.0.4 (2017-09-20)

Implementation of unordered rules, i.e. rules that listen to unordered sequences of events. Unordered rules is a new feature that should be treated as an experimental one.

API change: The Rule methods setConditions and setAllConditions have been renamed respectively to addConditions and addJavaConditions.

Fixed the error in the interactive mode of the standalone application preventing exit when using exit or quit command.

Dependencies upgrade.

Distribution as a Docker container.

1.0.3 (2017-08-30)

Optimization of event set processors initialization.

Optimization of creating new instances of processors in script-based interpreters.

API change: onInit in correlators will be invoked after onAcceptAsFirst, not before.