Skip to content
On this page

Client Code

Writing code to enable your client code to connect to your Services (whether said client code is a system or an end user front end) involves instantiating the TSF's SuperMainExternalClientCmpnt() and using it to communicate with your Services.

SuperMainExternalClientCmpnt

This SuperMainExternalClientCmpnt also takes a .json configuration that is considerably less sophisticated than for the TSF Grid. It has the parameters listed in the Client Parameters Reference Section including those common to client and server that are mentioned here as part of that same page. Please see Client Parameters Reference.

The SuperMainExternalClientCmpnt is to be instantiated by your client code and used to communicate with and receive responses from your Services in the TSF. In addition to the configuration file that we mentioned (and is shown below), it requires a valid userName and password (which must be in the ACL) even for an insecure connection. For a secure connection, it also requires a trustStore and keyStore password to obtain the client's private and public key needed to establish the 2-way authenticated SSL connection.

Client Creation and Configuration

Overview: There are 3 files that must be created to instantiate a TSFClient (i.e., a SuperMainExternalClientCmpnt) to connect to a grid of servers managed by the TSF:
--A .properties file: Contains the userName of the secure or insecure user; the ClientConfigFileName with the path and name of the .json file with all the parameters and properties for the connection/connection's behavior; and, a TokenFileNameAndPath with the path and file name of the "token" file containing at least the password for the userName, and, for a secureConnection, additionally, the password of the end user's TrustKeyStore and KeyKeyStore.
--A .json file: This file contains the configuration parameters that control the behavior of the TSFClient and its interaction with the TSF Grid of servers. Its values are discussed below.
--A "token" file: This file holds the password for the userName in the .properties file (necessary even for an insecure connection). If this is a secure connection, this file also holds the passwords of the TrustKeyStore and KeyKeyStore.

Below is an example of a .json file (which could be named, e.g., TSFClient.json) in order to instantiate a SuperMainExternalClientCmpnt to connect to the TSF:

{
  "hostname": "localhost",
  "port": 9080,
  "SSLPort": 9081,
  "secureConnection": true,
  "sessionId": 0,
  "numOfAttemptsToReconnect": 3,
  "numMillisToWaitBetweenReconnectAttempts": 1000,
  "acceptNewSessionOnReconnect": true,
  "actionAfterFinalReconnectFails": "AttemptNewConnWithNewSession",
  "requestTimeoutInMillis": 3000,
  "numOfResponseProcessingThreads": 3,
  "autoVerifySignedMsgPkg": true,
  "sendSignedMsgPkg": true,
  "signatureAlgorithm": "SHA256withRSA",
  "sendPingRequestEveryMillis": 20000,
  "receivePingRequestEveryMillis": 50000,
  "maxReceiveQueueSize": 10000,
  "maxReceiveQueueDataSize": 10000000, 
  "maxSendQueueSize": 10000,
  "maxSendQueueDataSize": 10000000,
  "queueFullRetryIntervalInMillisForReceptionCmpnt": 2000
  "numRetryAttemptsOnQueueFullReceptionCmpnt": 3
  "keyStoreName": "src/test/resources/BulgNatlKeyKeyStore.eclipse.p12",
  "keyPairAlias": "bulgnatlfxtrading",
  "trustStoreName": "src/test/resources/BulgNatlTrustKeyStore.eclipse.p12"
}

