Skip to main content

Communications

This section gives details around client to server and server to server communications. This includes message formats and defining message metadata in the Genesis Application Platform, how to set up encrypted process communication, and using the network API.

GenesisSet

GenesisSet is a generic message format used to send data between Genesis processes. The information in the messages must be stored as key-value pairs. A GenesisSet can store integers, booleans, text, etc. Importantly, it can also contain other GenesisSets.

In this section, we give you some examples that illustrate usage and structure.

Send message to Event Handler service

genesisSet {
MESSAGE_TYPE with "EVENT_LOGIN_AUTH"
SERVICE_NAME with "GENESIS_AUTH_MANAGER"
SOURCE_REF with "sourceRef"
DETAILS with genesisSet {
USER_NAME with "User"
PASSWORD with "Password"
}
}

Send message to Request Server

genesisSet {
MESSAGE_TYPE with "REQ_INSTRUMENT"
SERVICE_NAME with "GCOM_REQUEST_SERVER"
SOURCE_REF with "sourceRef"
REQUEST with genesisSet {
"INSTRUMENT_ID" with "*"
}
}

Using constructor

val genesisSet = GenesisSet()
genesisSet.setInteger("PRICE", 10)
genesisSet.setString("MESSAGE_TYPE", "EVENT_LOGIN_AUTH")

Constructors

SignatureDescription
GenesisSet(expectedSize: Int = 32)Creates a GenesisSet object with a predetermined expected number of key-value pairs.
GenesisSet(fields: MutableMap<String, Any?>)Creates a GenesisSet object using an already existing Map object containing key-value pairs.

Functions

