Writing gRPC Based Multiple Event Handlers using the WSO2 Identity Server Eventing Framework

Nuwanga Herath
6 min readJan 18, 2021

Hello everyone! Hope you all have followed my blog series on the implementation of gRPC based Event Handler using WSO2 Identity Server Eventing Framework. If not, please go through those articles to not get confused about this blog. You can find my blog series from here.

If you are already familiar with gRPC based Handler, you might know what the purpose of implementing gRPC based event handler and what it does and how it works. Anyway, let me give you a brief introduction about gRPC based Event Handler.

What is an Event Handler?

  • An event handler is used to perform an operation based on the published events. In another word, the event handler does operations upon a triggered event.

Why gRPC based Event Handler?

  • A gRPC Based Event Handler enables users to implement platform-independent event handlers to WSO2 Identity Server. The idea is that user can implement methods to perform operations which are being triggered upon particular events, on a remote server written in any gRPC supported language like Java, Python, Go, Ruby, C++, C# and Node.JS. Since the handling part is done on the remote server, it can be considered as a remote event handler.

Now you have a better idea about the gRPC based Event Handler. So let’s move into our main topic that you are going to follow in this blog.

gRPC based Multiple Event Handlers

  • I’m pretty sure that you are confusing with the word Multiple here. Let’s clear it out. As far as you follow my previous blogs and WSO2 Identity server official documentation on Custom Event Handlers, you may know that implemented gRPC based event handler is placed in the WSO2 Identity server as OSGi component. Inside the event handler component, it called the methods on a remote server using gRPC stub. In that scenario, the event handler component communicates with only one remote server at one time according to the configurations that we added to the identity-event.properties file.
  • The key feature which differentiates the gRPC based multiple event handlers from the gRPC based event handler is the ability to connect with more than one remote server using only one event handler component in the identity server. That means the multiple event handlers can perform operations upon a triggered event on multiple remote servers. Use of gRPC gives the ability to implement multiple event handlers only by configuring the identity-event.properties file with server configurations of the servers where event handler operations are implemented.

High-Level Architecture

  • Let’s have a look at the high-level architecture of the gRPC based Multiple Event Handler.

How to implement?

Here you will not need to start the implementation from the beginning if you have already implemented the gRPC based event handler. If not, I request you to follow my blog on Writing a gRPC Based Event Handler using the WSO2 Identity Server Eventing Framework and give a try to implement a gRPC based event handler.

  • Keep in mind that there are two main classes named GrpcEventHandlerand GrpcEventHandlerComponent addition to the gRPC data access classes. To implement the gRPC based multiple event handlers, some changes are needed to be added to those classes.

Step 1: Configuring the Multiple Event Handlers

Like we did in the configuring of the gRPC based Event Handler, here also you have to configure the Multiple Event Handlers on the WSO2 Identity Server.

  • To do that, The multiple event handler configurations can be added as follows to {wso2is-home}/repository/conf/deployment.toml file. The events which need to subscribe to a particular handler can be listed in subscriptions.
  • Similarly, host ,port and certPath for each handler have to add accordingly.
  • Addition to that, all the gRPC based event handlers which refers as multiple event handlers have to listed in grpc handlers.
  • In this blog, I use two event handlers named grpcBasedEventHandlerjava and grpcBasedEventHandlerPython as examples.
  • Here is the way to add the configurations.
[[grpc]]
handlers=["grpcBasedEventHandlerJava","grpcBasedEventHandlerPython"]
[[event_handler]]
name="grpcBasedEventHandlerJava"
subscriptions=["POST_ADD_USER"]
enable=true
properties.priority="57"
properties.host="localhost"
properties.port="8020"
properties.certPath="/home/nuwanga/wso2/custom-event-handler/src/main/java/org/wso2/grpc/event/handler/cert1/ca-cert.pem"
[[event_handler]]
name="grpcBasedEventHandlerPython"
subscriptions=["PRE_ADD_USER"]
properties.priority="58"
properties.host="localhost"
properties.port="8010"
properties.certPath="/home/nuwanga/wso2/custom-event-handler/src/main/java/org/wso2/grpc/event/handler/cert1/ca-cert.pem"

Step 2: Obtaining gRPC based Event Handler names from identity-event.properties

We have to obtain gRPC based Event Handler names from the configurations that we previously added to the identity-event.properties of the WSO2 Identity Server.

  • Here we defined a method named populateHandlerNames() inside the GrpcEventHandlerComponent class to obtain the handler names from identity event configurations.
public void populateHandlerNames() {

// Obtain gRPC based handler names from identity-event properties.
try {
this.grpcEventHandlerNames = IdentityEventConfigBuilder.getInstance()
.getModuleConfigurations("grpcHandler").getModuleProperties().values().iterator();
if (log.isDebugEnabled()) {
StringBuilder stringBuilder = new StringBuilder();
while (grpcEventHandlerNames.hasNext()) {
stringBuilder.append(grpcEventHandlerNames.next() + "|");
}
log.debug("gRPC Handler names : " + stringBuilder.toString());
}
} catch (IdentityEventException e) {
log.error("Error occurred while reading Identity Event properties for gRPC handler names.", e);
}
}

Step 3: Obtaining handlers properties and populate GrpcBasedHandlerProperties objects

  • The GrpcBasedHandlerProperties class has fields for event handler properties and getters for every field.