The above .json file (e.g., TSFClient.json) must be identified in a .properties file (e.g., TSFClient.properties), where said `.properties value has 3 values as shown in this example TSFClient.properties file:

ClientConfigFileName = src/test/resourecs/TSFClient.json
UserName = HenryHalstead
TokenFileNameAndPath = src/test/resources/.client-tokens

The ClientConfigFileName identifies the full apth and name of the .json file needed to create the SuperMainExternalClientCmpnt -- an example of which is shown above and whose values are described below.

The UserName is the name of a secure or insecure user set up in the ACL of the server. Even an insecure userName will require a password that is stored in the identified TokenFileNameAndPath. If this is a userName associated with a secure connection (meaning: there is not just a password for it in the TSF's ACL, but also, said userName and password are associated with a DName of an X509Certificate belonging to and holding the public key of connecting client)

The proper way to create a client (assuming the above configuration was in a file called TSFClient.json) is as follows: (Include path name preceding TSFClient.properties if needed.)

TSFClientFactory clientFactory = TSFExternalClientFactory.getInstance();
SuperMainExternalClientCmpnt tSFClient = clientFactory.createClient("TSFClient.properties");

Notice how it is the name of the .properties file which is passed (which in turn has the path and file name of the .json file with the configuration parameters, the UserName and the token file path and name). (The token file contains the password for the UserName [and the passwords for the KeyKeyStore and TrustKeyStore if the SuperMainExternalClientCmpnt is to be used for establishing a 2=way authenticated secure connection].)

Here is an example of the token file's contents (in our example, .client-tokens) identified in the .properties file:

UserPassword=pwdForHenryHalstead224
KeyKeyStorePassword=Harry1Potter2
TrustKeyStorePassword=Hermione4567

If we were defining a token file for an insecure connection, only the UserPassword key and password would need to be provided in this file and the last 3 parameters of the .json file: "keyStoreName", "keyPairAlias", and "trustStoreName" would not need to be provided and "secureConnection" would have a value of 'false' (rather than 'true' as shown above).

EX: Client code for single synch request and response to HelloworldService

In line 7, the TSF's client component is created. Shown using the three configuration files above, a secure connection is being attempted to establish a 2-way authenticated SSL connection. During creation, this SuperMainClientCmpnt attempts to connect to the server and if it fails (whether because the user passed is not in the ACL or not there with the password passed; or, before that, if this is a secure connection, if the trust or keystore cannot be open or the 2-way authentication with server fails during the SSL handshake), an Exception is thrown.

In line 11, we send a synchronous request to the HelloService with a timeout of 10 seconds for the server to respond. If we did not pass this waitTimeout parameter (which is the same as passing 0), this would block "forever" until a response was received or until there was an exception on the Socket. Internally, when sendSyncMsg() is invoked, the request is sent asynchronously and the SuperMainClientCmpnt creates an internal object to hold the result and then synchronizes on that and waits to be notified when the response is received. When the response is received, the SuperMainClientCmpnt's processing thread will "fill" the object created with the response and synchronize on the object created and notify() the client's waiting main() thread. Which will then receive the response. So, sendSyncMsg() behaves synchronously to the invoker even though it really is sending the request to the server Service asynchronously.

java
public class HelloWoldClient {

  public static void main(String[] args) {
    
    try {
      TSFClientFactory clientFactory = TSFExternalClientFactory.getInstance();
      SuperMainExternalClientCmpnt client = clientFactory.createClient("TSFClient.properties");

      int waitTimeout = 10_000;
      String name = "John";
      SyncMsgTargetForResp response = client.sendSyncMsg(“HelloService”, name.getBytes(), waitTimeout);
      
      TSFMessage message = response.getMsgPackageReceived();
      
      String responseMsg = new String (message.getContent());
      System.out.println("Response from Server: " + responseMsg);

    }  catch (RequestTimeoutException e) {
       System.out.println(Request Timeout…");
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

EX: Client code for single asynch request and response to HelloworldService

In line 5, the TSF's client component is created.

In line 9, a callback is created and this must implement the MsgPkgCallback interface.

In line 11, we send the asynchronous message, registering the HelloServiceCallback starting at line 22.

It is likely that the client code's main() thread terminates, printing the msg at line 12 well before the callback is called by SuperMainClientCmpnt's own processing thread when the response comes back. As the SuperMainClientCmpnt does not start daemon threads that survive the termination of the thread that created these -- in this case, client's main() thread when it created the SuperMainClientCmpnt, we have put a sleep() for 15 seconds at line 14 to give the Service more than enough time to respond.

java
public class HelloWorldClient {
  public static void main(String[] args) {
    try {
      TSFClientFactory clientFactory = TSFExternalClientFactory.getInstance();
      SuperMainExternalClientCmpnt client = clientFactory.createClient("TSFClient.properties");

      String name = "John";
      // Create a callback to process Asynchronous response
      MsgPackageCallback callback = new HelloServiceCallback();
      // Sending asynchronous message
      client.sendAsyncMsg(“HelloService”, name.getBytes(), callback);
      System.out.println(This line likely executes prior to the callback which is invoked by SuperMainClientCmpnt’s thread...);
      try { Thread.sleep(1500); } catch (Exception e2) { }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
  
  static class HelloServiceCallback implements MsgPackageCallback {
    @Override
    public boolean processMsgPkg(RequestResponseHandlerCommon requestResponseHandler, TSFMessage message) {
      String responseMsg = new String (message.getContent());
      System.out.println("Response from Server: " + responseMsg);
      return true;
    }
  }
}
// NOTE: No changes in the way server responds to ASynchronous/Synchronous request.

EX: Client code for sending single synchronous request that receives a variable number of responses

Note in line 13, the sendAsyncMsg() call uses the form that passes trueto say that server is to generate a "multi-response" where it calls the form ofsendResponse()on theRequestResponseHandlerthat takes thetotalNumResponses` as its last argument.