NameSignatureDescription
containsFieldfun containsField(fieldName: String): BooleanChecks whether provided field name exists in GenesisSet
equalsoverride fun equals(other: Any?): BooleanChecks equality of two GenesisSets
getArrayopen operator override fun equals(other: Any?): BooleanGet an array value, or default value if property not present.
getBigDecimalfun getBigDecimal(property: String, defaultValue: BigDecimal)Get a BigDecimal value, or default value if property not present.
getBooleanfun getBoolean(property: String, defaultValue: Boolean)Get a boolean value, or the 'defaultValue' parameter if field not found.
getByteArrayfun getByteArray(property: String): ByteArray?Get a byte array value (assume a Base64 string if a string is found). You can use a full stop to denote an embedded Genesis set, or retrieve the set directly.
getDatefun getDate(key: String, defaultValue: DateTime)Get a DateTime value, or default value if property not present.
getDoublefun getDouble(property: String, defaultValue: Double)Get a double value, or default value if property not present.
getGenesisSetfun getGenesisSet(property: String, defaultValue: GenesisSet)Get a Genesis set, or default value if property not present.
getIntegerfun getInteger(property: String, defaultValue: Int)Get an integer value, or default value if property not present.
getLongfun getLong(property: String, defaultValue: Long)Get a long value, or default value if property not present.
getObjecttailrec fun getObject(property: String, genesisSet: GenesisSet = this): Any?Recursively get an object.
getShortfun getShort(property: String, defaultValue: Short)Get a short value, or default value if property not present.
getStringfun getString(property: String, defaultValue: String)Get a string value, or the default value if property not found. You can use a full stop to denote an embedded Genesis set, or retrieve the set directly.
setArrayfun setArray(key: String, value: Any?)Set an array value. Method can be called repeatedly with the same key to build up a list of values.
setBigDecimalfun setBigDecimal(key: String, value: BigDecimal?)Set BigDecimal value. If the value exists in the set it is overwritten.
setBooleanfun setBoolean(key: String, value: Boolean?)Set integer value. If the value exists in the set it is overwritten.
setByteArrayfun setByteArray(key: String, value: ByteArray?)Set byte array value. If the value exists in the set it is overwritten.
setDatefun setDate(key: String, date: DateTime?)Set date value. If the value exists in the set it is overwritten.
setDirectfun setDirect(property: String, value: Any?)Shorthand method to set a field that may be multiple sets deep.
setDirectNullfun setDirectNull(property: String, value: Any?, defaultValue: Any)Shorthand method to set a field that may be multiple sets deep, and accepts a default value for null parameters.
setDoublefun setDouble(key: String, value: Double?)Set integer value. If the value exists in the set it is overwritten.
setFullArrayfun setFullArray(key: String, array: Iterable<*>)Set a full list of values.
setGenesisSetsetGenesisSet(key: String, set: GenesisSet?)Embed a set inside this set. If the value exists in the set it is overwritten.
setIntegerfun setInteger(key: String, value: Int?)Set integer value. If the value exists in the set it is overwritten.
setLongfun setLong(key: String, value: Long?)Set long value. If the value exists in the set it is overwritten.
setStringfun setString(key: String, value: String?Set string value. If the value exists in the set it is overwritten.
toStringoverride fun toString(): StringReturns a pretty-print string.
unSetfun unSet(field: String)Shorthand method to unset a single field.
unSetDirecttailrec fun unSetDirect(property: String, genesisSet: GenesisSet = this)Shorthand method to unset a field that may be multiple sets deep
unSetGenesisSetfun unSetGenesisSet(key: String)Shorthand method to unset a GenesisSet.
withinfix fun String.with(any: Any?)Sets key-value in GenesisSet Ex: MESSAGE_TYPE with EVENT_INSERT

Companion object

Functions

fun genesisSet(init: GenesisSet.() -> Unit): GenesisSet : This function enables the Kotlin DSL builder used in these examples.

fun builder(): GenesisSetBuilder : This method is commonly used as an alternative to the Kotlin DSL builder when creating GenesisSet objects in Java. GenesisSetBuilder allows you to create new instances of GenesisSet in a fluent way, as shown in the above examples.

Type-safe messages

The Genesis Application Platform uses type-safe messages to perform message serialization and deserialization. In addition, it automatically extracts relevant metadata to expose this to the front end in the form of a Json Schema definition that is compliant with the 2019-09 specification. These messages will be validated automatically in the back end, based on their definition.

These type-safe messages are most commonly used in Request Servers, GPAL Event Handlers and Event Handlers that have been implemented as a set of classes.

Input messages

The input message type I is defined as a Kotlin data class, which specifies all the necessary information to parse the incoming message and to expose it as metadata. Take a look at this example, which we shall discuss below:

enum class LogLevel {
TRACE, DEBUG, INFO, WARN, ERROR
}

data class SetLogLevel(
@Title("Process name")
val processName: String,
@Description("Represents the target logging level")
val logLevel: LogLevel? = null,
val datadump: Boolean = false,
val expiration: Int = 0
)

In this example, the SetLogLevel data class has a single constructor that also defines the properties of the data class. Also note:

  • Mandatory metadata field. processName does not have a default value associated with it; therefore, a value is mandatory to construct this message. So, it will be exposed as a mandatory metadata field.
  • Optional metadata fields. logLevel, datadump and expiration all have default values; they will therefore be exposed as optional metadata fields.

You are free to use all the following types, as long as they are composed using the same elements:

  • Genesis metadata field basic types (Boolean, Short, Int, Long, Double, String, BigDecimal or Joda DateTime)
  • enumerated types (as you can see defined in LogLevel above)
  • basic collection types (List, Set and Map)
  • other Kotlin data classes

All these different types will be understood by the metadata system and exposed accordingly. Kotlin also has nullable and non-nullable types, and the metadata system will expose this information too.

Annotations such as @Title and @Description can be used to provide extra information to the front end.

For example:

  • @Title could be used to provide a human-readable name for a metadata field to be displayed in a grid column.
  • @Description could be used to provide tooltip information when hovering over that column header.

You can find more information in our page about metadata annotations.

Read-only values

Read-only values can be exposed inside a Kotlin companion object and can be as complex as any other metadata field definition. In the example below, the enhanced SetLogLevel class provides information about the default LogLevel:

data class SetLogLevel(
@Title("Process name")
val processName: String,
@Description("Represents the target logging level")
val logLevel: LogLevel? = null,
val datadump: Boolean = false,
val expiration: Int = 0
) {
companion object ReadOnly {
val defaultLogLevel: LogLevel = LogLevel.INFO
}
}

Deserialized fields

There is a significant disadvantage in using type-safe messages with support for default values; once the message has been deserialized, you don't know what the original payload contained.

Following the previous example with the SetLogLevel data class, it is possible to receive a message with just a processName value; you will still have default values for all the other fields because of the automatic defaults. This causes problems where you have business logic where those fields were part of the original payload.

For example, if you receive a value for the field expiration set as 0, you might want to define a different business logic than if the value was never sent in the first place - even though 0 is the same value as the default value.

In order to solve this problem, there is a class called DeserializedFieldsSupport. This class can be extended by any type-safe data class. It is available for both Event Handler definitions and Request Server definitions. The SetLogLevel data class in the previous example would now look like this:

data class SetLogLevel(
@Title("Process name")
val processName: String,
@Description("Represents the target logging level")
val logLevel: LogLevel? = null,
val datadump: Boolean = false,
val expiration: Int = 0
) : DeserializedFieldsSupport() {
companion object ReadOnly {
val defaultLogLevel: LogLevel = LogLevel.INFO
}
}

Any message extending this class will have access to a property called deserializedFields of type Map<String, DeserializedField> This property provides enough information to reconstruct the values that were part of the original payload.

The DeserializedField sealed class definition looks like this:

sealed class DeserializedField {
object Simple : DeserializedField()
data class Array(val fields: List<DeserializedField>) : DeserializedField()
data class Object(val fields: Map<String, DeserializedField>) : DeserializedField()
}

So, if we revisit a real-life example for SetLogLevel in which we only receive field values for processName and datadump, the content of deserializedFields will be a Map with the following key values:

{
"PROCESS_NAME" : DeserializedField.Simple
"DATADUMP" : DeserializedField.Simple
}

If your message has nested arrays or objects, the deserializedFields property will also contain nested structures in the shape of DeserializedField.Array and DeserializedField.Object types.

Output messages

The output message type O can be defined as a single Kotlin data class or sealed class with multiple Kotlin data classes defined as subtypes. For multiple subtypes, the Genesis Application Platform is able to extract information for all the possible messages and expose it as metadata.

As an example, we shall look at EventReply and how Event Handlers work with output types in real life.

Event Handler examples

The default output message type to use in Event Handlers is EventReply. This is a Kotlin sealed class, which is most commonly represented by two subtypes: EventAck and EventNack. The Kotlin definitions of these are:

data class EventAck(val generated: List<Map<String, Any>> = emptyList()) : EventReply()
data class EventNack(
val warning: List<GenesisError> = emptyList(),
val error: List<GenesisError> = emptyList()
) : EventReply()

Alternatively, you can create your own reply type using a normal Kotlin data class or sealed class. The example below defines EventSetLogLevelReply:

sealed class EventSetLogLevelReply : Outbound() {
class EventSetLogLevelAck : EventSetLogLevelReply()
data class EventSetLogLevelNack(val error: String) : EventSetLogLevelReply()
}

These custom reply types allow a predetermined number of customized replies for a single eventHandler codeblock, with their type information exposed in the metadata system. They need to be handled carefully, as the internal error-handling mechanism for the Event Handler is only able to handle EventReply messages. Therefore, non-captured exceptions and errors will break the type-safety guarantees of the reply.

warning

IMPORTANT! The success message should always end in Ack in order for the internal eventHandler logic to handle validation correctly.

Error messages

There is a common format for error or warning messages sent between server and client. The message format is the same for all HTTP and WebSocket messages that we support:

MESSAGE_TYPE = ...
SOURCE_REF = ...
ERROR = [
{
CODE (String) = -- predefined error code,
TEXT (String) = -- human readable message,
STATUS_CODE (ENUM: HttpStatusCode) = -- http status code
// Optional: Additional custom fields
}
]
WARNING = [
{
CODE = -- predefined error code,
TEXT = -- human readable message,
STATUS_CODE (ENUM: HttpStatusCode) = -- http status code
// Optional: Additional custom fields
}
]

All errors and warnings are represented as implementations of the GenesisError interface. This is defined as follows:

interface GenesisError {
val code: String
val text: String
val statusCode: HttpStatusCode
get() = HttpStatusCode.InternalServerError
}

So by default, all error/warning messages have the following properties, along with any extra properties that are needed to represent the error:

  • CODE is the error code, which can be of two types:

    • ErrorCode is the ENUM class that contains a list of different error codes coming from the server, as shown below
    • String is used to pass any code that is not part of ErrorCode enum
  • TEXT is of type String and contains more detailed information about the error code that is being sent.

  • STATUS_CODE is of type ENUM, represented by HttpStatusCode enum class, which corresponds to netty HttpResponseStatus and will be used to represent HTTP status of all error/warning messages.

There is also the common interface GenesisNackReply, for NACK messages:

interface GenesisNackReply {
val warning: List<GenesisError>
val error: List<GenesisError>
}

The interface GenesisNackReply with MESSAGE_TYPE and SOURCE_REF fields represents the whole error or warning message that is sent to the API client.

Types of Nack message

These are the main types of Nack (error or warning) message. Most of them are sent either as EVENT_NACK or MSG_NACK:

Nack message typeDetails
EVENT_NACKused as response for unsuccessful event
MSG_NACKused when something unexpected happens and on anything that is not an event
LOGOUT_NACKused when there is an issue with Logout Request
LOGIN_AUTH_NACKused when there is an issue with Login Request
LOGON_NACKused when there is an issue with Data Server subscription
EVENT_LOGIN_DETAILS_NACKused when there is an issue with provided login details: USER_NAME or SESSION_AUTH_TOKEN
CREATE_MFA_SECRET_NACKused when there is an issue with creation of MFA secret

Error codes

Below is the list of standard error codes, along with their HTTP Status code. The framework implementation is standardized to provide error code CODE as Enum represented by ErrorCode class, but it also provides the flexibility to include any error code.

ErrorCode class definition
enum class ErrorCode(private val readableString: String, val statusCode: HttpStatusCode)
Error CodeHTTP status code
GENERIC_ERROR500 Internal Server Error
MISSING_FIELD400 Bad Request
VALIDATION_ERROR400 Bad Request
INVALID_MESSAGE400 Bad Request
NOT_SUPPORTED_ENUM_VALUE400 Bad Request
NOT_AUTHORISED403 Forbidden
DUPLICATE_KEY500 Internal Server Error
INVALID_MESSAGE_TYPE400 Bad Request
INVALID_DATASOURCE400 Bad Request
INVALID_PARAMETER400 Bad Request
LOGIN_ERROR401 Unauthorized
MULTIPLE_TABLES500 Internal Server Error
MISSING_KEY400 Bad Request
UNKNOWN_TABLE400 Bad Request
UNKNOWN_FIELD400 Bad Request
UNKNOWN500 Internal Server Error
NO_MESSAGE_TYPE400 Bad Request
NO_SOURCE_REF400 Bad Request
NO_USER_NAME400 Bad Request
UNAVAILABLE503 Service Unavailable
UNKNOWN_MESSAGE_TYPE400 Bad Request
FEATURE_NOT_PROVIDED400 Bad Request
FEATURE_NOT_FOUND404 Not Found
JSON_SCHEMA_NOT_FOUND404 Not Found
REJECT_RULE_DOES_NOT_EXIST400 Bad Request
ERROR_CHECKING_EXISTING_RULE400 Bad Request
NO_DS_NAME400 Bad Request
INVALID_DS_NAME404 Not Found
INVALID_INDEX400 Bad Request
REJECT_RULE_MISSING404 Not Found
ERROR_MODIFYING_RULE500 Internal Server Error
INVALID_CRITERIA400 Bad Request
MAX_LOGON_LIMIT429 Too Many Requests
RECORD_NOT_FOUND404 Not Found
SERVICE_NOT_FOUND404 Not Found
DATABASE_FAILURE500 Internal Server Error
DATABASE_ERROR500 Internal Server Error
OPERATION_TIMEOUT408 Request Timeout
DEPENDENT_RECORD_FOUND500 Internal Server Error
REQUIRES_APPROVAL403 Forbidden
APPROVAL_MESSAGE_MISSING400 Bad Request
INTERNAL_ERROR500 Internal Server Error
GATEWAY_ERROR400 Bad Request
REQUEST_FAILED400 Bad Request
UNABLE_TO_UPDATE_APPROVAL500 Internal Server Error
APPROVAL_SAME_USER_CANNOT_ACCEPT400 Bad Request
APPROVAL_RECORD_NOT_FOUND404 Not Found
REJECTED_BY_SERVICE500 Internal Server Error
APPROVAL_DIFF_USER_CANNOT_CANCEL400 Bad Request
APPROVAL_WRONG_STATUS_CANNOT_CANCEL400 Bad Request
APPROVAL_WRONG_STATUS_CANNOT_ACCEPT400 Bad Request
APPROVAL_SAME_USER_CANNOT_REJECT400 Bad Request
APPROVAL_WRONG_STATUS_CANNOT_REJECT400 Bad Request
MISSING_HOSTNAME400 Bad Request
NUMBER_OF_RECORDS_DOES_NOT_MATCH400 Bad Request

HTTP status code

We use standard HTTP status codes to represent the response status. This is a well-known standard that is easy to understand. It is internally represented by the HttpStatusCode enum class, which corresponds to netty HttpResponseStatus.

To get the appropriate status code for an error message, you need to enable this at router-level. You do this using the property strictHttpStatusCode.

For example, strictHttpStatusCode is set to false by default. Because of this, you will get 200 OK for some error messages, and you cannot guarantee that you will always receive the appropriate status code. So, we set strictHttpStatusCode to true in the genesis-router.kts file:

router {
webPort = 9064
socketPort = 9065
strictHttpStatusCode = true
nettyLoggingEnabled = true
}

A single message can contain multiple errors and warnings. Here is how the response status code for the message is allocated:

Messages with only errors

  • If the message contains a single error, then its code is used as the response status code for the message
  • If the message contains multiple errors with the same code, then this is used as the response status code for the message
  • If the message contains multiple errors with different status codes, then:
    • for multiple 5xx errors, the response status code is set to 500
    • for multiple 4xx errors, the response status code is set to the status code of first error
    • for a mix of 5xx and 4xx errors, the response status code is set to 500

Messages with only warnings

  • If there are only warning messages, the response status is set to 400.

Messages with errors and warnings

  • If there are both error messages and warning messages, the response status code is based on the error message or messages:
    • for a single error message or multiple error messages of the same type, this error code is used as the response status code for the message
    • for multiple 5xx errors, the response status code is set to 500
    • for multiple 4xx errors, the response status code is set to the status code of first error
    • for a mix of 5xx and 4xx errors, the response status code is set to 500

Metadata annotations

The following annotations are found in the package global.genesis.message.core.annotation and can be applied when defining Kotlin data classes to be used as input I message types.

As an example, these input types can be used in Event Handlers and custom Request Servers (see type-safe messages).

It is important to note that these annotations will influence the automatic generation of relevant Json schema definitions within the backend metadata system, as well as enforcing transparent validation checks.

caution

The UniqueItems annotation will generate a Json schema uniqueItems definition. Unfortunately, the current specification of uniqueItems means that only primitive values will be checked for uniqueness (i.e. string, integer, etc), as opposed to fully fledged object definitions. One of the consequences is that it is currently impossible to use Json Schema and allow for advanced uniqueItems checks, including unique id checks. See discussions around key-based uniqueness in here.

Annotation nameTargetsParametersDescription
TitleProperty and classtitle: StringSuggests a human readable name for the annotated class or property
DescriptionProperty and classdescription: StringSuggests a human description for the annotated class or property
StringConstantPropertyconstant: StringDefines a constant value, which is always expected for a property
MinItemsPropertyminItems: IntDefines a minimum number of items for a list type property
MaxItemsPropertymaxItems: IntDefines a maximum number of items for a list type property
UniqueItemsPropertynoneMarks a list type property as unique items only
MinLengthPropertyminLength: IntDefines a minimum number of characters in a String property
MaxLengthPropertymaxLength: IntDefines a maximum number of characters in a String property
PatternPropertypattern: StringDefines an expected regular expression pattern for a String property
ShortMinPropertymin: ShortDefines the minimum value expected for a Short property
ShortMaxPropertymax: ShortDefines the maximum value expected for a Short property
IntMinPropertymin: IntDefines the minimum value expected for an Int property
IntMaxPropertymax: IntDefines the maximum value expected for an Int property
LongMinPropertymin: LongDefines the minimum value expected for a Long property
LongMaxPropertymax: LongDefines the maximum value expected for a Long property
DoubleMinPropertymin: DoubleDefines the minimum value expected for a Double property
DoubleMaxPropertymax: DoubleDefines the maximum value expected for a Double property
BigDecimalMinPropertymin: StringDefines the minimum value expected for a BigDecimal property
BigDecimalMaxPropertymax: StringDefines the maximum value expected for a BigDecimal property

Network API

Use these APIs to send and receive messages between micro-services.

ClientConnectionsManager

Use @Inject to create ClientConnectionsManager.

See the example below:

class TestService(@Inject val clientConnectionManager: ClientConnectionsManager) {}

GenesisMessageClient

GenesisMessageClient is a messaging client that can be obtained using ClientConnectionManager. Here is an example:

If you connect successfully to the POSITION_APP_EVENT_HANDLER service, you will get GenesisMessageClient. Otherwise, you will get null.

class TestAuthManagerService(@Inject val clientConnectionManager: ClientConnectionsManager) {
private val genesisMessageClient = clientConnectionManager.getGenesisMessageClient("POSITION_APP_EVENT_HANDLER")
// custom code here
}

Constructor

GenesisMessageClient(address: String, port: Int, secure: Boolean, configuration: NetworkConfiguration)

Functions

NameSignature
addConnectionEventHandlerfun addConnectionEventHandler(handler: ConnectionEventHandler)
removeConnectionEventHandlerfun removeConnectionEventHandler(handler: ConnectionEventHandler)
requestfun <I : Inbound, O : Outbound> request(messageWorkflow: DataWorkflow<I, O>, timeout: Int = configuration.reqRepTimeout,): Single<O>
requestsuspend fun <I : Any, O : Any> request(messageWorkflow: RequestReplyDataWorkflow<I, O>,timeout: Int = configuration.reqRepTimeout,): Reply<O>
requestfun <I : Inbound, O : Outbound> request(message: I, output: Class<O>, timeout: Int = configuration.reqRepTimeout,): Single<O>
requestsuspend inline fun <reified O : Outbound> request(message: Inbound, messageType: String, timeout: Int = configuration.reqRepTimeout,): O
requestParametricsuspend inline fun <reified O, reified P : Any> requestParametric(message: Inbound, messageType: String, timeout: Int = configuration.reqRepTimeout,):
sendMessagefun sendMessage(set: GenesisSet): Boolean
sendMessagefun sendMessage(set: GenesisMessage): Boolean
sendMessagesfun sendMessages(sets: List<GenesisSet>)
sendReqRepfun sendReqRep(set: GenesisSet, consumer: Consumer<GenesisSet>)
sendReqRepfun sendReqRep(set: GenesisSet, consumer: Consumer<GenesisSet>, reqRepTimeout: Int,)
sendReqRepfun sendReqRep(set: GenesisSet): ListenableFuture<GenesisSet>
sendReqRepfun sendReqRep(set: GenesisSet, reqRepTimeout: Int,): ListenableFuture<GenesisSet>
shutdown@Throws(InterruptedException::class) fun shutdown()
suspendRequestsuspend inline fun <reified I : Inbound, reified O : Outbound> suspendRequest(message: I, timeout: Int = configuration.reqRepTimeout,): O?
suspendRequestsuspend inline fun <reified I : Inbound, reified O : Outbound> suspendRequest(messageWorkflow: DataWorkflow<I, O>, timeout: Int = configuration.reqRepTimeout,): O?

Properties

NameSummary
handlerGives GenesisMessageHandler, which allows us to attach listeners to servers and clients
isActiveChecks whether netty connector is open for new connection
isConnectedChecks whether netty connector is open for new connection

Example:

class TestAuthManagerService(@Inject val clientConnectionManager: ClientConnectionsManager) {  
private val genesisMessageClient = clientConnectionManager.getGenesisMessageClient("GENESIS_AUTH_MANAGER")
fun sendMessageToEventHandler() {
genesisMessageClient?.waitForConnection()
genesisMessageClient?.sendReqRep(
genesisSet {
MESSAGE_TYPE with "EVENT_LOGIN_AUTH"
SERVICE_NAME with "GENESIS_AUTH_MANAGER"
SOURCE_REF with "sourceRef"
DETAILS with genesisSet {
USER_NAME with "User"
PASSWORD with "Password"
}
}
)?.get()
genesisMessageClient?.shutdown()
}
}

GenesisMessageHandler

GenesisMessageHandler enables you to attach listeners to servers and clients.

Functions

NameSignature
addListenerfun addListener(listener: GenesisMessageListener<V>)
removeListenerfun removeListener(listener: GenesisMessageListener<V>)

GenesisMessageListener

GenesisMessageListener is a functional interface with method onNewMessage, which listens for any new messages. @FunctionalInterface public interface GenesisMessageListener<V extends GenesisMessage>

onNewMessage

This method is called when a new message is received.

Example:

class CreateListener(@Inject val clientConnectionManager: ClientConnectionsManager) {    
private val genesisMessageClient = clientConnectionManager.getGenesisMessageClient("GENESIS_AUTH_MANAGER")
fun listener(args: Array<String>) {
// Add listener which prints GenesisSet
genesisMessageClient?.handler?.addListener {
set: GenesisSet?, channel: GenesisChannel? -> println(set)
}
// Listener gets called when new message is received
genesisMessageClient?.sendMessage(genesisSet {
MESSAGE_TYPE with "EVENT_LOGIN_AUTH"
SERVICE_NAME with "GENESIS_AUTH_MANAGER"
SOURCE_REF with "sourceRef"
DETAILS with genesisSet {
USER_NAME with "User"
PASSWORD with "Password"
}
})
}
}