Skip to content
/ gepard Public
forked from gessinger-hj/gepard

General purpose communications for distributed applications / events, locks, semaphores, messages (JavaScript, Java, Python)

License

Notifications You must be signed in to change notification settings

mcozd/gepard

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

gepard

General purpose communication and synchronization layer for distributed applications / events, semaphores, locks and messages for JavaScript, Java and Python

Overview

Gepard is a system consisting of a broker and connected clients. The communication is done via sockets or web-sockets. The sockets are always open so that any partner of a connection may be informed if this connection ended. This is very useful in the area of semaphores and locks.
A client uses only one socket for all interactions with the broker. Thus a program needs only 1 client for all features. In order to use only 1 Client instance it is suggested to use the static method

  • JavaScript:
gepard = require  ( "gepard" )  :
var client = gepard.getClient ( [ port [, host ] ] ) ;
  • Java:
import org.gessinger.gepard.Client ;
Client client = Client.getInstance ( [ port [, host ] ] ) ;
  • Python:
import gepard
client = gepard.Client.getInstance ( [ port [, host ] ] ) ;

Up to now a client is a standalone JavaScript program, a JavaScript app inside a browser, a Java program or a Python program. In the next step clients for other languages like Php, Perl etc are planned.

The broker can be instantiated from a JavaScript program but the most common and simplest way to use it is to start it detached as a daemon.

The appropriate command is:

node_modules/.bin/gp.broker

This starts the Broker and the corresponding web-socket-proxy
If you want to start the broker alone:

node_modules/.bin/gp.broker.no.web

There is a separate program for administration purposes:

node_modules/.bin/gp.info

or

node_modules/.bin/gp.admin [ --help ]

What is new

Release 1-4-0 New Heartbeat Protocol to ensure the Availability of Connections

Gepard is based on fast communication by means of always-open sockets. Therefore it is crucial to monitor these connections. This is achieved by a mechanism which exchanges packets between the broker and all connected clients in fixed time intervals defined by the broker. This interval is transmitted to clients as they connect.


The broker sends a __PING__ message to the connected clients in each interval to which all clients are expected to respond with a __PONG__ message within the three next intervals. If no such response is received by the end of the third interval, the broker closes the connection-socket.
On the other end, after dispatching a __PONG__ message, the client waits for the next __PING__ from the broker to arrive within 3 intervals. In case the subsequent __PING__ is not received, the client closes the connection socket and emits a __"disconnect"__ event to signal the status to the application.
If the client is configured to re-connect, it will try to establish a new connection to the broker in a pre-defined interval. On success, the client will emit a __"reconnect"__ event to the application. All gepard-event-listeners which had been registered at the time of disconnect will then automatically be registered with the broker again.

Example time-out conditions are:

  • Broker restart after maintenance
  • Backup time of a virtual machine
  • Restart of a firewall

Parameter, details and example

Release 1-3-3 New FileContainer class for JavaScript and Java to simplify file-transfer.

An instance of the FileContainer class may be inserted at any place inside the body of an Event.
If the client runs on the same machine as the broker only the full path-name of the file will be transferred.
If the broker runs on a different machine the content of the file is read in as a byte-array and transferred as payload to the broker.
If the broker detects a target on a different machine the file is read in and put into the event's body before sending the data.
This is done on a per connection basis.
Details

Release 1-3-0 Let's talk about Python

In this release a full featured Python client is included. The implementation is pure generic Python code. The features are:

  • emit event
  • listen to events
  • request / result ( messages )
  • semaphores ( synchronously / asynchronously )
  • locks

Controlling Connections and Actions with a Hook

In order to control connections and actions a default hook class is provided: ConnectionHook

This class contains several methods which are called in appropriate cases:

connect ( connection )
shutdown ( connection, event )
getInfoRequest ( connection, event )
addEventListener ( connection, eventNameList )
sendEvent ( connection, eventName )
lockResource ( connection, resourceId )
acquireSemaphore ( connection, resourceId )

