Quickstart


1. Concepts

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

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

Trigger

Triggers execute specified operations when an appropriate event happens.

Rule

Rules detect situations when a sequence of events happens.

Correlator

Correlators detect correlations between events and could be used for implementing any complex event processing that isn’t provided by filters, triggers or rules.

1.2. Knowledge base

A knowledge base is a registry where processors are defined.

There are two types of knowledge bases:

  • scripting knowledge bases (i.e. written in a supported scripting language),

  • Java-based knowledge bases (i.e. written in Java).

The main advantage of scripting knowledge bases is a possibility of modification without the need of recompilation or even restarting the system.

Basic features of a knowledge base:

  • contains a logic of event processing,

  • defines callback functions that will be invoked by the Sponge engine in certain life cycles of the system (e.g. on load, on startup, on¬†shutdown).

1.3. Plugins

The connection between the outside world and knowledge bases is provided by plugins.

1.4. Event flow

The figure below shows the event flow in the Sponge engine.

engine event flow
Figure 1. Event flow
Event flow in event processors
Filter

An event that is put into the Input Event Queue goes through defined filters. If the event hasn’t been accepted by one or more of the filters, the event is discarded.

Trigger

If there is a trigger listening for the event that successfully passed the filters, than this trigger is executed.

Rule

If there is a rule that listens to that event, the event will be processed by that rule. In this case the engine will:

  • create a new instance of this rule,

  • or save the event in the internal event tree of the already existing rule instance,

  • or cause the already existing rule instance to fire (i.e. run the rule).

Correlator

If there is a correlator that listens to that event, the event will be processed by that correlator.

2. Distributions

2.1. Embedded in a Java application

Sponge could be embedded in a Java application by adding a Maven dependency.

2.1.1. Maven dependency

If you want to use Sponge with, for example, Python scripting knowledge bases, add this dependency to your pom.xml:

<dependency>
    <groupId>org.openksavi.sponge</groupId>
    <artifactId>sponge-jython</artifactId>
    <version>1.5.0</version>
</dependency>

2.1.2. Creating and starting the Sponge engine

The following example shows how to make Sponge a part of a custom Java application.

Example of starting the embedded Sponge with the configuration file
SpongeEngine engine = DefaultSpongeEngine.builder().config("examples/script/py/triggers_hello_world.xml").build(); (1)
engine.startup(); (2)
1 Creates a Sponge engine by using Engine Builder API and providing the Sponge XML configuration file.
2 Starts up the engine. After startup the engine runs in the background, i.e. using threads other than the current one.

The engine runs until it is shut down explicitly. So, for example, if you place this code in the main method and execute it, the program will run infinitely.

Example of starting the embedded Sponge with the knowledge base file
SpongeEngine engine = DefaultSpongeEngine.builder().knowledgeBase("knowledgeBaseName", "examples/script/py/triggers_hello_world.py").build(); (1)
engine.startup();
1 Creates a Sponge engine by using Engine Builder API providing a Python script knowledge base.

2.2. Standalone command-line program

Prerequisites:

  • Installed Java 1.8 or above.

  • Environment variable JAVA_HOME set or java executable placed in PATH.

Verify Java version
java -version
If necessary, logging levels could be changed in config/logback.xml. Logs will be written to the console as well as to log files placed in logs/ directory.

2.2.1. Linux/MacOS/Unix

First steps:

  • Unpack the archive

    unzip -q sponge-1.5.0-standalone.zip
  • Run Sponge example using a configuration file.

    cd bin
    ./sponge -c ../examples/script/py/triggers_hello_world.xml
    Output console shows
    Hello World!

    The Sponge standalone command-line application continues listening to events in an endless loop. Press CTRL+C to exit.

  • Run Sponge example using the knowledge base file

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

    Press CTRL+C to exit.

  • In most common situations you would run Sponge in the background

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

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. 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.3. Interactive mode

The Sponge standalone command-line program may 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.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.

Example of mounting a host directory
docker run -it -v ~/examples:/opt/examples openksavi/sponge:latest

You may also invoke Sponge directly.

Example of invoking Sponge directly
docker run -it openksavi/sponge:latest ./sponge -c ../examples/script/py/triggers_hello_world.xml

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

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.

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.

Example of calling the action via the REST API
# Call the action to get the JSON response with the result.
curl -i -k -X POST -H "Content-type:application/json" http://localhost:1836/sponge.json/v1/call -d '{"name":"HelloWorldAction","args":["Sponge user"]}'

# You could also get the action metadata as a JSON response.
curl -i -k -X POST -H "Content-type:application/json" http://localhost:1836/sponge.json/v1/actions -d '{"name":"HelloWorldAction"}'

