Service topic data
Service topic data
Functional topic data that provides an Asynchronous Request/Response service
This is a special form of topic data that provides a request/response service framework.
It is a common requirement for clients to be able to send some form of command through a topic to a publisher, which performs some processing and optionally return some form of response on the same topic.
Service topic data provides the following functionality to support such a service paradigm:
- Adding service topic data to a topic makes that topic a service (or command based) topic where all inbound messages from the client are routed directly to the topic data instance for execution rather than to the publisher.
- A user written service handler is specified to perform the processing for a topic and this are invoked for every command message received on that topic.
- The handler can return a response immediately (synchronous processing) or return no response but delegate the return of the response to some later process (asynchronous processing).
- Subscription actions (sending of topic load) happens automatically, so no handling needed in Publisher.subscription() method.
- Automatic timeout mechanism for asynchronous requests.
- Built in error reporting mechanism.
Creating service topic data
Service topic data is created as follows:
ServiceTopicData topicData = TopicDataFactory.newServiceData("Service1", new MyServiceHandler());
Configuring service topic data
Service topic data does not have state to be initialized as other topics do but there are some options to configure the behavior of the topic data before it is added to the topic:
Service data
Even though service topic data does not have data state there is the option to specify some static data for the topic which is sent to the client upon subscription (in the topic load message). The data is specified in the form of a Message which can contain headers and/or data.
The following example shows some service data being specified:
TopicMessage serviceData = createDeltaMessage("Service1"); serviceData.putFields("A","B","C"); topicData.setServiceData(serviceData);
Target topic Name
The ServiceRequest object passed to the ServiceHandler contains a message containing the data from the request passed from the client. By default, this message has its topic name set to that of the topic that owns the topic data. However, this can be changed if required, for example if the request is to be passed to another process that requires a different topic name. The setTargetTopicName method can be used to specify a different topic name.
When only synchronous processing is in use there is probably no point in changing the topic name in the message.
Header Options
The ServiceRequest object passed to the ServiceHandler contains a message containing the headers from the request passed from the client but might also have other headers automatically included. This is done using the setHeaderOptions method which allows a list of header types to be specified. Any headers specified in this list are included in the message within the request before any user headers passed from the client.
Such headers are required only for asynchronous processing as all of the specified information is available in the ServiceRequest object anyway when processing synchronously. The purpose of adding such headers is only for the sake of a process to which the request message can be sent.
The following header options are available:
- SERVICE_TYPE
- The value of the service type specified when the topic data was created.
- REQUEST_TYPE
- The request type is passed with the request from the client. This is needed to differentiate between different types of requests. However, if there is only ever one type of request, request type is not required.
- CLIENT_ID
- The client identifier of the client that sent the request.
- REQUEST_ID
- The request identifier sent from the client with the request which uniquely identifies the request for the client.
By default no additional headers are added.
When asynchronously processed the topic data only waits for a certain amount of time before timing out the request and automatically sending a timeout error to the Client. By default this value is set to 5 seconds but can be changed using the setRequestTimeout method.
Writing a service handler
How to write the handler for a service topic
The key element of a service topic is the user written service handler which performs the actual processing. The service topic data itself is merely a framework within which to execute service handlers.
Service requests are sent from the client and routed through the topic data to an instance of the service handler as specified to the topic data.
A service handler must implement the ServiceHandler interface.
Requests are formatted into ServiceRequest objects and passed to the Service handler on its serviceRequest method. The ServiceRequest has information like the client details, a unique request identifier (from the client), a request type and request data in the form of a message. The request data comes from the message received from the client and can comprise user headers and/or data. It is also permissible to have no request data, just a request type.
The service handler can process the request and immediately return a ServiceResponse object encapsulating the details of the response to send back to the client. This is synchronous processing. The handler might also choose to delegate the processing to some other process which will asynchronously return the response at some later point using the serviceResponse method on the topic data.
For synchronous processing an error can be reported by throwing a ServiceException from the handler. This is formatted and returned to the client. For asynchronous processing a callback to the serviceError method on the topic data might be used to report a failure in processing.
Client handling of service topic data
How a client handles a service topic
At the client end the client application must be able to handle the service topic Protocol. Most client APIs provide the capability for handling such topics transparently. This section shows how it is handled in the Java™ client API.
Handling a topic load
A client receives messages on its listener methods and can detect a load message from a service topic by means of the isServiceLoad() method. On receipt of such a load message the client application must create a ServiceTopicHandler to handle the topic. This handler provides the facility to send requests to the topic and also routes responses and errors from the topic to a specified ServiceTopicListener. Such a handler can be created using the ExternalClientConnection.createServiceTopicHandler method.
The following code sample shows how to create a suitable topic handler on receipt of a service load message:
public void messageFromServer(ServerConnection serverConnection, TopicMessage message) { if (message.isServiceLoad()) { try { theHandler=theConnection.createServiceTopicHandler(message,this); } catch (Exception ex) { ex.printStackTrace(); } } }
The returned handler is of type ServiceTopicHandler and the above example assumes the calling class implements ServiceTopicListener and process service responses and errors.
Having created such a handler no further messages are received for that topic on the messageFromServer method as they are all consumed by the handler.
Service type and service data
After creating the handler the service type and any service data sent from the server can be obtained from it. The service type identifies the service to the server, and can be used by the client to differentiate between different types of service. The server might also have returned service data which can be used by the service to return any information that might be required by the client. The way in which service type and service data is used is entirely up to the service implementation at the server.
Sending Requests
A service request can be sent to the server through the handler using the request method. A request must specify a request type which must be a request type understood by the service. It can also optionally specify a message containing headers and/or data which is sent with the request. The message can be used to provide parameters to the service request.
The following example shows the simplest case where no parameters are required to the request:
String requestId = theHandler.request("GetAccounts",null);
The method returns a unique request identifier which can be used to correlate the response (or any error) returned from the service with the request.
The following example shows a message being used to pass a parameter-
TopicMessage message = theConnection.createDeltaMessage("XYZ",50); message.put("12435"); String requestId = theHandler.request("GetAccountDetails",message);
The topic name specified for the message is not important as it is replaced by the handler in the actual request that is sent.
Handling Responses
Responses to requests are returned using the serviceResponse method on the ServiceTopicListener specified when creating the service Handler. The serviceResponse method passes a ServiceTopicResponse object from which can be obtained the following:
requestId | This is the request identifier that was returned when the request was originally sent. |
responseType | This is a response type as sent by the service and is used to allow the client to differentiate between different possible responses. |
responseMessage | This is a message containing any headers and/or data returned by the service. This can be an empty message if the service did not return any data. |
Handling Errors
If the service request fails in any way at the server, an error is returned through the serviceError method on the ServiceTopicListener specified when creating the service handler. The serviceError method passes a ServiceTopicError object from which can be obtained the following:
requestId | This is the request identifier that was returned when the request was originally sent. |
errorType | This is an enum with one of the following possible values: |
errorMessage | This is the error message associated with the error. This will always be present. |
exceptionMessage | This is an optional exception message which can be returned if the error was due to an exception. This can be null. |
additionalDetails | This can return optional additional data associated with an exception. This can be null. |
SERVICE | An error has occurred whilst executing the service |
INVALID | The service request was invalid |
TIMEOUT | The request was executed asynchronously at the server but was timed out before a response was returned. |
USER | An error was returned by the user written service handler. |
DUPLICATE | A duplicate request identifier was sent. This cannot happen when using the Java API interface but is present for completeness. |
Unsubscribing
When the client application unsubscribes from the service topic then the handler will become unusable and any outstanding requests for which responses have not been returned are discarded.