Each of these methods must return an answer wether to allow or reject the corresponding action.
The answer must be either a boolean value or a Thenable which means a Promise object of any kind.
The default for shutdown is to return a false value if the incoming connection is not from localhost. In all other cases the default returns a true
The parameter can be used to test the allowance in a deeper way.
For example using a Promise for shutdown enables an asynchronous check with help of a database configuration.
To configure this hook a subclass of ConnectionHook must be implemented and defined as user-hook in an JSON configuration file:

{
  "connectionHook": "<path-to-javascript-code>/XmpConnectionHook"
}

This hook file is require'd with the start of the broker.
In this case the command to start the broker is:

gp.broker --config=<full-config-file-name>

An example for a user defined hook is the XmpConnectionHook.js file:

var util = require ( "util" ) ;
var ConnectionHook = require ( "gepard" ).ConnectionHook ;
var XmpConnectionHook = function()
{
  XmpConnectionHook.super_.call ( this ) ;
};
util.inherits ( XmpConnectionHook, ConnectionHook ) ;
XmpConnectionHook.prototype.connect = function ( connection )
{
  console.log ( "connection.getRemoteAddress()=" + connection.getRemoteAddress() ) ;
  return true ;
};
module.exports = XmpConnectionHook ;

If you prefer to start the broker from within your own JavaScript-program the configuration object can be set like:

  var b = new Broker() ;
  b.setConfig ( <config-object or path to config-json-file> ) ;
  b.listen() ;

The parameter connection in the above method-signatures is an internal used object with mainly the public useful methods:

  1. connection.isLocalHost()
  2. connection.getRemoteAddress()
  3. connection.getHostName()
  4. connection.getLanguage()
  5. connection.getApplicationName()
  6. connection.getApplication()
  7. connection.getId()

Perfect load balanced message handling.

The use-case for request/respond is enhanced to a perfect load balancing.
Suppose there are n message-listeners offering the same service-name ( event-name )
m messages come in to the broker with m = n + 1
The following is done:

  1. the first n messages are sent to the n free listener for processing the request.
  2. the m-th message is stored inside the broker waiting for any of the listeners sending back the response.
  3. after receiving the first message-response from any listener the waiting m-th + 1 message is sent to the now free listener.

As long as a sent message is not returned the Broker stores it in relation to the worker connection. If this connection dies the stored message is sent back to the originator marked with the fail state and appropriate text. The status can be tested with event.isBad() which returns true or false.

Java bindings for all features:

  • emit event
  • listen to events
  • request / result ( messages )
  • semaphores
  • locks

With this it is very easy to communicate or synchronize between JavaScript programs or webapps in a browser with a Java server or Java program.

The conversion from JSON to Java and vice versa is done with the Gson Google library for Java.

If you need special serialization / deserialization you may set the appropriate Gson instance in the Event-class statically with the method Event.setGson() ;

The Event-class may convert the json to map Java's byte[]-array to NodeJS's Buffer and vice versa. This can be set statically by Event.mapByteArrayToJavaScriptBuffer ( boolean state ). The default is true.

Install

npm install gepard

or the newest stable but development version:

npm install git+https://github.com/gessinger-hj/gepard

Getting Started

Here are some kind of "Hello World" examples.

All commands are in the directory: node_modules/.bin or node_modules/.bin/gepard

Up to now the JavaScript and the Java classes are implemented.
The examples show the nice and easy interaction between programs written in these different languages.

Base

  1. gp.broker
    Start the gepard broker with websocket proxy

  2. gp.shutdown
    Send a shutdown event to all clients an stop the broker

  3. gp.info
    Show basic information from the broker

JavaScript

  1. gp.listen --name=hello
    Start a listener for events named hello
    If you want to listen to all events with name starting with hello use a wildcard:
    gp.listen "--name=hello*"

  2. gp.emit --name=hello [--body='{"City":"Frankfurt"}'] emit an event with name hello

  3. gp.sem
    Acquire a semaphore

  4. gp.lock
    Acquire a lock

  5. If you want to play with the web-client implementation use the appropriate files in: node_modules/gepard/xmp/webclient