3.1.1. Python

Python Hello World action example knowledge base file
class HelloWorldAction(Action): (1)
    def onConfigure(self): (2)
        self.displayName = "Hello world" (3)
        self.description = "Returns a greeting text."
        self.argsMeta = [ArgMeta("name", StringType()).displayName("Your name").description("Type your name.")] (4)
        self.resultMeta = ResultMeta(StringType()).displayName("Greeting").description("The greeting text.") (5)
    def onCall(self, name): (6)
        return "Hello World! Hello {}!".format(name)

def onStartup(): (7)
    sponge.logger.info("{}", sponge.call("HelloWorldAction", "Sponge user")) (8)
1 The definition of the HelloWorldAction action.
2 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 display name 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 this example can be found in the file actions_hello_world.py.

Running this example in the standalone command-line application
./sponge -k ../examples/script/py/actions_hello_world.py

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.

3.1.2. Ruby

Ruby Hello World action example knowledge base file
class HelloWorldAction < Action (1)
    def onConfigure (2)
        self.displayName = "Hello world" (3)
        self.description = "Returns a greeting text."
        self.argsMeta = [ArgMeta.new("name", StringType.new()).displayName("Your name").description("Type your name.")] (4)
        self.resultMeta = ResultMeta.new(StringType.new()).displayName("Greeting").description("The greeting text.") (5)
    end
    def onCall(name) (6)
        return "Hello World! Hello %s!" % [name]
    end
end

def onStartup (7)
    $sponge.logger.info("{}", $sponge.call("HelloWorldAction", "Sponge user")) (8)
end

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

Running this example in the standalone command-line application
./sponge -k ../examples/script/rb/actions_hello_world.rb

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

3.1.3. Groovy

Groovy Hello World action example knowledge base file
class HelloWorldAction extends Action { (1)
    void onConfigure() { (2)
        this.displayName = "Hello world" (3)
        this.description = "Returns a greeting text."
        this.argsMeta = [new ArgMeta("name", new StringType()).displayName("Your name").description("Type your name.")] (4)
        this.resultMeta = new ResultMeta(new StringType()).displayName("Greeting").description("The greeting text.") (5)
    }

    String onCall(String name) { (6)
        return "Hello World! Hello $name!"
    }
}

void onStartup() { (7)
    sponge.logger.info("{}", sponge.call("HelloWorldAction", "Sponge user")) (8)
}

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

Running this example in the standalone command-line application
./sponge -k ../examples/script/groovy/actions_hello_world.groovy

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

3.1.4. JavaScript

JavaScript Hello World action example knowledge base file
var HelloWorldAction = Java.extend(Action, { (1)
    onConfigure: function(self) { (2)
        self.displayName = "Hello world"; (3)
        self.description = "Returns a greeting text.";
        self.argsMeta = [new ArgMeta("name", new StringType()).displayName("Your name").description("Type your name.")]; (4)
        self.resultMeta = new ResultMeta(new StringType()).displayName("Greeting").description("The greeting text."); (5)
    },
    onCall: function(self, args) { (6)
        // The onCall method in JS always gets an array of arguments. Dynamic onCall callback methods are not supported.
        return "Hello World! Hello " + args[0] + "!";
    }
});

function onStartup() { (7)
    sponge.logger.info("{}", sponge.call("HelloWorldAction", "Sponge user")) (8)
}

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

Running this example in the standalone command-line application
./sponge -k ../examples/script/js/actions_hello_world.js

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

3.2. Hello World trigger example

This chapter presents a different version of the Hello World example. In this case the text "Hello World!" will be printed when an event helloEvent fires a trigger HelloWorldTrigger.

3.2.1. Python

Python Hello World trigger example knowledge base file
class HelloWorldTrigger(Trigger): (1)
    def onConfigure(self): (2)
        self.event = "helloEvent" (3)
    def onRun(self, event): (4)
        print event.get("say") (5)

def onStartup(): (6)
    sponge.event("helloEvent").set("say", "Hello World!").send() (7)
1 The definition of the HelloWorldTrigger trigger.
2 The trigger configuration callback method.
3 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.

3.2.2. Ruby

Ruby Hello World trigger example knowledge base file
class HelloWorldTrigger < Trigger (1)
    def onConfigure (2)
        self.event = "helloEvent" (3)
    end

    def onRun(event) (4)
        puts event.get("say") (5)
    end
end

def onStartup (6)
    $sponge.event("helloEvent").set("say", "Hello World!").send() (7)
end

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

Running this example in the standalone command-line application
./sponge -k ../examples/script/rb/triggers_hello_world.rb

Press CTRL+C to exit.