java
public class HelloWorldClient {
  public static void main(String[] args) {
    try {
      TSFClientFactory clientFactory = TSFExternalClientFactory.getInstance();
      SuperMainExternalClientCmpnt client = clientFactory.createClient("TSFClient.properties");

      int waitTimeout = 10_000;
      String name = "John";
      // Sending synchronous message with expectations to receive one or more responses.
      boolean doesRespInclSeqNumAndTotalNumResponses = true; 
      
      SyncMsgTargetForMultiResp response = 
          client.sendSyncMsg(“HelloService”, name.getBytes(), waitTimeout,             
                             doesRespInclSeqNumAndTotalNumResponses);
      TSFMessage[] messages = response.getMsgPackagesRcvd();
      TSFMessage message;
      
      for( int responseId = 1; responseId < messages.length; responseId++) {
        message = messages[responseId];
        String responseMsg = new String (message.getContent());
        System.out.println("Response Id " + message.getResultMsgPkgSequenceNum() +
                           " out of total responses expected " + 
                           message.getNumOfResultMsgPkgs() + " from Server: " + responseMsg);
      }
    } catch (RequestTimeoutException e) {
      System.out.println(A timeout occurred before any/all results could be received…”);
    }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

EX: Client code for sending single asynchronous request that receives a variable number of responses

Note in line 12, the sendAsyncMsg() call uses the form that passes trueto say that server is to generate a "multi-response" where it calls the form ofsendResponse()on theRequestResponseHandlerthat takes thetotalNumResponses` as its last argument.

java
public class HelloWorldClient {
  public static void main(String[] args) {
    try {
      TSFClientFactory clientFactory = TSFExternalClientFactory.getInstance();
      SuperMainExternalClientCmpnt client = clientFactory.createClient("TSFClient.properties");

      String name = "John";
      // Create a callback to process Asynchronous response(s)
      MsgPackageCallback callback = new HelloServiceCallback();
      // Sending asynchronous message with expectations to receive one or more responses.
      boolean doesRespInclSeqNumAndTotalNumResponses = true; 
      client.sendAsyncMsg(“HelloService”, name.getBytes(), callback, doesRespInclSeqNumAndTotalNumResponses);
      System.out.println(This executes immediately -- possibly before any and most certainly before all responses.);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
  
  static class HelloServiceCallback implements MsgPackageCallback {
    @Override
    public boolean processMsgPkg(RequestResponseHandlerCommon requestResponseHandler, TSFMessage message) {
      // Same callback function is called for each of the multiple responses being sent.
      String responseMsg = new String (message.getContent());
      System.out.println("Response Id " + message.getResultMsgPkgSequenceNum() +
                         " out of total responses expected " + 
                         message.getNumOfResultMsgPkgs() + " from Server: " + responseMsg);
      return true;
    }
  }
}
// NOTE: No changes in the way server responds to ASynchronous/Synchronous request.

Created by Team Tessell