To simplyfy this the command
gp.http.simple [options]

is supplied starting a simple js webserver detached. Options are:

  • --port=<port>, default=8888
  • --root=<web-root>, default=node_modules/gepard/xmp/webclient
  • --index=<index-file>, default=index.html

Start your browser and go to: localhost:8888

  1. gp.http.simple.shutdown
    Stop the simple webserver.

  2. gp.http.simple.is.running
    Check if the simple webserver is running.


In order to try out the examples goto node_modules/gepard/xmp.
The following examples exist:
  • Listener.js
  • Emitter.js
  • EmitterWithBody.js
  • EmitterWithStatusInfo.js
  • Requester.js
  • Responder.js
  • Locker.js
  • AsyncSemaphore.js

Java

In order to try out the examples goto node_modules/gepard/java. All examples are included in lib/Gepard.jar. With the following command all examples can be executed:

java [-D<name>=<value>] -cp lib/Gepard.jar:lib/gson-2.3.1.jar org/gessinger/gepard/xmp/Listener

Listener may be replaced by:

  • Listener
  • Emitter
  • EmitterWithBody
  • EmitterWithStatusInfo
  • Requester
  • Responder
  • Locker
  • AsyncSemaphore
  • BlockingSemaphore

The class-version in the existing Gepard.jar is 1.6, so you need to have at least java 1.6 installed. There is an ant file to build your own jar.

Options, e.g. for the event-name must be set in the common Java format: -Dname=hello

Python

In order to try out the examples goto node_modules/gepard/python/xmp.

The following examples exist:

  • Listener.py
  • Emitter.py
  • EmitterWithBody.py
  • EmitterWithStatusInfo.py
  • Requester.py
  • Responder.py
  • Locker.py
  • AsyncSemaphore.py
  • BlockingSemaphore.py

Configuration

The communication is based on sockets. Thus only the port and optional the host must be specified to use Gepard. The defaults are:

  • port=17501
  • host=localhost
  • web-socket port=17502

The port, host and logging directory can be set either by supplying these items

  1. within creating an instance of Client or Broker in your code.

  2. as startup arguments of your program as:

    • -Dgepard.port=<port>
    • -Dgepard.host=<host>
    • -Dgepard.log=<log-dir>
  3. with environmant variables of the form:

    • export ( or set ) GEPARD_PORT=<port>
    • export ( or set ) GEPARD_HOST=<host>
    • export ( or set ) GEPARD_LOG=<log-dir>

Use Cases

Configuration Changes (Events)

Suppose you have 1 program that changes configuration-entries in a database-table. After the new entries are committed the program sends an event with:

client.emit ( "CONFIG-CHANGE" ) ;

Several clients do their work based on these data.

All clients including web-clients setup a listener for example with

client.on ( "CONFIG-CHANGE", function callback(e) {} ) ;

Concurrent editing of a Dataset (Semaphores)

Two users with their web browser want to edit the same user-data in a database. In this case a Semaphore is very useful.


Both do
gepard.port = 17502 ;
var sem = new gepard.Semaphore ( "user:4711" ) ;
this.sem.acquire ( function sem_callback ( err )
{
  // we are owner
  fetch data, edit and save

  then:

  this.release() ; // with this statement the second user's browser app is callbacked
}) ;

Synchronization of file processing (Locks)

Suppose there are many files in a directory waiting to be processed.
Let's name the directory: foo/bar/input
In order to speed up the overall processing several identical programs should work together.
In this case Locks are very useful. The following should be done:

var fs     = require ( "fs" ) ;
var gepard = require ( "gepard" ) ;
var lock ;

var array = fs.readdirSync ( "foo/bar/input" ) ;
for ( var i = 0 ; i < array.length ; i++ )
{
	lock = new gepard.Lock ( array[i], function()
	{
		try
		{
			if ( ! this.isOwner() ) return ;
			.............. process file ................
		}
		finally
		{
			this.release() ;
		}
	} ) ;
}

