JSON RPC examples - apinazo/booter GitHub Wiki

Overview

JSON-RPC could be more useful than REST in inter-service communications, since it will reduce the traffic overload and gives a simple way to expose services, avoiding all the REST verbosity.

Here is an example of how to use it, as it's implemented in Booter.

Configuration

Dependencies

I'll be using the third party library jsonrpc4j as it's easy and simple. Just add this dependency to the pom.xml.

<dependency>
    <groupId>com.github.briandilley.jsonrpc4j</groupId>
    <artifactId>jsonrpc4j</artifactId>
    <version>1.5.3</version>
</dependency>

Service

First of all, let's define the service. In order for this to work, there must be both an interface and an implementations. It won't work with a single class.

The interface just define the service's methods.

public interface SampleRPCService {

    String sayHello(@JsonRpcParam(value = "name") String name);
}

The @JsonRpcParam(value = "name") gives a name to the parameter, in the JSON-RPC requests.

The implementation of the service could be this one:

@Service
public class SampleRPRCServiceImpl implements SampleRPCService {

    public String sayHello(String name) {
        return "Hello, " + name;
    }
}

Note that the service is declared as a Spring bean, annotated with @Service. This is mandatory to inject it as shown later.

Configuration

The configuration is a normal @Configuration, defining a @Bean for each service class to expose through JSON-RPC. The bean will be of type JsonServiceExporter and will be built using the service interface declared above, so the @Service implementation is injected.

@Configuration
public class RPCConfiguration {

    @Bean(name = "/rpc/sample")
    public JsonServiceExporter jsonServiceExporter(SampleRPCService sampleRPCService) {
        JsonServiceExporter exporter = new JsonServiceExporter();
        exporter.setService(sampleRPCService);
        exporter.setServiceInterface(SampleRPCService.class);
        return exporter;
    }
}

The name = "/rpc/sample" declares the path the service will have in the context.

Consuming the service

Once the app is up, consuming the service is very similar to doing so with a REST service.

  • The path is the one defined in the bean. In this example: /rpc/sample.
  • The HTTP method will always be POST.
  • The payload will content the name of the remote service and its params.

Example request:

curl -v -H "Content-Type: application/json" -d "{\"id\":0, \"method\":\"sayHello\", \"params\":[\"John Doe\"]}" http://localhost:8001/rpc/sample

Same request, using named params:

curl -i -H "Content-Type: application/json" -d "{\"id\":0, \"method\":\"sayHello\", \"params\":{\"name\":\"John Doe\"} }" http://localhost:8001/rpc/sample

The response to the request will be something like this:

{"jsonrpc":"2.0","id":0,"result":"Hello, John Doe"}

Testing

Unit testing of the example makes no sense since it is ver simple. But integration tests are very interesting to demonstrate how JSON-RPC works. Some are implemented in the SampleRPRCServiceIntegrationTest class.

The tests uses RestAssured as a HTTP test client, like in this example:

@Test
public void testSayHello() {

    // Build the data to send to the service.
    Map<String,Object> payload = new HashMap<>();
    payload.put("id", "0"); // id is mandatory.
    payload.put("method", "sayHello");
    payload.put("params", new ArrayList<>(Arrays.asList(userName)));

    // Do the request.
    given()
        .filter(new RequestLoggingFilter()) // Log request details.
        .filter(new ResponseLoggingFilter()) // Same for response.
        .contentType("application/json")
        .body(payload) // Auto converted to JSON.
    .when()
        .post("http://localhost:" + serverPort + "/rpc/sample")
    .then()
        .statusCode(200)
        .body(containsString(userName));
    }
⚠️ **GitHub.com Fallback** ⚠️