Craig Raw
3 years ago
3 changed files with 161 additions and 20 deletions
@ -0,0 +1,145 @@ |
|||
package com.sparrowwallet.sparrow.net; |
|||
|
|||
import com.fasterxml.jackson.databind.ObjectMapper; |
|||
import com.github.arteam.simplejsonrpc.client.JsonRpcClient; |
|||
import com.github.arteam.simplejsonrpc.client.Transport; |
|||
import com.github.arteam.simplejsonrpc.client.builder.AbstractBuilder; |
|||
import com.github.arteam.simplejsonrpc.client.builder.BatchRequestBuilder; |
|||
import com.google.common.collect.Lists; |
|||
import com.sparrowwallet.sparrow.io.Config; |
|||
import org.jetbrains.annotations.NotNull; |
|||
import org.jetbrains.annotations.Nullable; |
|||
|
|||
import java.util.*; |
|||
|
|||
import static com.sparrowwallet.sparrow.net.BatchedElectrumServerRpc.MAX_RETRIES; |
|||
import static com.sparrowwallet.sparrow.net.BatchedElectrumServerRpc.RETRY_DELAY; |
|||
|
|||
public class PagedBatchRequestBuilder<K, V> extends AbstractBuilder { |
|||
public static final int DEFAULT_PAGE_SIZE = 500; |
|||
|
|||
@NotNull |
|||
private final List<Request> requests; |
|||
|
|||
/** |
|||
* Type of request ids |
|||
*/ |
|||
@Nullable |
|||
private final Class<K> keysType; |
|||
|
|||
/** |
|||
* Expected return type for all requests |
|||
* <p/> |
|||
* This property works exclusively with {@code returnTypes}. Only one of them should be set. |
|||
*/ |
|||
@Nullable |
|||
private final Class<V> returnType; |
|||
|
|||
/** |
|||
* Creates a new batch request builder in an initial state |
|||
* |
|||
* @param transport transport for request performing |
|||
* @param mapper mapper for JSON processing |
|||
*/ |
|||
public PagedBatchRequestBuilder(@NotNull Transport transport, @NotNull ObjectMapper mapper) { |
|||
this(transport, mapper, new ArrayList<Request>(), null, null); |
|||
} |
|||
|
|||
public PagedBatchRequestBuilder(@NotNull Transport transport, @NotNull ObjectMapper mapper, |
|||
@NotNull List<Request> requests, |
|||
@Nullable Class<K> keysType, @Nullable Class<V> returnType) { |
|||
super(transport, mapper); |
|||
this.requests = requests; |
|||
this.keysType = keysType; |
|||
this.returnType = returnType; |
|||
} |
|||
|
|||
/** |
|||
* Adds a new request without specifying a return type |
|||
* |
|||
* @param id request id as a text value |
|||
* @param method request method |
|||
* @param param request param |
|||
* @return the current builder |
|||
*/ |
|||
@NotNull |
|||
public PagedBatchRequestBuilder<K, V> add(Object id, @NotNull String method, @NotNull Object param) { |
|||
requests.add(new Request(id, method, param)); |
|||
return this; |
|||
} |
|||
|
|||
/** |
|||
* Sets type of request keys. |
|||
* The purpose of this method is providing static and runtime type safety of processing of batch responses |
|||
* |
|||
* @param keysClass type of keys |
|||
* @param <NK> type of keys |
|||
* @return a new builder |
|||
*/ |
|||
public <NK> PagedBatchRequestBuilder<NK, V> keysType(@NotNull Class<NK> keysClass) { |
|||
return new PagedBatchRequestBuilder<NK, V>(transport, mapper, requests, keysClass, returnType); |
|||
} |
|||
|
|||
/** |
|||
* Sets an expected response type of requests. |
|||
* This method is preferred when requests have the same response type. |
|||
* |
|||
* @param valuesClass expected requests return type |
|||
* @param <NV> expected requests return type |
|||
* @return a new builder |
|||
*/ |
|||
public <NV> PagedBatchRequestBuilder<K, NV> returnType(@NotNull Class<NV> valuesClass) { |
|||
return new PagedBatchRequestBuilder<K, NV>(transport, mapper, requests, keysType, valuesClass); |
|||
} |
|||
|
|||
/** |
|||
* Validates, executes the request and process response |
|||
* |
|||
* @return map of responses by request ids |
|||
*/ |
|||
@NotNull |
|||
public Map<K, V> execute() throws Exception { |
|||
Map<K, V> allResults = new HashMap<>(); |
|||
JsonRpcClient client = new JsonRpcClient(transport); |
|||
|
|||
List<List<Request>> pages = Lists.partition(requests, getPageSize()); |
|||
for(List<Request> page : pages) { |
|||
BatchRequestBuilder<K, V> batchRequest = client.createBatchRequest().keysType(keysType).returnType(returnType); |
|||
for(Request request : page) { |
|||
if(request.id instanceof String strReq) { |
|||
batchRequest.add(strReq, request.method, request.param); |
|||
} else if(request.id instanceof Integer intReq) { |
|||
batchRequest.add(intReq, request.method, request.param); |
|||
} else { |
|||
throw new IllegalArgumentException("Id of class " + request.id.getClass().getName() + " not supported"); |
|||
} |
|||
} |
|||
|
|||
Map<K, V> pageResult = new RetryLogic<Map<K, V>>(MAX_RETRIES, RETRY_DELAY, List.of(IllegalStateException.class, IllegalArgumentException.class)).getResult(batchRequest::execute); |
|||
allResults.putAll(pageResult); |
|||
} |
|||
|
|||
return allResults; |
|||
} |
|||
|
|||
private int getPageSize() { |
|||
int pageSize = Config.get().getBatchPageSize(); |
|||
if(pageSize < 1) { |
|||
pageSize = DEFAULT_PAGE_SIZE; |
|||
} |
|||
|
|||
return pageSize; |
|||
} |
|||
|
|||
/** |
|||
* Creates a builder of a JSON-RPC batch request in initial state |
|||
* |
|||
* @return batch request builder |
|||
*/ |
|||
@NotNull |
|||
public static PagedBatchRequestBuilder<?, ?> create(Transport transport) { |
|||
return new PagedBatchRequestBuilder<Object, Object>(transport, new ObjectMapper()); |
|||
} |
|||
|
|||
private static record Request(Object id, String method, Object param) {} |
|||
} |
Loading…
Reference in new issue