A Nice Exotic Mixture of Programming Languages

Suppose the following: There are a couple of JavaScript and Python programs to interact with a database. The database changes. And it would be nice to not change modules for database access.
Especially if the new database is an Oracle database. Perhaps on Linux.
Everybody who had ever tried to install the appropriate NodeJS or Python module ended up in a mess of build, configuration and installation problems.
One of the nicest Java features is the total abstraction of the database handling via the JDBC api.
It is clear what to do: Use a Java Gepard client connected to a database and execute all simple REST kind of actions via request/respond on the basis of Gepard.
In this combination changing a database vendor is only a 10 second job changing the connection url and restart the Java client. Ok: another 5 seconds. But that's it.
No compilation, no installation no problems.

The Event Body

This member of an event is the holder for all payload-data. In all languages this is a hashtable with the restriction that the key must be of type string.

  • Java: Map<String,Object>
  • JavaScript: {} or in long form: Object
  • Python: {} which is in fact an object of type dict

Setter and getter are the appropriate methods Event.putValue(name,value) and Event.getValue(name).
value is either a scalar type, a hashtable with strings as keys and valid object types, a list containing valid object types or a a combination of all valid types. Thus a set of data like a tree can be used.
Note: Gepard's data exchange mechanism is NOT intended to transport serialized objects between clients.
The valid types are:

  • scalar type objects: string, int, double, number
  • array type objects:
    • Array ( [] )
    • List ( [] )
  • hashtable type objects:
    • Java: Map<String,Object>
    • Python: dict ( {} )
    • JavaScript: Object ( {} )

There are 2 type of objects which are treated by gepard in a special way:

  • dates
    • JavaScript: Date
    • Java: Date
    • Python: datetime.datetime
  • bytes
    • JavaScript: Buffer
    • Java: byte[]
    • Python: bytearray, bytes ( bytes should not be used to send because in python < 3 bytes is a subclass of str, typeof byte == 'str' and thus cannot be detected by this mechanism)

In these cases an object is transferred from a generic class of a sender to the generic class of the receiver which means it is reconstructed in the target programming language.
Note on Python:
The built-in date class in python is not able to parse or format ISO date strings. In order to enable full interoperability related to dates the gapard module tries to import the well known dateutils module. This in turn imports the six module. If these modules are in python's module path the generic python date class can be used.
Details in:

Examples

Ready to use examples for JavaScript are located in .../gepard/xmp
Ready to use examples for Java are located in .../gepard/gepard/java/org.gessinger/gepard/xmp and compiled in .../gepard/java/lib/Gepard.jar
Ready to use examples for Python are located in .../gepard/python/xmp

Examples Short

Event listener

Adding a event-listener with the on() method may be done with a single event-name or a list of event-names.

JavaScript: client.on ( "ALARM", callback )
or client.on ( [ "ALARM", "BLARM" ], callback )

Java: client.on ( "ALARM", callback )
or client.on ( new String[] { "ALARM", "BLARM" }, callback )

Python: client.on ( "ALARM", callback )
or client.on ( [ "ALARM", "BLARM" ], callback )

The callback will be called with an Event object of the appropriate name ( e.getName() )

Application

  var gepard = require ( "gepard" ) ;
  var client = gepard.getClient() ;

Browser

  var client = gepard.getWebClient ( 17502 ) ;

Code

client.on ( "ALARM", function event_listener_callback(e)
{
  console.log ( e.toString() ) ;
});

Java

import org.gessinger.gepard.Client ;
import org.gessinger.gepard.EventListener ;
import org.gessinger.gepard.Event ;
Client client = Client.getInstance() ;

client.on ( "ALARM", new EventListener()
{
  public void event ( Event e )
  {
    System.out.println ( e ) ;
  }
} ) ;

Python

import gepard
client = gepard.Client.getInstance()

