Skip to content

Commit

Permalink
DefaultRpcClient: add constructor for configurabe Transport implement…
Browse files Browse the repository at this point in the history
…ations

* Add TransportFactory interface
* Add constructor that takes a TransportFactory, make it the canonical constructor
* Rename JavaNet test spec to DefaultRpcClientSpec and have it use the factory
  to construct an client using the JavaNet client.

TODO: Testing could obviously be improved. See the TODOs in the code.
  • Loading branch information
msgilligan committed Oct 5, 2023
1 parent 9db5736 commit beaf95f
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 61 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.consensusj.bitcoin.rx;/**
*
*/
public class ChainTipPublisher {
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@
import java.util.function.Function;
import java.util.function.Supplier;

// TODO: the constructor should take a JsonRpcTransport implementation object.
// We now have DefaultRpcClient which is currently hard-coded to use the JavaNet transport (1-line change + recompile to switch)

// We're overusing inheritance in this hierarchy. We are breaking Effective Java, Item 18: Favor composition over inheritance.
// We're using inheritance to configure:
// A. The set of JSON-RPC methods that are supported, e.g. `getblockcount()` (which creates potential conflicts with `send()` etc)
Expand Down Expand Up @@ -54,34 +51,45 @@
*/
public class DefaultRpcClient implements JsonRpcClient<JavaType> {
private static final Logger log = LoggerFactory.getLogger(DefaultRpcClient.class);
private static final JsonRpcMessage.Version DEFAULT_JSON_RPC_VERSION = JsonRpcMessage.Version.V2;

/**
* 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
* to '1.0' in this base class.
* Functional interface for creating JsonRpcTransport instances. This is used to prevent a circular
* dependency on {@link ObjectMapper}.
*/
private static final JsonRpcMessage.Version DEFAULT_JSON_RPC_VERSION = JsonRpcMessage.Version.V1;

@FunctionalInterface
public interface TransportFactory {
/**
* @param mapper mapper that is shared between {@code DefaultRpcClient} and the {@link JsonRpcTransport}.
* @return a transport instance
*/
JsonRpcTransport<JavaType> create(ObjectMapper mapper);
}
protected final JsonRpcMessage.Version jsonRpcVersion;
protected final ObjectMapper mapper;
private final JavaType defaultType;
private final JsonRpcTransport<JavaType> transport;

public DefaultRpcClient(URI server, final String rpcUser, final String rpcPassword) {
this(JsonRpcTransport.getDefaultSSLContext(), DEFAULT_JSON_RPC_VERSION, server, rpcUser, rpcPassword);
}

public DefaultRpcClient(JsonRpcMessage.Version jsonRpcVersion, URI server, final String rpcUser, final String rpcPassword) {
this(JsonRpcTransport.getDefaultSSLContext(), jsonRpcVersion, server, rpcUser, rpcPassword);
}
public DefaultRpcClient(SSLContext sslContext, JsonRpcMessage.Version jsonRpcVersion, URI server, final String rpcUser, final String rpcPassword) {
this((m) -> new JsonRpcClientJavaNet(m, sslContext, server, rpcUser, rpcPassword), jsonRpcVersion);
}

public DefaultRpcClient(TransportFactory transportFactory, JsonRpcMessage.Version jsonRpcVersion) {
this.jsonRpcVersion = jsonRpcVersion;
mapper = new ObjectMapper();
// TODO: Provide external API to configure FAIL_ON_UNKNOWN_PROPERTIES
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
defaultType = mapper.getTypeFactory().constructType(Object.class);
// TODO: Right now you can manually switch between JsonRpcClientJavaNet and JsonRpcClientHttpUrlConnection by
// commenting out one line or the other and recompiling. There needs to be a new constructor which will
// allow the default (JsonRpcClientJavaNet) to be overridden.
transport = new JsonRpcClientJavaNet(mapper, sslContext, server, rpcUser, rpcPassword);
//transport = new JsonRpcClientHttpUrlConnection(mapper, sslContext, server, rpcUser, rpcPassword);
transport = transportFactory.create(mapper);
}

@Override
public JsonRpcMessage.Version getJsonRpcVersion() {
return jsonRpcVersion;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package org.consensusj.jsonrpc

import spock.lang.Ignore
import spock.lang.Specification

import org.consensusj.bitcoin.jsonrpc.RpcURI

/**
* RPCClient test specification
*/
@Ignore("Integration test")
class DefaultRpcClientSpec extends Specification {
private final int regTestPort = RpcURI.RPCPORT_REGTEST
private final URI testServerUri = "http://localhost:${regTestPort}".toURI();
private final String user = "bitcoinrpc"
private final String pass = "pass"
// TODO: Refactor so tests automatically run against both factories.
private final DefaultRpcClient.TransportFactory transportFactory = (m) -> new JsonRpcClientJavaNet(m, JsonRpcTransport.getDefaultSSLContext(), testServerUri, user, pass)
//private final DefaultRpcClient.TransportFactory transportFactory = (m) -> new JsonRpcClientHttpUrlConnection(m, JsonRpcTransport.getDefaultSSLContext(), testServerUri, user, pass)

def "constructor works correctly" () {
when:
def client = new DefaultRpcClient(transportFactory, JsonRpcMessage.Version.V2)

then:
client.serverURI == "http://localhost:${regTestPort}".toURI()
client.getJsonRpcVersion() == JsonRpcMessage.Version.V2
}

// TODO: Create JsonRpcClientJavaNetSpec that tests the sendRequestForResponseString method

// def "get block count as string" () {
// when:
// def client = new DefaultRpcClient(testServerUri, user, pass)
// JsonRpcRequest req = new JsonRpcRequest("getblockcount");
// String blockcount = client.sendRequestForResponseString(req).join()
//
// then:
// blockcount instanceof String
// blockcount.length() >= 1
// }

def "get block count as JsonRpcResponse" () {
when:
def client = new DefaultRpcClient(transportFactory, JsonRpcMessage.Version.V2)
Integer blockcount = client.send("getblockcount", Integer.class)

then:
blockcount >= 0
}
}

This file was deleted.

0 comments on commit beaf95f

Please sign in to comment.