JSON-RPC 2.0 Base

  • Minimalist Java library for composing and
    parsing JSON-RPC 2.0 messages
  • No framework or transport layer
    dependencies
  • Batching left out for simplicity

1. Practial and efficient Java classes for JSON-RPC 2.0 messages

This package concentrates on providing the most simple and practical Java representation of the JSON-RPC 2.0 protocol messages. Use it as a starting point to furnish a JSON-RPC 2.0 interface to your web application or service. Kept minimal and efficient, it doesn't impose any particular framework or transport onto you. You get a base set of classes to parse, represent and serialise JSON-RPC 2.0 messages and handle the respective exceptions. In just 33K of JAR-compressed byte code.

2. What is JSON-RPC and what is it good for?

JSON-RPC is a protocol of the web. While it can be used with just about any network transport to carry the messages, its JSON encoding and relative simplicity (inspired by XML-RPC) make it ideal for accessing services from JavaScript, mobile and web applications.

JSON-RPC 2.0 overview

3. Version 2.0 of the JSON-RPC protocol

This package implements version 2.0 of JSON-RPC. Explaining the protocol is beyond the scope of this writing, however, these are the two key improvements from the original 1.0 version:

Here is an example version 2.0 JSON-RPC request (with pretty formatting applied):

{
  "method"  : "accounts.addUser",
  "params"  : { "name" : "Jason Remote", "age" : 25, "email" : "[email protected]" },
  "id"      : 123,
  "jsonrpc" : "2.0"
}

And here is an example response:

{
  "result" : "uid-001",
  "id"     : 123,
  "jsonrpc":"2.0"
}

The JSON-RPC 2.0 specification also allows sending of batch (multicall) requests. The rationale for this feature is to improve RPC performance over HTTP connections. However, judging by user posts in the JSON-RPC forum, this tends to confuse people and so it was deliberately left out in this implementation.

The original JSON-RPC website is at json-rpc.org. Discussions and more recent standardisation work are at groups.google.com/group/json-rpc.

4. Requirements

Download the package jsonrpc2-base-{version}.jar JAR file and its JSON Smart JAR dependency to your computer, then place them in your CLASSPATH and you're ready to go.

If you use Maven to build your project:

<dependency>
	<groupId>com.thetransactioncompany</groupId>
	<artifactId>jsonrpc2-base</artifactId>
	<version>{version}</version>
</dependency>

where {version} should be the latest stable.

5. The life cycle of a JSON-RPC 2.0 request

The table below shows the life cycle of a JSON-RPC 2.0 request and the Java classes + methods typically used at each step:

Step Side Action Used methods
1. Client Create a new request JSONRPC2Request()
2. Client Serialise request to string and send JSONRPC2Request.toString()
3. Server Parse received string back to request object JSONRPC2Request.parse()
4. Server Get the request data JSONRPC2Request.getMethod()
JSONRPC2Request.getParamsType()
JSONRPC2Request.getPositionalParams()
JSONRPC2Request.getNamedParams()
JSONRPC2Request.getID()
5. Server Create a response JSONRPC2Response()
6. Server Serialise response to string and send back JSONRPC2Response.toString()
7. Client Parse received string back to response object JSONRPC2Response.parse()
8. Client Check the response for success, get the result/error JSONRPC2Response.indicatesSuccess()
JSONRPC2Response.getResult()
JSONRPC2Response.getError()

6. JSON ↔ Java entity mapping

The JSON-RPC 2.0 Base library applies the following mapping between JSON and Java entities, which you must bear in mind when composing or extracting request parameters. This mapping is actually defined by the underlying JSON Smart package which is used for JSON encoding and decoding.

JSON Java
true|false java.lang.Boolean
number java.lang.Number
string java.lang.String
array java.util.List
object java.util.Map
null null

For more information check out the JSON Smart wiki. JSON Smart is a fork of the popular but stale JSON.simple toolkit, offering vastly improved parse performance and other goodies.

7. Example use

Let's now go through a JSON-RPC 2.0 example where the client sends a request to the server to make a payment on the user's behalf. Upon success the server returns a response containing a string with the payment transaction ID.

First we need to import the necessary packages.

// The JSON-RPC 2.0 classes
import com.thetransactioncompany.jsonrpc2.*;

// We'll need the standard Map and HashMap classes too
import java.util.*;

On the client side we create a request object, which we then serialise to a string to be sent out to the server using whatever transport mechanism is required (usually HTTP):

// The remote method to call
String method = "makePayment";

// The required named parameters to pass
Map<String,Object> params = new HashMap<String,Object>();
params.put("recipient", "Penny Adams");
params.put("amount", 175.05);

// The mandatory request ID
String id = "req-001";

// Create a new JSON-RPC 2.0 request
JSONRPC2Request reqOut = new JSONRPC2Request(method, params, id);

// Serialise the request to a JSON-encoded string
String jsonString = reqOut.toString();

// jsonString can now be dispatched to the server...

Here is the resulting JSON-encoded request string (pretty formatting applied extra):

{
  "id"      : "req-001",
  "method"  : "makePayment",
  "params"  : { "amount" : 175.05, "recipient" : "Penny Adams" },
  "jsonrpc" : "2.0"
}

On the server side, after receiving request string we proceed like this:

// Parse request string
JSONRPC2Request reqIn = null;

try {
	reqIn = JSONRPC2Request.parse(jsonString);

} catch (JSONRPC2ParseException e) {
	System.out.println(e.getMessage());
	// Handle exception...
}

// How to extract the request data
System.out.println("Parsed request with properties :");
System.out.println("\tmethod     : " + reqIn.getMethod());
System.out.println("\tparameters : " + reqIn.getNamedParams());
System.out.println("\tid         : " + reqIn.getID() + "\n\n");