def on_ABLARM ( event ):
  print ( "on_ALARM" )
  print ( event )

client.on ( "ALARM", on_ABLARM )

Details in:

Event Emitter

Application

var gepard = require ( "gepard" ) ;
var client = gepard.getClient() ;
client.emit ( "ALARM",
{
  var thiz = this ;
  write: function()
  {
    thiz.end() ; // close connection after written
  }
});

Browser

var client = gepard.getWebClient ( 17502 ) ;
client.emit ( "CONFIG-CHANGED" ) ;

Java

import org.gessinger.gepard.Client ;
Client client = Client.getInstance() ;
client.emit ( "ALARM" ) ;

Python

import gepard
client = gepard.Client.getInstance()
client.emit ( "ALARM" )

Details in:

Locks

Application

  var gepard = require ( "gepard" ) ;
  var lock = new gepard.Lock ( "resid:main" ) ;

Browser

gepard.port = 17502 ;
var lock = new gepard.Lock ( "resid:main" ) ;

Code

lock.acquire ( function ( err )
{
  console.log ( this.toString() ) ;
  if ( this.isOwner() )
  {
    .........
    this.release() ;
  }
} ) ;

Java

import org.gessinger.gepard.Lock ;
Lock lock = new Lock ( "resid:main" ) ;
lock.acquire() ;
if ( lock.isOwner() )
{
  .........
  lock.release() ;
}

Python

import gepard

lock = gepard.Lock ( "resid:main" )
lock.acquire()

if lock.isOwner():
  ......................
  lock.release()

Details in:

Semaphores

Application

  var gepard = require ( "gepard" ) ;
  var sem = new gepard.Semaphore ( "user:4711" ) ;

Browser

gepard.port = 17502 ;
var sem = new gepard.Semaphore ( "user:4711" ) ;

Code

sem.acquire ( function ( err )
{
  console.log ( this.toString() ) ;

    .....................

  this.release() ;
} ) ;

Java

Asynchronously

import org.gessinger.gepard.Semaphore ;
import org.gessinger.gepard.SemaphoreCallback ;
final Semaphore sem = new Semaphore ( "user:4711" ) ;
sem.acquire ( new SemaphoreCallback()
{
  public void acquired ( Event e )
  {
    System.out.println ( sem ) ;
    .....................
    sem.release() ;
  }
}) ;

Synchronously

import org.gessinger.gepard.Semaphore ;
final Semaphore sem = new Semaphore ( "user:4711" ) ;
// with or without a timeout
sem.acquire(5000) ;

if ( sem.isOwner() ) // if not timeout occured
{
    .....................
  sem.release() ;
}

Python

Asynchronously

import gepard

def on_owner(sem):
  ................
  sem.release()

sem = gepard.Semaphore ( "user:4711" )
sem.acquire ( on_owner )

Synchronously

import gepard

sem = gepard.Semaphore ( name )

sem.acquire ( 5 ) # with or without a timeout

if sem.isOwner():
  ...........
  sem.release()

Details in:

Request / Result

Send request

Application:

  var gepard = require ( "gepard" ) ;
  var client = gepard.getClient() ;

Browser:

  var client = gepard.getWebClient ( 17502 ) ;

Code:

client().request ( "getFileList"
, function result(e)
  {
    console.log ( e.getBody().list ) ;
    this.end() ;
  });

Java

import org.gessinger.gepard.Client ;
import org.gessinger.gepard.ResultCallback ;
import org.gessinger.gepard.Util ;
import java.util.List ;
final Client client = Client.getInstance() ;
client.request ( "getFileList", new ResultCallback()
{
  public void result ( Event e )
  {
    if ( e.isBad() )
    {
      System.out.println ( "e.getStatusReason()=" + e.getStatusReason() ) ;
    }
    else
    {
      List<String> list = (List<String>) e.getBodyValue ( "file_list" ) ;
      System.out.println ( Util.toString ( list ) ) ;
    }
    client.close() ;
  }
}) ;

Python

import gepard