public class GrpcBasedHandlerProperties {

private String handlerName;
private int priority;
private String host;
private int port;
private String certPath;

public GrpcBasedHandlerProperties(String handlerName, String priority, String host, String port, String certPath) {

this.handlerName = handlerName;
this.priority = Integer.parseInt(priority);
this.host = host;
this.port = Integer.parseInt(port);
this.certPath = certPath;

}

public String getHandlerName() {

return this.handlerName;
}

public int getPriority() {

return this.priority;
}

public String getHost() {

return this.host;
}

public int getPort() {

return this.port;
}

public String getCertPath() {

return this.certPath;
}

}
  • Inside the GrpcEventHandlerComponent class, we define a method named populateHandlerConfigs() to obtain handler properties from configs and populate GrpcBasedHandlerProperties objects using obtained properties. Then the populated objects are stored for future implementations.
public void populateHandlerConfigs() {

// Obtain gRPC based handler configurations from identity-event properties.
while (grpcEventHandlerNames.hasNext()) {
String handlerName = String.valueOf(grpcEventHandlerNames.next());
ModuleConfiguration handlerConfiguration = null;

try {
handlerConfiguration = IdentityEventConfigBuilder.getInstance()
.getModuleConfigurations(handlerName);
} catch (IdentityEventException e) {
log.error("Error occurred while reading Identity Event properties for gRPC handler configurations."
, e);
}
String priority = handlerConfiguration.getModuleProperties()
.getProperty(handlerName + ".priority");
String host = handlerConfiguration.getModuleProperties().getProperty(handlerName + ".host");
String port = handlerConfiguration.getModuleProperties().getProperty(handlerName + ".port");
String certPath = handlerConfiguration.getModuleProperties().getProperty(handlerName + ".certPath");

// Add gRPC based handler properties to handlerConfigs.
GrpcBasedHandlerProperties grpcBasedHandlerProperties = new GrpcBasedHandlerProperties(handlerName,
priority,
host,
port,
certPath);
this.handlerConfigs.add(grpcBasedHandlerProperties);
}
}

Step 4: Activating multiple Event Handler instances

We added changes to the existing Activate() method of the GrpcEventHandlerComponent class to activate multiple event handler instances. Multiple event handler instances are created using the populated GrpcBasedHandlerProperties objects.

@Activate
protected void activate(ComponentContext context) {

this.populateHandlerNames();
this.populateHandlerConfigs();

// Create multiple gRPC based event handler instances.
Iterator<GrpcBasedHandlerProperties> handlerConfigsArray = this.handlerConfigs.listIterator();
while (handlerConfigsArray.hasNext()) {
GrpcBasedHandlerProperties grpcBasedHandlerProperties = handlerConfigsArray.next();
GrpcEventHandler eventHandler = new GrpcEventHandler();
eventHandler.init(grpcBasedHandlerProperties.getHandlerName(),
grpcBasedHandlerProperties.getPriority(),
grpcBasedHandlerProperties.getHost(),
grpcBasedHandlerProperties.getPort(),
grpcBasedHandlerProperties.getCertPath());

// Register the gRPC based event handlers as an OSGI service.
context.getBundleContext().registerService(
AbstractEventHandler.class.getName(), eventHandler, null);
log.info(grpcBasedHandlerProperties.getHandlerName() + " is activated successfully.");
}

}
  • Not as in the gRPC based Event Handler implementation, here we obtain handler configs from the GrpcEventHandlerComponent class. Because of that, we have to introduce a new method to initialize the attributes of GrpcEventHandlerComponent class.
private String grpcServerHost;
private int grpcServerPort;
private String handlerName;
private int priority;
private String certPath;
private ManagedChannel channel;
  • Here is the init method which is defined inside the GrpcEventHandler class.
public void init(String handlerName, int priority, String host, int port, String certPath) {

this.handlerName = handlerName;
this.priority = priority;
this.grpcServerHost = host;
this.grpcServerPort = port;
this.certPath = certPath;

// Obtains the certificate file.
File clientCACertFile = new File(certPath);

// Create the channel for gRPC server with server authentication SSL/TLS.
try {
this.channel = NettyChannelBuilder.forAddress(grpcServerHost, grpcServerPort)
.sslContext(GrpcSslContexts.forClient().trustManager(clientCACertFile).build())
.build();
} catch (SSLException e) {
log.error("Error occurred while verifying the SSL certificate : ", e);
}

// Create the gRPC client stub.
this.clientStub = serviceGrpc.newBlockingStub(channel);
}

That’s all about the extending of the gRPC based Event Handler to have the functionality of working with gRPC based Multiple Event Handlers. For the testing purposes, I have implemented gRPC servers in Python and in Java. You can find them from,

Here also I attache the full code segments of the classes,

  • GrpcBasedHandlerProperties
  • GrpcEventHandler
  • GrpcEventHandlerComponent

Finally we have reached the end of this tutorial. Hopefully, you got an advance knowledge on gRPC based Event Handler and on gRPC based Multiple Event Handlers.

  • If you have faced any problem, put it in the comment section.

Refer following links for the source codes.

Thank You!

--

--

Nuwanga Herath

Former Software Engineering Intern @ WSO2, Computer Science and Engineering Undergraduate @ University of Moratuwa, Sri Lanka