Creating Custom Protocols
Overview
The External Service Protocol (ESP) was developed to simplify creation of custom protocols. It contains a minimal set of messages for exchanging information between a client and server. All messages contain a reference field to correlate the response with the request. The payload of messages is contained in key-value structures which are used as message properties. Because key-value collections can be used recursively, the total number of properties depends on the message. The custom protocol implements binary transport and obeys common rules for Genesys protocols.
Set of Messages
Message | Description |
---|---|
Class Request3rdServer | The Request3rdServer class holds requests for your custom server. This class extends the Message class and adds three additional fields:
|
Class Event3rdServerResponse | The Event3rdServerResponse class is used to send a response to clients. This class extends the Message class and adds three additional fields:
|
Class Event3rdServerFault | The Event3rdServerFault class is sent to clients if the request cannot be processed for some reason. This class extends the Message class and adds two additional fields:
|
Using ESP on the Client Side
Creating a Custom Protocol
To create the simplest ESP-based protocol, all you need to do is create a class inherited from the ExternalServiceProtocol class. However, this protocol only provides a way to send data. Your custom protocol still has to handle incoming and outgoing messages.
For example:
public class MessageProcessor {
private static final String PROTOCOL_DESCRIPTION = "CustomESPProtocol";
/**
* Processes message which is has to be sent.
* @param msg message to be sent.
* @param clientSide flag indicates that the message is processing on a client side
* @return message instance if message was processed successfully otherwise null.
*/
static Message processSendingMessage(String msg, boolean clientSide)
{
Message outMsg = clientSide ? Request3rdServer.create() : Event3rdServerResponse.create();
KeyValueCollection request = new KeyValueCollection();
request.addString("Protocol", PROTOCOL_DESCRIPTION);
request.addString("Request", msg);
if (outMsg instanceof Request3rdServer) {
((Request3rdServer)outMsg).setRequest(request);
}
else if (outMsg instanceof Event3rdServerResponse) {
((Event3rdServerResponse)outMsg).setRequest(request);
}
return outMsg;
}
/**
* Handles incoming message.
* @param message Incoming message.
* @return Message instance if message was processed successfully otherwise null
*/
static String processEvent(Message message)
{
KeyValueCollection request = null;
if (message instanceof Event3rdServerResponse) {
request = ((Event3rdServerResponse)message).getRequest();
}
else if (message instanceof Request3rdServer) {
request = ((Request3rdServer)message).getRequest();
}
if (request == null) {
return null;
}
String requestString = request.getString("Request");
if (requestString == null) {
return null;
}
String protocolDescr = request.getString("Protocol");
return PROTOCOL_DESCRIPTION.equals(protocolDescr) ? requestString : null;
}
}
public class EspExtension extends ExternalServiceProtocol {
private static ILogger log = Log.getLogger(EspExtension.class);
public EspExtension(Endpoint endpoint) {
super(endpoint);
}
public String request(String message) throws ProtocolException
{
Message newMessage = MessageProcessor.processSendingMessage(message, true);
if (newMessage != null) {
return MessageProcessor.processEvent(request(newMessage));
}
if (log.isDebug()) {
log.debugFormat("Cannot send message: '{0}'", message);
}
return null;
}
}
Using ESP on the Server Side
Class ExternalServiceProtocolListener
The ExternalServiceProtocolListener class provides server-side functionality based on the Platform SDK ServerChannel class, and implements External Service Protocol. The simplest server side logic is shown in the following example:
public class EspServer {
private static final ILogger log = Log.getLogger(EspServer.class);
private final ExternalServiceProtocolListener listener;
public EspServer(Endpoint settings)
{
listener = new ExternalServiceProtocolListener(settings);
listener.setClientRequestHandler(new ClientRequestHandler() {
@Override
public void processRequest(RequestContext context) {
try {
EspServer.this.processRequest(context);
} catch (ProtocolException e) {
if (log.isError()) {
log.error("Message processing error:\n" + context.getRequestMessage(), e);
}
}
}
});
}
public ExternalServiceProtocolListener getServer() {
return listener;
}
private void processRequest(RequestContext context) throws ProtocolException {
//TODO: Return to client reversed source request
Message requestMessage = context.getRequestMessage();
if (requestMessage == null) {
return;
}
if (log.isDebug()) {
log.debugFormat("Request: {0}", requestMessage);
}
String msg = MessageProcessor.processEvent(requestMessage);
if (msg != null)
{
String reversedMsg = new StringBuilder(msg).reverse().toString();
Message outMsg = MessageProcessor.processSendingMessage( reversedMsg, false);
if (outMsg instanceof Referenceable
&& requestMessage instanceof Referenceable) {
((Referenceable)outMsg).updateReference(((Referenceable)requestMessage).retreiveReference());
}
if (log.isDebug()) {
log.debugFormat("Request: {0}", requestMessage);
}
context.respond(outMsg); // or context.getClientChannel().send(outMsg);
}
}
}
Testing Your Protocol
A simple test example is shown below:
public class TestEsp {
final String REQUEST = "Hello world!!!";
@Test
public void testMirrorSerializedMessage() throws ChannelNotClosedException, ProtocolException, InterruptedException
{
String response = null;
ExternalServiceProtocolListener server = new EspServer(new WildcardEndpoint(0)).getServer();
server.open();
InetSocketAddress ep = server.getLocalEndPoint();
if (ep != null) {
EspExtension client = new EspExtension(new Endpoint("localhost", ep.getPort()));
client.open();
response = client.request(REQUEST);
client.close();
}
server.close();
System.out.println("Request: \n" + REQUEST);
System.out.println("Response: \n" + response);
Assert.assertNotNull(response);
String expected = new StringBuilder(REQUEST).reverse().toString();
Assert.assertEquals(response, expected);
}
}
Overview
The External Service Protocol (ESP) was developed to simplify creation of custom protocols. It contains a minimal set of messages for exchanging information between a client and server. All messages contain a reference field to correlate the response with the request. The payload of messages is contained in key-value structures which are used as message properties. Because key-value collections can be used recursively, the total number of properties depends on the message. The custom protocol implements binary transport and obeys common rules for Genesys protocols.
Set of Messages
Message | Description |
---|---|
Class Request3rdServer | The Request3rdServer class serves for request the server. This class contains the base set of properties inherent to the IMessage interface and three additional fields.
|
Class Event3rdServerResponse | The Event3rdServerResponse class is used to send a response to clients. This class contains the base set of properties inherent to the IMessage interface and three additional fields:
|
Class Event3rdServerFault | The Event3rdServerFault class is sent to clients if the request cannot be processed for some reasons. This class contains the base set of properties inherent to the IMessage interface and two additional fields:
|
Using ESP on the Client Side
Creating a Custom Protocol
To create the simplest ESP-based protocol, all you need to do is create a class inherited from the ExternalServiceProtocol class. However, this protocol only provides a way to send data. Your custom protocol still has to handle incoming and outgoing messages.
For example:
internal static class MessageProcessor
{
private const string ProtocolDescriptionStr = "CustomESPProtocol";
/// <summary>
/// Processes message which is has to be sent.
/// </summary>
/// <param name="msg">message to be sent</param>
/// <param name="clientSide">flag indicates that message is processing on client side</param>
/// <returns>IMessage instance if message was processed successfuly otherwise null</returns>
internal static IMessage ProcessSendingMessage(string msg, bool clientSide)
{
IMessage outMsg = clientSide ? Request3rdServer.Create() : Event3rdServerResponse.Create() as IMessage;
var request = new KeyValueCollection {{"Protocol", ProtocolDescriptionStr}, {"Request", msg}};
var req = outMsg as Request3rdServer;
if (req != null) req.Request = request;
var evt = outMsg as Event3rdServerResponse;
if (evt != null) evt.Request = request;
return outMsg;
}
/// <summary>
/// Handles incoming message
/// </summary>
/// <param name="message">Incoming message</param>
/// <returns>IMessage instance if message was processed successfuly otherwise null</returns>
internal static string ProcessEvent(IMessage message)
{
var response = message as Event3rdServerResponse;
var req = message as Request3rdServer;
KeyValueCollection request = null;
if ((response != null) && (response.Request != null))
{
request = response.Request;
}
else
{
if ((req != null) && (req.Request != null))
request = req.Request;
}
if (request == null) return null;
var requestStr = request["Request"] as String;
if (String.IsNullOrEmpty(requestStr)) return null;
var protocolDescr = request["Protocol"] as string;
return (ProtocolDescriptionStr.Equals(protocolDescr))?requestStr:null;
}
}
public class EspExtension : ExternalServiceProtocol
{
public EspExtension(Endpoint endPoint) : base(endPoint) { }
public string Request(string message)
{
var newMessage = MessageProcessor.ProcessSendingMessage(message, true);
if (newMessage != null)
{
return MessageProcessor.ProcessEvent(Request(newMessage));
}
if ((Logger != null) && (Logger.IsDebugEnabled))
Logger.DebugFormat("Cannot send message: '{0}'", message);
return null;
}
}
Using ESP on the Server Side
Class ExternalServiceProtocolListener
The ExternalServiceProtocolListener class provides server-side functionality based on the Platform SDK ServerChannel class, and implements External Service Protocol. The simplest server side logic is shown in the following example:
public class EspServer
{
private readonly ExternalServiceProtocolListener _listener;
public EspServer(Endpoint settings)
{
_listener = new ExternalServiceProtocolListener(settings);
_listener.Received += ListenerOnReceived;
}
public ExternalServiceProtocolListener Server { get { return _listener; } }
private void ListenerOnReceived(object sender, EventArgs eventArgs)
{
//TODO: Return to client source request
var duplexChannel = sender as DuplexChannel;
var args = eventArgs as MessageEventArgs;
if ((duplexChannel == null) || (args == null)) return;
Console.WriteLine(args.Message);
var msg = MessageProcessor.ProcessEvent(args.Message);
if (msg != null)
{
var outMsg = MessageProcessor.ProcessSendingMessage(new string(msg.Reverse().ToArray()), false);
var source = args.Message as IReferenceable;
var dest = outMsg as IReferenceable;
if ((source!=null) && (dest!=null))
dest.UpdateReference(source.RetrieveReference());
Console.WriteLine(outMsg);
duplexChannel.Send(outMsg);
}
}
}
Testing Your Protocol
A simple test example is shown below:
[TestClass]
public class TestEsp
{
[TestMethod]
public void TestMirrorSerializedMessage()
{
const string request = "Hello world!!!";
string response = null;
var server = new EspServer(new WildcardEndpoint(0)).Server;
server.Open();
var ep = server.LocalEndPoint as IPEndPoint;
if (ep != null)
{
var client = new EspExtension(new Endpoint("localhost", (server.LocalEndPoint as IPEndPoint).Port));
client.Open();
response = client.Request(request);
client.Close();
}
server.Close();
Console.WriteLine("Request: \n{0}", request);
Console.WriteLine("Response: \n{0}", response);
Assert.IsNotNull(response);
Assert.IsTrue(request.Equals(new string(response.Reverse().ToArray()))); }
}