Skip to content

Commit

Permalink
JsonRpcClient: significant refactoring, Jackson decoupling
Browse files Browse the repository at this point in the history
1. Define JsonRpcTransport interface
2. JsonRpcClient<T extends Type> is now generic
3. JacksonRpcClient is removed (sorry no deprecation)
4. AbstractRpcClient<JavaType> remains Jackson-aware
  • Loading branch information
msgilligan committed Sep 23, 2023
1 parent c88cba6 commit e85f323
Show file tree
Hide file tree
Showing 13 changed files with 148 additions and 273 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package org.consensusj.bitcoin.jsonrpc.groovy

import com.fasterxml.jackson.databind.JavaType
import org.bitcoinj.base.Network
import org.consensusj.bitcoin.jsonrpc.BitcoinExtendedClient
import org.consensusj.jsonrpc.groovy.DynamicRpcMethodFallback

/**
* Bitcoin RPC client for scripting. Allows dynamic methods to access new RPCs or RPCs not implemented in Java client
*/
class BitcoinScriptingClient extends BitcoinExtendedClient implements DynamicRpcMethodFallback {
class BitcoinScriptingClient extends BitcoinExtendedClient implements DynamicRpcMethodFallback<JavaType> {

/**
* No args constructor reads bitcoin.conf
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.base.Sha256Hash;
import org.bitcoinj.core.Transaction;
import org.consensusj.jsonrpc.JsonRpcTransport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -152,7 +153,7 @@ public BitcoinClient(SSLContext sslContext, URI server, String rpcuser, String r
* @param rpcpassword Password (if required)
*/
public BitcoinClient(URI server, String rpcuser, String rpcpassword) {
this(getDefaultSSLContext(), (Network) null, server, rpcuser, rpcpassword);
this(JsonRpcTransport.getDefaultSSLContext(), (Network) null, server, rpcuser, rpcpassword);
}

/**
Expand All @@ -163,7 +164,7 @@ public BitcoinClient(URI server, String rpcuser, String rpcpassword) {
* @param rpcpassword Password (if required)
*/
public BitcoinClient(Network network, URI server, String rpcuser, String rpcpassword) {
this(getDefaultSSLContext(), network, server, rpcuser, rpcpassword);
this(JsonRpcTransport.getDefaultSSLContext(), network, server, rpcuser, rpcpassword);
}

@Deprecated
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.TransactionOutPoint;
import org.bitcoinj.core.TransactionOutput;
import org.consensusj.jsonrpc.JsonRpcTransport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -75,16 +76,16 @@ public BitcoinExtendedClient(SSLContext sslContext, Network network, URI server,
}

public BitcoinExtendedClient(Network network, URI server, String rpcuser, String rpcpassword) {
this(getDefaultSSLContext(), network, server, rpcuser, rpcpassword);
this(JsonRpcTransport.getDefaultSSLContext(), network, server, rpcuser, rpcpassword);
}

@Deprecated
public BitcoinExtendedClient(NetworkParameters netParams, URI server, String rpcuser, String rpcpassword) {
this(getDefaultSSLContext(), netParams.network(), server, rpcuser, rpcpassword);
this(JsonRpcTransport.getDefaultSSLContext(), netParams.network(), server, rpcuser, rpcpassword);
}

public BitcoinExtendedClient(URI server, String rpcuser, String rpcpassword) {
this(getDefaultSSLContext(), (Network) null, server, rpcuser, rpcpassword);
this(JsonRpcTransport.getDefaultSSLContext(), (Network) null, server, rpcuser, rpcpassword);
}

public BitcoinExtendedClient(RpcConfig config) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import org.consensusj.bitcoin.jsonrpc.BitcoinExtendedClient;
import org.consensusj.bitcoin.rx.ChainTipService;
import org.consensusj.bitcoin.rx.zeromq.RxBitcoinZmqService;
import org.consensusj.jsonrpc.JsonRpcTransport;
import org.consensusj.rx.jsonrpc.RxJsonRpcClient;

import javax.net.ssl.SSLContext;
Expand All @@ -32,7 +33,7 @@ public RxBitcoinClient(Network network, URI server, String rpcuser, String rpcpa
}

public RxBitcoinClient(Network network, URI server, String rpcuser, String rpcpassword, boolean useZmq) {
this(getDefaultSSLContext(), network, server, rpcuser, rpcpassword, useZmq);
this(JsonRpcTransport.getDefaultSSLContext(), network, server, rpcuser, rpcpassword, useZmq);
}

public RxBitcoinClient(SSLContext sslContext, Network network, URI server, String rpcuser, String rpcpassword, boolean useZmq) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package org.consensusj.jsonrpc.groovy
import org.consensusj.jsonrpc.JsonRpcMessage
import org.consensusj.jsonrpc.JsonRpcClientHttpUrlConnection

import com.fasterxml.jackson.databind.JavaType;

/**
* Client that uses Groovy <code>methodMissing</code> to allow <i>any</i> JSON-RPC call to be made as <code>client.rpcMethod(args)</code>.
* Note that calling a non-existent method will result in an error from the server.
Expand All @@ -14,7 +16,7 @@ import org.consensusj.jsonrpc.JsonRpcClientHttpUrlConnection
* This client and the {@link DynamicRpcMethodFallback} trait are provided for those looking for something simple,
* flexible, dynamic, and Groovy.
*/
class DynamicRpcClient extends JsonRpcClientHttpUrlConnection implements DynamicRpcMethodFallback {
class DynamicRpcClient extends JsonRpcClientHttpUrlConnection implements DynamicRpcMethodFallback<JavaType> {

DynamicRpcClient(URI server, String rpcuser, String rpcpassword) {
this(JsonRpcMessage.Version.V2, server, rpcuser, rpcpassword)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import java.util.concurrent.CompletableFuture
* @see <a href="https://docs.groovy-lang.org/latest/html/documentation/#_methodmissing">Groovy Language Documentation: methodMissing</a>
* @see <a href="https://docs.groovy-lang.org/latest/html/documentation/#_implementing_a_trait_at_runtime">Groovy Language Documentation: Implementing a trait at runtime</a>
*/
trait DynamicRpcMethodFallback implements JsonRpcClient {
trait DynamicRpcMethodFallback<T> implements JsonRpcClient {
/**
* Dynamically forward missing method calls to the server and return a result.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import javax.net.ssl.SSLContext;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.io.IOException;
import java.util.concurrent.CompletableFuture;

// TODO: Rather than implementing transport (HttpUrlConnection vs. java.net.http) with subclasses use composition
// In other words, the constructor should take a transport implementation object.
Expand Down Expand Up @@ -39,13 +39,17 @@
// map from request to string/stream and to map from string/stream to response. The java.net.http implementation has already defined
// some functional interfaces for this, so coming up with an interface that both the java.net.http implementation and the HttpUrlConnection
// implementation can use will lead to this "SECOND STEP"
//
// Update: Now that JsonRpcClient is a generic with <T extends Type>, we have loosened the Jackson coupling somewhat. The sendRequestForResponse
// and sendRequestForResponseAsync methods from JsonRpcClient have been moved to the JsonRpcTransport class which the JavaNet and HttpUrlConnection
// flavors implement. The AbstractRpcClient constructor should be passed an instance of either transport class and forward methods calls for
// sendRequestForResponseAsync (and sendRequestForResponse ?) to the transport.
/**
* Abstract Base class for a strongly-typed, Jackson-based JSON-RPC client. Most of the work is done
* in default methods in {@link JacksonRpcClient} This abstract class implements the constructors, static fields, and
* Abstract Base class for a strongly-typed, Jackson-based JSON-RPC client. This abstract class implements the constructors, static fields, and
* getters, but leaves the core {@code sendRequestForResponse} method as {@code abstract} to be implemented by subclasses
* allowing implementation with alternative HTTP client libraries.
*/
public abstract class AbstractRpcClient implements JacksonRpcClient {
public abstract class AbstractRpcClient implements JsonRpcClient<JavaType> {
/**
* The default JSON-RPC version in JsonRpcRequest is now '2.0', but since most
* requests are created inside {@code RpcClient} subclasses, we'll continue to default
Expand All @@ -70,37 +74,39 @@ public JsonRpcMessage.Version getJsonRpcVersion() {
return jsonRpcVersion;
}

@Override
/**
* Convenience method for requesting an asynchronous response with a {@link JsonNode} for the result.
* @param request The request to send
* @return A future JSON RPC Response with `result` of type {@code JsonNode}
*/
public CompletableFuture<JsonRpcResponse<JsonNode>> sendRequestForResponseAsync(JsonRpcRequest request) {
return sendRequestForResponseAsync(request, responseTypeFor(JsonNode.class));
}

public ObjectMapper getMapper() {
return mapper;
}

@Override
public JavaType getDefaultType() {
public JavaType defaultType() {
return defaultType;
}

/**
* Return the default {@link SSLContext} without declaring a checked exception
* @return The default {@code SSLContext}
*/
protected static SSLContext getDefaultSSLContext() {
try {
return SSLContext.getDefault();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
@Override
public JavaType responseTypeFor(JavaType resultType) {
return getMapper().getTypeFactory().
constructParametricType(JsonRpcResponse.class, resultType);
}

/**
* Encode username password as Base64 for basic authentication
* <p>
* We're using {@link java.util.Base64}, which requires Android 8.0 or later.
*
* @param authString An authorization string of the form `username:password`
* @return A compliant Base64 encoding of `authString`
*/
protected static String base64Encode(String authString) {
return Base64.getEncoder().encodeToString(authString.getBytes()).trim();
@Override
public JavaType responseTypeFor(Class<?> resultType) {
return getMapper().getTypeFactory().
constructParametricType(JsonRpcResponse.class, resultType);
}

@Override
public JavaType typeForClass(Class<?> clazz) {
return getMapper().constructType(clazz);
}

}

This file was deleted.

Loading

0 comments on commit e85f323

Please sign in to comment.