client = gepard.Client.getInstance()

def getFileList ( e ):
  if e.isBad():
    print ( e.getStatusReason() )
  else:
    print ( e.getValue ( "file_list" ) )
  e.getClient().close()

client.request ( "getFileList", getFileList )

Details in:

Send result

Application:

  var gepard = require ( "gepard" ) ;
  var client = gepard.getClient() ;

Browser:

  var client = gepard.getWebClient ( 17502 ) ;

Code:

var list = [ "one.js", "two.js", "three.js" ] ;
client.on ( "getFileList", function ( e )
{
  e.getBody().list = list ;
  e.sendBack() ;
});

Java

import org.gessinger.gepard.Client ;
import org.gessinger.gepard.EventListener ;
import org.gessinger.gepard.Event ;
final Client client = Client.getInstance() ;
client.on ( name, new EventListener()
{
  public void event ( Event e )
  {
    String[] fileList = new String[] { "a.java", "b.java", "c.java" } ;
    e.putBodyValue ( "file_list", fileList ) ;
    e.sendBack() ;
  }
} ) ;

Python

import gepard
client = gepard.Client.getInstance()

fileList = [ "a.py", "b.py", "c.py" ] ;
def on_getFileList ( event ):
  print ( "Request in" ) ;
  print ( "File list out:" ) ;
  print ( fileList ) ;
  event.body["file_list"] = fileList ;
  event.sendBack() ;

client.on ( "getFileList", on_getFileList )

Details in:

Examples Long

Event listener

In Application

var gepard = require ( "gepard" ) ;
var c = gepard.getClient() ;

var eventName = "ALARM" ;
console.log ( "Listen for events with name=" + eventName ) ;
c.on ( eventName, function(e)
{
  console.log ( e ) ;
});
c.on('end', function()
{
  console.log('socket disconnected');
});
c.on('error', function ( event)
{
  console.log( event );
});
c.on('shutdown', function()
{
  console.log('broker shut down');
});

In Browser

<script type="text/javascript" src="Event.js" ></script>
<script type="text/javascript" src="MultiHash.js" ></script>
<script type="text/javascript" src="GPWebClient.js" ></script>
    var wc = gepard.getWebClient ( 17502 ) ;
    this.wc.on ( "open", function onopen()
    {
    }) ;
    this.wc.on ( "error", function onerror()
    {
    }) ;
    this.wc.on ( "close", function onclose()
    {
    }) ;
		wc.on ( "ALARM", function event_listener_callback ( e )
		{
		  console.log ( e.toString() ) ;
		}) ;

Event Emitter

In Application

var gepard  = require ( "gepard" ) ;
var c = gepard.getClient() ;

var event = new gepard.Event ( "CONFIG-CHANGED" ) ;
event.setBody ( { "config-name" : "app.conf" } ) ;
c.emit ( event,
{
  write: function() // close connection after write
  {
    c.end() ;
  }
});

In Browser

<script type="text/javascript" src="Event.js" ></script>
<script type="text/javascript" src="MultiHash.js" ></script>
<script type="text/javascript" src="GPWebClient.js" ></script>
var wc = gepard.getWebClient ( 17502 ) ;
var event = new gepard.Event ( "CONFIG-CHANGED" ) ;
event.setBody ( { "config-name" : "app.conf" } ) ;
wc.emit ( event ) ;

File Transfer with the FileContainer Class

The basic usage of this class is as follows:

FileSender

JavaScript:
See also: gepard/xmp/FileSender.js

var gepard  = require ( "gepard" ) ;
var client = gepard.getClient() ;
var event = new gepard.Event ( "__FILE__" ) ;
var file = "<full-file-name>" ;
event.putValue ( "DATA", new gepard.FileContainer ( file ) ) ;
client.request ( event, function ( e )
{
  if ( e.isBad() )
  {
    console.log ( e.getStatus() ) ;
  }
  else
  {
    console.log ( "File " + file + " sent successfully." )
  }
  this.end() ;
}) ;