// Process request...

// Create the response (note that the JSON-RPC ID must be echoed back)
String result = "payment-id-001";
JSONRPC2Response respOut = new JSONRPC2Response(result, reqIn.getID());

// Serialise response to JSON-encoded string
jsonString = respOut.toString();

// The response string can now be sent back to the client...

The response string:

{
  "id"      : "req-001",
  "result"  : "payment-id-001",
  "jsonrpc" : "2.0"
}

Back on the client side we parse the response and check whether it was successful or an error was reported:

// Parse response string
JSONRPC2Response respIn = null;

try {
	respIn = JSONRPC2Response.parse(jsonString);
} catch (JSONRPC2ParseException e) {
	System.out.println(e.getMessage());
	// Handle exception...
}


// Check for success or error
if (respIn.indicatesSuccess()) {

	System.out.println("The request succeeded :");

	System.out.println("\tresult : " + respIn.getResult());
	System.out.println("\tid     : " + respIn.getID());
}
else {
	System.out.println("The request failed :");

	JSONRPC2Error err = respIn.getError();

	System.out.println("\terror.code    : " + err.getCode());
	System.out.println("\terror.message : " + err.getMessage());
	System.out.println("\terror.data    : " + err.getData());
}

You can download the complete example from here.

8. Generic parsing of JSON-RPC 2.0 messages

The above example assumed that the server is expecting only JSON-RPC requests. However, the protocol also defines another type of messages - notifications. They can be used in the following situation:

To parse JSON-RPC 2.0 messages of all types - requests, notifications and responses - use the static parse() method of the base abstract class JSONRPC2Message. The classes JSONRPC2Request, JSONRPC2Notification and JSONRPC2Response inherit from it.

Here is an example illustrating how you could do this:

JSONRPC2Message msg = null;

try {
	msg = JSONRPC2Message.parse(jsonString);

} catch (JSONRPC2ParseException e) {
	System.out.println(e);
	// handle exception
}

if (msg instanceof JSONRPC2Request) {
	System.out.println("The message is a Request");
}
else if (msg instanceof JSONRPC2Notification) {
	System.out.println("The message is a Notification");
}
else if (msg instanceof JSONRPC2Response) {
	System.out.println("The message is a Response");
}

The complete example demonstrating combined parsing of JSON-RPC messages can be downloaded from here.

9. Utility classes to extract positional and named parameters

In addition to the base classes offered here, you also get two utility classes to ease the retrieval of the paramaters once the request is received and correctly identified (by its method name):

There are getter methods that correspond to each JSON type, which are then split into two variants - one for retrieving mandatory parameters (will raise an exception if the parameter is missing) and another one for retrieving optional parameters (allows a default value to be specified if the parameter wasn't set).

Let's illustrate their usage with an example.

Suppose you have a method "makePayment" which takes three parameters specified by name. Its signature looks like this:

method: "makePayment"
named parameters:
	"recipient"    : string, mandatory
	"amount"       : number, mandatory
	"confirmation" : boolean, optional (defaults to false)

To extract the parameter values we need to create a new NamedParamsRetriever instance from the parsed request.

First, import the class from the *.util package:

import com.thetransactioncompany.jsonrpc2.util.*;

Then

// Parse the request
JSONRPC2Request request = null;

try {
	// suppose "json" contains the request string JSON...
	request = JSONRPC2Request.parse(json);

} catch (JSONRPC2ParseException e) {
	// handle parse exception...
}

// Get the requested method...
String method = request.getMethod();

// ...after choosing the appropriate handler for this method...

// We expect named parameters (JSON object)
JSONRPC2ParamsType paramsType = request.getParamsType();
if (paramsType != JSONRPC2ParamsType.OBJECT) {
	// raise exception...
}

// Create named parameters instance from params Map
Map<String,Object> params = request.getNamedParams();
NamedParamsRetriever np = new NamedParamsRetriever(params);

Now we are ready to retrieve the method parameters.

To retrieve mandatory parameters we must call a getXXX(String name) method where XXX is the expected value type. E.g. getDouble("amount") to retrieve a double value specified by the name "amount". The method will throw a JSONRPC2Error.INVALID_PARAMS exception if the parameter is missing.

To retrieve an optional parameter we must call a getOptXXX(String name, Object defaultValue) method where XXX is the expected value type. Use the second argument to specify a default value if the parameter wasn't set. E.g. getOptBoolean("confirmation", false) to retrieve an optional boolean parameter by the name "confirmation" with a default value of false.

// Extract parameters
try {
	// Extract mandatory "recipient" parameter
	String recipient = np.getString("recipient");

	// Extract mandatory "amount" parameter
	double amount = np.getDouble("amount");

	// Extract optional "confirmation" parameters, defaults to false
	// if undefined
	boolean confirmation = np.getOptBoolean("confirmation", false);

} catch (JSONRPC2Error e) {
	// If a mandatory parameter is missing or there is a type mismatch
	// you get an exception
}

The complete Java example can be downloaded from here.

10. JavaDoc documentation

The JSON-RPC 2.0 Base source comes with extensive documentation in the form of JavaDocs. A copy of the docs is available for browsing online.

11. Download

The JSON-RPC 2.0 Base package is offered under the Apache 2.0 open source license.

Download now JSON-RPC 2.0 Base

Changes to the source code are tracked in the Git repo at https://bitbucket.org/thetransactioncompany/json-rpc-2.0-base.

Get in touch with me if you have questions or comments about the JSON-RPC 2.0 Base software. For general questions go to the JSON-RPC forum.

You may also want to have a look at the companion JSON-RPC 2.0 Shell product, a must have tool for developers undertaking serious JSON-RPC 2.0 work.

12. Change log