jsonrpc4j

repository·master·Indexed 22 days ago

https://github.com/briandilley/jsonrpc4j

A Java library for implementing the JSON-RPC 2.0 specification. It supports transport layers including HTTP, Sockets, and Streaming, and provides deep integration with the Spring Framework via components like JsonServiceExporter and JsonProxyFactoryBean. The library uses Jackson for serialization and offers features such as auto-discovery of services, custom error handling via ErrorResolver, and dynamic client proxies using ProxyUtil.

Tokens
6.7K
Snippets
21
Records
26
Agent score
78%

What's inside jsonrpc4j

  1. How server method resolution works

    master

    When a request arrives, the server resolves the method using these steps (short-circuiting when 1 or fewer methods remain):

    1. @JsonRpcMethod: If the method has @JsonRpcMethod(value="name"), it is considered. If required=true, the Java method name is ignored.
    2. Name Match: Otherwise, all methods with the same name as the request are considered.
    3. Parameter Count (Less): If allowLessParams is false, methods with more parameters than the request are removed.
    4. Parameter Count (Extra): If allowExtraParams is false, methods with fewer parameters than the request are removed.
    5. Parameter Count (Proximity): If either of the above are enabled, methods with the lowest difference in parameter count are kept.
    6. Type Matching: Parameters are compared to request types; methods with the highest number of matching parameters are kept.
    7. Tie-break: If multiple methods remain, the first one is used.

    Note on Overloading: Resolution works well for primitives. However, resolution between different POJOs (e.g., UserObject vs UserObjectEx) is not supported because the server cannot efficiently determine the difference in JSON structure.

  2. Combine multiple services into a Composite Service

    master

    Use ProxyUtil.createCompositeServiceProxy to merge multiple service interfaces into a single proxy object. This allows a single JSON-RPC endpoint to expose methods from various services. You can then pass this composite proxy to a JsonRpcServer to expose all methods at one location.

    UserverService userService = ...;
    ContentService contentService = ...;
    BlackJackService blackJackService = ...;
    
    Object compositeService = ProxyUtil.createCompositeServiceProxy(
        this.getClass().getClassLoader(),
        new Object[] { userService, contentService, blackJackService},
        new Class<?>[] { UserService.class, ContentService.class, BlackJackService.class},
        true);
    
    // Use the composite service
    User user = ((UserverService)compositeService).createUser(...);
    
    // Expose all via one server
    JsonRpcServer jsonRpcServer = new JsonRpcServer(compositeService);
  3. Expose Java services via Spring using JsonServiceExporter

    master

    You can expose a Java service as a JSON-RPC endpoint over HTTP by configuring the JsonServiceExporter in your Spring XML configuration. This handles automatic type conversion between JSON and Java objects.

    1. Define and implement your service interface.
    2. Register your implementation as a Spring Bean.
    3. Configure JsonServiceExporter with the service (the bean instance) and serviceInterface (the interface class).
    <!-- Spring Configuration -->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    
    <!-- Your Service Implementation -->
    <bean id="userService" class="com.mycompany.UserServiceImpl" />
    
    <!-- The JSON-RPC Exporter -->
    <bean name="/UserService.json" class="com.googlecode.jsonrpc4j.spring.JsonServiceExporter">
        <property name="service" ref="userService"/>
        <property name="serviceInterface" value="com.mycompany.UserService"/>
    </bean>
  4. Install jsonrpc4j via Maven or Gradle

    master

    Add the jsonrpc4j dependency to your project to use JSON-RPC features in Java. The library uses Jackson for JSON serialization/deserialization. If you are already using Spring, most dependencies are likely already present, except potentially Jackson.

    <!-- Maven -->
    <dependency>
        <groupId>com.github.briandilley.jsonrpc4j</groupId>
        <artifactId>jsonrpc4j</artifactId>
        <version>1.7</version>
    </dependency>
    // Gradle
    implementation('com.github.briandilley.jsonrpc4j:jsonrpc4j:1.7')
  5. Set up a Streaming (Socket) Server

    master

    The StreamServer class provides a straightforward way to run a JSON-RPC server over raw Sockets. It is non-blocking on start and blocks on stop until shutdown is complete.

    // 1. Create the JsonRpcServer
    JsonRpcServer jsonRpcServer = new JsonRpcServer(...);
    
    // 2. Create the StreamServer
    int maxThreads = 50;
    int port = 1420;
    InetAddress bindAddress = InetAddress.getByName("...");
    StreamServer streamServer = new StreamServer(
        jsonRpcServer, maxThreads, port, bindAddress);
    
    // 3. Start (non-blocking)
    streamServer.start();
    
    // 4. Stop (blocking)
    streamServer.stop();
  6. Enable Auto-Discovery of services and clients in Spring

    master

    Instead of manual XML configuration, you can use annotations to automatically discover and register services and clients.

    1. Server-side (Exposing Services):

    • Annotate the interface with @JsonRpcService("/path").
    • Annotate the implementation with @AutoJsonRpcServiceImpl.
    • Register AutoJsonRpcServiceImplExporter in your Spring configuration.

    2. Client-side (Consuming Services):

    • Register AutoJsonRpcClientProxyCreator in your Spring configuration and provide a baseUrl and scanPackage.
    // Service Interface
    @JsonRpcService("/path/to/MyService")
    interface MyService {
        // ... methods
    }
    
    // Service Implementation
    @AutoJsonRpcServiceImpl
    class MyServiceImpl implements MyService {
        // ... implementation
    }
    <!-- Server-side Spring Config -->
    <bean class="com.googlecode.jsonrpc4j.spring.AutoJsonRpcServiceImplExporter"/>
    
    <!-- Client-side Spring Config -->
    <bean class="com.googlecode.jsonrpc4j.spring.AutoJsonRpcClientProxyCreator">
        <property name="baseUrl" value="http://hostname/api/" />
        <property name="scanPackage" value="com.mycompany.services" />
    </bean>
  7. Use JsonRpcHttpClient for non-Spring clients

    master

    To communicate with a JSON-RPC service without using the Spring Framework, instantiate a JsonRpcHttpClient with the service URL. You can invoke methods directly by passing the method name, an array of arguments, and the expected return class.

    JsonRpcHttpClient client = new JsonRpcHttpClient(
        new URL("http://example.com/UserService.json"));
    
    User user = client.invoke("createUser", new Object[] { "bob", "the builder" }, User.class);
  8. Implement a non-Spring JsonRpcServer

    master

    You can host a JSON-RPC server without Spring by instantiating JsonRpcServer with your service implementation and its interface class. To handle requests in a web environment, call the handle(req, resp) method within a servlet's doPost method.

    // Create the server
    JsonRpcServer server = new JsonRpcServer(userService, UserService.class);
    
    // Example usage in a Servlet
    class UserServiceServlet extends HttpServlet {
        private UserService userService;
        private JsonRpcServer jsonRpcServer;
    
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
            jsonRpcServer.handle(req, resp);
        }
    
        public void init(ServletConfig config) {
            this.jsonRpcServer = new JsonRpcServer(this.userService, UserService.class);
        }
    }
  9. Create a JSON-RPC client proxy in Spring

    master

    To consume a JSON-RPC service from a Spring application, use the JsonProxyFactoryBean. This creates a dynamic proxy of your service interface that transparently makes remote calls.

    <bean class="com.googlecode.jsonrpc4j.spring.JsonProxyFactoryBean">
        <property name="serviceUrl" value="http://example.com/UserService.json"/>
        <property name="serviceInterface" value="com.mycompany.UserService"/>
    </bean>
  10. Create a dynamic client proxy with ProxyUtil

    master

    Instead of manual invocation, you can use ProxyUtil.createClientProxy to create a dynamic proxy of your service interface. This allows you to call methods directly on the interface as if it were a local object.

    JsonRpcHttpClient client = new JsonRpcHttpClient(
        new URL("http://example.com/UserService.json"));
    
    UserService userService = ProxyUtil.createClientProxy(
        getClass().getClassLoader(),
        UserService.class,
        client);
    
    User user = userService.createUser("bob", "the builder");
  11. Use @JsonRpcParam for named parameters

    master

    By default, JSON-RPC may use indexed parameters. To use named parameters in your JSON requests, annotate the parameters in your service interface with @JsonRpcParam.

    package com.mycompany;
    
    public interface UserService {
        User createUser(@JsonRpcParam(value="theUserName") String userName, @JsonRpcParam(value="thePassword") String password);
    }
  12. Pass fixed parameters with @JsonRpcFixedParam

    master

    Use @JsonRpcFixedParam to inject constant values into a service method that the client does not need to provide. You can use @JsonRpcFixedParams to provide a collection of multiple fixed parameters.

    @JsonRpcService("/jsonrpc")
    public interface LibraryService {
        @JsonRpcMethod("VideoLibrary.GetTVShows")
        @JsonRpcFixedParam(name = "status", value = "published")
        List<TVShow> fetchTVShows(@JsonRpcParam(value="properties") final List<String> properties);
    }

    JSON Request:

    {
        "jsonrpc":"2.0", 
        "method": "VideoLibrary.GetTVShows", 
        "params": { 
            "status": "published", 
            "properties": ["title"] 
        }, 
        "id":1
    }