Python:
See also: gepard/python/xmp/FileSender.py

client = gepard.Client.getInstance()

event = gepard.Event ( "__FILE__" )

file = "<full-file-name>" ;
event.putValue ( "DATA", gepard.FileContainer ( file ) )

def result ( e ):
  if e.isBad():
    print ( e.getStatusReason() )
  else:
    print ( "File " + file + " sent successfully." )
  e.getClient().close()

print ( "Sending " + file )
client.request ( event, result )

Java:
See also: gepard/java/org.gessinger/gepard/xmp/FileSender.java

    final Client client = Client.getInstance() ;

    Event event = new Event ( "__FILE__" ) ;
    final String file = "<full-file-name>"
    event.putValue ( "DATA", new FileContainer ( file ) ) ;
    client.request ( event, new ResultCallback()
    {
      public void result ( Event e )
      {
        if ( e.isBad() )
        {
          System.out.println ( e ) ;
        }
        else
        {
          System.out.println ( "File " + file + " sent successfully." ) ;
          System.out.println ( "code: " + e.getStatusCode() );
          System.out.println ( "name: " + e.getStatusName() );
          System.out.println ( "reason: " + e.getStatusReason() );
        }
        client.close() ;
      }
    }) ;

##FileReceiver

JavaScript:
See also: gepard/xmp/FileReceiver.js

  var client = gepard.getClient() ;
  client.on ( "__FILE__", function(e)
  {
    var data = e.removeValue ( "DATA" ) ;
    console.log ( data.getName() + " received." ) ;
    var fname = data.getName() + ".in" ;
    try
    {
      data.write ( fname ) ;
      console.log ( fname + " written." ) ;
    }
    catch ( exc )
    {
      e.control.status = { code:1, name:"error", reason:"could not write: " + fname } ;
      console.log ( exc ) ;
    }
    e.sendBack() ;
  });

Python:
See also: gepard/python/xmp/FileReceiver.py

client = gepard.Client.getInstance()

def on___FILE__ ( e ):
  data = e.removeValue ( "DATA" )
  print ( data.getName() + " received." ) ;
  fname = data.getName() + ".in"
  try:
    data.write ( fname ) ;
    print ( fname + " written.")
  except Exception as exc:
    print ( exc )
  e.sendBack() ;

client.on ( "__FILE__", on___FILE__ )

Java:
See also: gepard/java/org.gessinger/gepard/xmp/FileReceiver.java

final Client client = Client.getInstance() ;
client.on ( "__FILE__", new EventListener()
{
  public void event ( Event e )
  {
    try
    {
      FileContainer fileContainer = (FileContainer) e.removeValue ( "DATA" ) ;
      String fname = fileContainer.getName() + ".in" ;
      fileContainer.write ( fname ) ;
      System.out.println ( fname + " written." );
      e.setStatus ( 0, "success", "File accepted." ) ;
    }
    catch ( Exception exc )
    {
      e.setStatus ( 1, "error", "File not saved." ) ;
      System.out.println ( Util.toString ( exc ) ) ;
    }
    try
    {
      e.sendBack() ;
    }
    catch ( Exception exc )
    {
      System.out.println ( Util.toString ( exc ) ) ;
    }
  }
} ) ;

Heartbeat and Reconnection Capability Parameterization

Broker Side

The default ping interval for the broker is 180000 milli-sec or 3 minutes. This value can be changed in different ways:

  1. Startup parameter: --gepard.heartbeat.millis=<nnn>
  2. Evironment variable: GEPARD_HEARTBEAT_MILLIS=<nnn>
  3. Variable in configuration-file: { "heartbeatMillis":<nnn> }
  4. In program: broker.setHeartbeatMillis ( <nnn> )

Client Side

The default is reconnect=false

This value can be changed in different ways:

  1. Startup parameter: --gepard.reconnect=true
  2. environment variable: export GEPARD_RECONNECT=true
  3. client.setReconnect ( true ) before any socket connection is active
  4. gepard.setProperty ( 'gepard.reconnect', 'true' )

