Java Event Handlers
Event Handlers can be written in Java using Event Handler APIs. On this page, we look at Event Handlers written using the Rx3 Event handlers
To work with a Java Event Handler:
- In your application-eventhandler/src/main/ folder, create an empty folder called java. This ensures that the Java file will be compiled.
- In your application-eventhandler folder, create a package for your Java Event Handler. Make sure that your package name is specific - this will be used to start the Event Handler process.
- You can then create the file for the Java Event Handler in this folder.
- Update your-application_processes.xml file to include the details of the new module. For example:
<process name="JAVA_EVENT_HANDLER">
<groupId>MYAPP</groupId>
<start>true</start>
<options>-Xmx256m -DRedirectStreamsToLog=true -DXSD_VALIDATE=false</options>
<module>myapp-eventhandler</module>
<package>global.genesis.eventhandler,genesis.global.trades</package>
<description>Handles events</description>
</process>
A simple example of a Java Event Handler
This method passes the input message type CounterParty
as a parameter and expects the output message type EventReply
to be returned.
The default name will be EVENT_<input message type name>
. So, for an input message type declared as CounterParty
, the event with the name EVENT_COUNTERPARTY
is registered automatically.
@Module
public class EventCounterParty implements Rx3EventHandler<Counterparty, EventReply> {
private final RxEntityDb entityDb;
@Inject
public EventCounterParty(RxEntityDb entityDb) {
this.entityDb = entityDb;
}
@Override
public Single<EventReply> process(Event<Counterparty> counterpartyEvent) {
Counterparty counterparty = counterpartyEvent.getDetails();
return entityDb.insert(counterparty).flatMap(result -> ack(this));
}
}
Any Java Event Handler classes you create must be placed in the same folder as the Java Event Handler module itself.
Adding a name
Every eventHandler
must have a unique name. If you do not provide one, it will be allocated a default name automatically, as shown in the previous example.
It is good practice to provide your own name for each eventHandler
. For example, if you have insert and modify codeblocks for the same table and you don't name them, then the platform will probably generate identical names for both - which will give you a problem.
Note that the prefix EVENT_
is automatically added to the name that you specify.
So, below, we modify our previous example by defining the name of the eventHandler
: COUNTERPARTY_INSERT by overriding messageType method of the Rx3EventHandler. This will automatically become EVENT_COUNTERPARTY_INSERT.
@Module
public class EventCounterParty implements Rx3EventHandler<Counterparty, EventReply> {
private final RxEntityDb entityDb;
@Inject
public EventCounterParty(RxEntityDb entityDb) {
this.entityDb = entityDb;
}
@Nullable
@Override
public String messageType() {
return "COUNTERPARTY_INSERT";
}
@Override
public Single<EventReply> process(Event<Counterparty> counterpartyEvent) {
Counterparty counterparty = counterpartyEvent.getDetails();
return entityDb.insert(counterparty).flatMap(result -> ack(this));
}
}
Adding validation
So far, we have overridden the process method in our eventHandler
. This is where the active instructions are - usually database changes.
If you want to provide some validation before the action, you need to implement the Rx3ValidatingEventHandler
interface and override onCommit
and onValidate
methods.
in the example below, the onValidate
method will be executed first and the onCommit
method will only be executed if the counterparty
field is not null.
@Module
public class EventCounterParty implements Rx3ValidatingEventHandler<Counterparty, EventReply> {
private final RxEntityDb entityDb;
@Inject
public EventCounterParty(RxEntityDb entityDb) {
this.entityDb = entityDb;
}
@Nullable
@Override
public String messageType() {
return "COUNTERPARTY_INSERT";
}
@NotNull
@Override
public Single<EventReply> onCommit(@NotNull Event<Counterparty> event) {
Counterparty counterparty = event.getDetails();
return entityDb.insert(counterparty) .flatMap(result -> ack(this));
}
@NotNull
@Override
public Single<EventReply> onValidate(@NotNull Event<Counterparty> event) {
Counterparty counterparty = event.getDetails();
if(counterparty.getName().isEmpty()) {
return nack(this, "Counterparty should have a name");
}
return ack(this);
}
}
Returning a nack
The onValidate
method must always return either an ack()
or nack(...)
.
Look at the onValidate
method in the previous example:
- if the counterparty field is empty, the
eventHandler
returns anack
, along with a suitable message. - if the counterparty field has content, then the
eventHandler
returns anack
Default reply types
So far, we have seen ack
and nack. There is a third reply type:
warningNack`. Let's stop and look at the specifications for all three default reply types:
ack
: used to signify a successful result.ack
takes an optional parameter ofList<Map<String, Any>>
. For example,ack(listOf(mapOf("TRADE_ID", "1")))
.nack
: used to signify an unsuccessful result.nack
accepts either aString
parameter or aThrowable
. For example,nack("Error!")
ornack(myThrowable)
.warningNack
: used to warn the client.warningNack
, likenack
, accepts either aString
parameter or aThrowable
. For example,warningNack("Provided User alias $userAlias will override Username $username.")
orwarningNack(myThrowable)
.
Transactional Event Handlers (ACID)
If you want your eventHandler
to comply with ACID, you need to use the RxEntityDb writeTransaction. Any exception or nack returned will result in a complete rollback of all parts of the onCommit
and onValidate
(the transaction also covers read commands) methods.
@Module
public class EventCounterParty implements Rx3EventHandler<Counterparty, EventReply> {
private final RxEntityDb entityDb;@Inject
public EventCounterParty(RxEntityDb entityDb) {
this.entityDb = entityDb;
}
@Nullable
@Override
public String messageType() {
return "COUNTERPARTY_INSERT";
}
@Override
public Single<EventReply> process(Event<Counterparty> counterpartyEvent) {
Counterparty counterparty = counterpartyEvent.getDetails();
return entityDb.writeTransaction(txn -> {
txn.insert(counterparty);
return ack(this);
}).map(result -> result.getFirst());
}
}
Context Event Handlers
In order to optimise database look-up operations, you might want to use data obtained by the onValidate
method inside your onCommit
method. To do this, implement the Rx3ContextValidatingEventHandler interface, as shown below:
@Module
public class ContextEvent implements Rx3ContextValidatingEventHandler<Company, EventReply, String> {
private final RxEntityDb entityDb;
@Inject
public ContextEvent(RxEntityDb entityDb) {
this.entityDb = entityDb;
}
@Nullable
@Override
public String messageType() {
return "CONTEXT_COMPANY_INSERT";
}
@NotNull
@Override
public Single<EventReply> onCommit(@NotNull Event<Company> event, @Nullable String context) {
String parsedContext;
parsedContext = Objects.requireNonNullElse(context, "Missing context");
Company company = event.getDetails();
return entityDb.insert(company).flatMap(result -> ack(this, List.of(Map.of("VALUE",parsedContext))));
}
@NotNull
@Override
public Single<ValidationResult<EventReply, String>> onValidate(@NotNull Event<Company> event) {
Company company = event.getDetails();
if(company.getCompanyName().equals("MY_COMPANY")) {
return ack(this).map(result -> validationResult(result, "Best company in the world"));
} else {
return ack(this).map(this::validationResult);
}
}
}
Using both Java Event Handlers and Kotlin Event Handlers
You can use both Java and Kotlin Event Handlers in the same application.
Make sure that both types of Event Handler are included in your application's processes.xml file. In the example below:
- The Kotlin Event Handler is
<module>genesis-pal-eventhandler</module>
. - The
<script>
and<language>
for the Kotlin file are registered. - The Kotlin and Java Event Handlers are both registered in the
<package>
. (In this case, the Java files are located in the package genesis.global.trades.) - The jar files for the Java Event Handler are registered in the
<classpath>
.
<process name="JAVA_KOTLIN_EVENT_HANLDERS">
<groupId>MYAPP</groupId>
<start>true</start>
<options>-Xmx256m -DRedirectStreamsToLog=true -DXSD_VALIDATE=false</options>
<module>genesis-pal-eventhandler</module>
<package>global.genesis.eventhandler.pal,genesis.global.trades</package>
<script>Myapp-eventhandler.kts</script>
<language>pal</language>
<classpath>myapp-eventhandler*.jar</classpath>
<description>Handles events</description>
</process>