16.2. Making RPC requests
This section describes the technique of making RPC requests. Again, before making a request, it is desirable to check whether the remote service is online. See the section “Interface ClientEventListener” for details.
There are two overloaded methods to make an RPC call. The second overload has an extra parameter in the end:
public void call(
RemoteService remoteService,
RemoteProcedure remoteProcedure,
RPCResponseHandler responseHandler,
RequestParams requestParams)
- remoteService represents a service to which the request is addressed;
- remoteProcedure contains the name and arguments of the procedure;
- responseHandler is an implementation of the RPCResponseHandler interface that the client app provides to the method;
- requestParams is an optional parameter whose structure is shown next.
RequestParams is a unified structure that clients use to specify additional parameters for a request method. It has three fields:
public class RequestParams {
public final Object attachment;
public final int waitSeconds;
public final SequenceEncoder sessionTag;
}
- attachment contains any attached data you want to pass to the respone handler;
- waitSeconds is the wait timeout after which the request method completes with an exception of type TimeoutExpiredSoftnetException. Its default value is zero, which sets the default timeout value to 30 seconds;
- sessionTag is used to provide information about the session in the context of which the request will be made. The data size is limited to 128 bytes.
RemoteProcedure has an arguments field where the client provides arguments to call the method. It is of type SequenceEncoder, and the maximum data size is limited to 64 kilobytes. To learn the size of data in the arguments field, you can call the getSize method provided by SequenceEncoder.
When instantiating the RemoteProcedure class, your application provides the procedure’s name to the constructor. The public members of the class are shown below:
public class RemoteProcedure {
public final String name;
public final SequenceEncoder arguments;
public RemoteProcedure(String name)
// non-public members are omitted
}
And here is the RPCResponseHandler interface:
public interface RPCResponseHandler {
void onSuccess(ResponseContext context, SequenceDecoder result);
void onError(ResponseContext context, int errorCode, SequenceDecoder errorData);
void onError(ResponseContext context, SoftnetException exception);
}
The interface has three methods to implement. Each request ends with a callback to one of these methods. In case of success, the onSuccess method is called back. Here is the description of its parameters:
- context is, as usual, the context of any response handler of the platform;
- result is an object of type SequenceDecoder that contains the ASN.1 DER encoded data returned as a result by the remote procedure.
The interface has two overloaded methods, onError, to handle requests completed with an error. The first overloaded method, onError, is called back if an error is returned by the remote procedure. Here is the description of its parameters:
- context is the context of the response;
- errorCode is an error code returned by the remore procedure;
- error contains additional data that describes an error.
The second overloaded method is called back if an error is detected by the platform. It provides the error in the second parameter of type SoftnetException. Possible exceptions are listed below:
- ServiceOfflineSoftnetException – the remote service is offline;
- AccessDeniedSoftnetException – the client does not have enough permissions to call the remote procedure;
- MissingProcedureSoftnetException – the procedure name you specified when calling the RPC is incorrect;
- ServiceBusySoftnetException – the number of current procedure calls on the server has reached the maximum concurrency limit;
- TimeoutExpiredSoftnetException – RPC response timeout expired.
Let’s continue the example from the previous section and design a client that calls the “TransponseMatrix” RPC procedure. This client builds an NxM matrix from random integers, sends it to the procedure in the call arguments, and expects a transposed MxN matrix in response. It implements the RPCResponseHandler interface using an anonymous class. In the first two elements, the arguments sequence provides the size of the original matrix. The next element, as in the example in the previous section, is of type SEQUENCE OF Raw, where Raw is of type SEQUENCE OF INTEGER. The number of rows and columns is limited by the range [2, 32]. The following is the ASN.1 definition for both input and output data structures:
parameters ::= SEQUENCE {
rawsCount ::= INTEGER (2..32)
colomnsCount ::= INTEGER (2..32)
rows ::= SEQUENCE (SIZE (2..32)) OF Raw
}
Raw ::= SEQUENCE (SIZE (2..32)) OF INTEGER
And then the client code:
static void makeTransponseMatrixCall(ClientEndpoint clientEndpoint, RemoteService remoteService)
{
System.out.println("-------------------------------------------");
System.out.println("Original Matrix:");
System.out.println();
int rowsCount = 17;
int colomnsCount = 10;
Random Rnd = new Random();
int[][] matrix = new int[rowsCount][colomnsCount];
for(int i=0; i<rowsCount; i++) {
for(int j=0; j<colomnsCount; j++) {
matrix[i][j] = Rnd.nextInt();
System.out.print(matrix[i][j]);
System.out.print(" ");
}
System.out.println();
}
RemoteProcedure remoteProcedure = new RemoteProcedure("TransposeMatrix");
SequenceEncoder asnArguments = remoteProcedure.arguments;
asnArguments.Int32(rowsCount);
asnArguments.Int32(colomnsCount);
SequenceOfEncoder asnRows = asnArguments.SequenceOf(UType.Sequence);
for(int i=0; i<rowsCount; i++) {
SequenceOfEncoder asnRow = asnRows.SequenceOf(UType.Integer);
for(int j=0; j<colomnsCount; j++)
asnRow.Int32(matrix[i][j]);
}
Pair<Integer,Integer> attachment = new Pair<Integer,Integer>(rowsCount, colomnsCount);
clientEndpoint.call(remoteService, remoteProcedure, new RPCResponseHandler()
{
@SuppressWarnings("unchecked")
public void onSuccess(ResponseContext context, SequenceDecoder result)
{
try
{
int origRowsCount = ((Pair<Integer,Integer>)context.attachment).First;
int origColomnsCount = ((Pair<Integer,Integer>)context.attachment).Second;
int rowsCount = result.Int32(2,32);
int colomnsCount = result.Int32(2,32);
if(rowsCount != origColomnsCount)
throw new FormatException(String.format(
"The transponsed matrix has an invalid number of rows %d",
rowsCount));
if(colomnsCount != origRowsCount)
throw new FormatException(String.format(
"The transponsed matrix has an invalid number of colomns %d",
colomnsCount));
System.out.println();
System.out.println("------------------------------------------");
System.out.println("Transponsed Matrix:");
System.out.println();
SequenceOfDecoder asnRows = result.SequenceOf(UType.Sequence);
for(int i=0; i<rowsCount; i++) {
SequenceOfDecoder asnRow = asnRows.SequenceOf(UType.Integer);
for(int j=0; j<colomnsCount; j++) {
System.out.print(asnRow.Int32());
System.out.print(" ");
}
System.out.println();
}
}
catch(AsnException e) {
System.out.println(e.getMessage());
}
catch(FormatException e) {
System.out.println(e.getMessage());
}
}
public void onError(ResponseContext context, int errorCode, SequenceDecoder errorData) {
try
{
System.out.println("The remote call to 'TransposeMatrix' failed.");
System.out.print("Error Code: ");
System.out.println(errorCode);
System.out.print("Error message: ");
System.out.println(errorData.UTF8String());
}
catch(AsnException e) {
System.out.println(e.getMessage());
}
}
public void onError(ResponseContext context, SoftnetException exception) {
System.out.println("The remote call to 'TransposeMatrix' failed.");
System.out.print("Error message: ");
System.out.println(exception.getMessage());
}
}, attachment);
}
TABLE OF CONTENTS
- 16.1. Handling RPC requests
- 16.2. Making RPC requests