If the boker is shut down all clients receive a shutdown event. If reconnect==true the appropriate callback in the client is called and the client api tries to reconnect.
At this place a call to client.setReconnect(false|False) initiates a Client-shutdown without reconnection.
There are two new callbacks available signaling the state-change to the owner-application:

  • Java

    1. client.onReconnect ( InfoCallback icb )
    2. client.onDisconnect ( InfoCallback icb )
  • Python

    1. client.onReconnect ( <callback> )
    2. client.onDisconnect ( <callback> )
  • JavaScript

    1. client.on ( "reconnect", <callback> )
    2. client.on ( "disconnect", <callback> )

Example to test

  1. open a terminal, execute: node_modules/.bin/gp.broker or node node_modules/gepard/src/Broker.js
  2. open a terminal, execute: node node_modules/gepard/xmp/Listener.js
  3. open a terminal, execute: python node_modules/gepard/python/xmp/Listener.py
  4. open a terminal, goto node_modules/gepard/java
    execute: java -cp lib/Gepard.jar:lib/gson-2.3.1.jar org.gessinger.gepard.xmp.Listener

Then goto terminal one and kill the Broker either with ^C ( ctrl+C ) or with kill -9 <pid> or with the taskmanger on windows.
Appropriate output is visible.
Then start the Broker again and all clients reconnect again. Check with gp.info that all event-listener are registered again.

Technical Aspects of the Client

NodeJS clients use the powerful but simple framework for asynchronously callbacks.
In Java and Python this asynchronicity is modeled by threads. There is a single thread reading from the socket connected to the Broker. Incoming events are dispatched and the appropriate callbacks are executed. The main thread is not affected.
Thus any number of event listener may be registered or removed in the main thread.
Synchronous callbacks are needed with Locks and Semaphores. In this case an id-based blocking message queue is used for communication between the threads.
Incoming events for asynchronous processing are propagated to separate running worker threads via a blocking fifo queue. Thus callbacks do not block each other. This applies to Java and Python.
By default 2 threads are running. This can be changed with the method Client.setNumberOfCallbackWorker(). Maximum is 10.
From this it is clear that all callback methods run in the context of one of the internal worker-threads and not in the context of the main thread.
Per default the internal thread is not a daemon thread. If needed this can be changed by calling the method

  • Python: Client.setDaemon([True|False])
  • Java: Client.setDaemon([true|false])

before the first action on a Client instance because the internal thread is started when the first connection is needed.

Found a bug? Help us fix it...

We are trying our best to keep Gepard as free of bugs as possible, but if you find a problem that looks like a bug to you please follow these steps to help us fix it...

  • Update Gepard and make sure that your problem also appears with the latest version of Gepard.

  • Goto the issue tracker to see if your problem has been reported already. If there is no existing bug report, feel free to create a new one. If there is an existing report, but you can give additional information, please add your data to this existing issue. If the existing issue report has already been closed, please only re-open it or comment on it if the same (or a closely related issue) re-appears, i.e., there is a high chance that the very same bug has re-appeared. Otherwise, create a new issue report.

  • Whenever you create a new issue report in our issue tracker, please make sure to include as much information as possible like exceptions with text and stack trace or other log information.
    Having all the required information saves a lot of work.

https://github.com/gessinger-hj/gepard/blob/master/CHANGELOG.md

Contributors

  • Hans-Jürgen Gessinger
  • Paul Gessinger

Features

  • High performance
  • Minimal configuration with
    • GEPARD_PORT
      • GEPARD_HOST
  • All JavaScript client features like event listener, event emitter, semaphores, locks and messages are available in any web-browser apps.
  • All client features are also available for Java and Python

Changelog

See change log details

About

General purpose communications for distributed applications / events, locks, semaphores, messages (JavaScript, Java, Python)

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • JavaScript 59.4%
  • Java 25.7%
  • Python 13.6%
  • HTML 1.3%