3.2.3. Groovy

Groovy Hello World trigger example knowledge base file
class HelloWorldTrigger extends Trigger { (1)
    void onConfigure() { (2)
        this.event = "helloEvent" (3)
    }
    void onRun(Event event) { (4)
        println event.get("say") (5)
    }
}

void onStartup() { (6)
    sponge.event("helloEvent").set("say", "Hello World!").send() (7)
}

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

Running this example in the standalone command-line application
./sponge -k ../examples/script/groovy/triggers_hello_world.groovy

Press CTRL+C to exit.

3.2.4. JavaScript

JavaScript Hello World trigger example knowledge base file
var HelloWorldTrigger = Java.extend(Trigger, { (1)
    onConfigure: function(self) { (2)
        self.event = "helloEvent"; (3)
    },
    onRun: function(self, event) { (4)
        print(event.get("say")); (5)
    }
});

function onStartup() { (6)
    sponge.event("helloEvent").set("say", "Hello World!").send(); (7)
}

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.

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.

3.3.1. Python

Python Heartbeat example knowledge base file
# Sounds the alarm when heartbeat event stops happening at most every 2 seconds.
class HeartbeatRule(Rule): (1)
    def onConfigure(self): (2)
        self.events = ["heartbeat h1", "heartbeat h2 :none"] (3)
        self.addConditions("h2", lambda rule, event: rule.firstEvent.get("source") == event.get("source")) (4)
        self.duration = Duration.ofSeconds(2) (5)
    def onRun(self, event): (6)
        sponge.event("alarm").set("severity", 1).send() (7)

class AlarmTrigger(Trigger): (8)
    def onConfigure(self):
        self.event = "alarm"
    def onRun(self, event):
        print "Sound the alarm!"
1 The definition of the rule HeartbeatRule.
2 Rule configuration method.
3 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.

3.3.2. Ruby

Ruby Heartbeat example knowledge base file
# Sounds the alarm when heartbeat event stops happening at most every 2 seconds.
class HeartbeatRule < Rule (1)
    def onConfigure (2)
        self.events = ["heartbeat h1", "heartbeat h2 :none"] (3)
        self.addConditions("h2", lambda { |rule, event|
            return rule.firstEvent.get("source") == event.get("source")
        }) (4)
        self.duration = Duration.ofSeconds(2) (5)
    end
    def onRun(event) (6)
        $sponge.event("alarm").set("severity", 1).send() (7)
    end
end

class AlarmTrigger < Trigger (8)
    def onConfigure
        self.event = "alarm"
    end
    def onRun(event)
        puts "Sound the alarm!"
    end
end

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

Running this example in the standalone command-line application
./sponge -k ../examples/script/rb/rules_heartbeat.rb
After a few seconds the output console shows
Sound the alarm!

Press CTRL+C to exit.

3.3.3. Groovy

Groovy Heartbeat example knowledge base file
// Sounds the alarm when heartbeat event stops happening at most every 2 seconds.
class HeartbeatRule extends Rule { (1)
    void onConfigure() { (2)
        this.events = ["heartbeat h1", "heartbeat h2 :none"] (3)
        this.addConditions("h2", { rule, event ->
            return rule.firstEvent.get("source") == event.get("source")
        }) (4)
        this.duration = Duration.ofSeconds(2) (5)
    }
    void onRun(Event event) { (6)
        sponge.event("alarm").set("severity", 1).send() (7)
    }
}

class AlarmTrigger extends Trigger { (8)
    void onConfigure() {
        this.event = "alarm"
    }
    void onRun(Event event) {
        println "Sound the alarm!"
    }
}

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

Running this example in the standalone command-line application
./sponge -k ../examples/script/groovy/rules_heartbeat.groovy
After a few seconds the output console shows
Sound the alarm!

Press CTRL+C to exit.

3.3.4. JavaScript

JavaScript Heartbeat example knowledge base file
// Sounds the alarm when heartbeat event stops happening at most every 2 seconds.
var HeartbeatRule = Java.extend(Rule, { (1)
    onConfigure: function(self) { (2)
        self.events = ["heartbeat h1", "heartbeat h2 :none"]; (3)
        self.addConditions("h2", function(rule, event) {
            return rule.firstEvent.get("source") == event.get("source");
        }); (4)
        self.duration = Duration.ofSeconds(2); (5)
    },
    onRun: function(self, event) { (6)
        sponge.event("alarm").set("severity", 1).send(); (7)
    }
});

var AlarmTrigger = Java.extend(Trigger, { (8)
    onConfigure: function(self) {
        self.event = "alarm";
    },
    onRun: function(self, event) {
        print("Sound the alarm!");
    }
});

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.