Skip to content

Commit 3a87ca8

Browse files
committed
[2.1.0-SNAPSHOT]
EtherScanAPI.Builder#withRetryOnLimitReach added
1 parent 3b1af9d commit 3a87ca8

33 files changed

+242
-175
lines changed

gradle.properties

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
groupId=com.github.goodforgod
22
artifactId=java-etherscan-api
3-
artifactVersion=2.0.0
3+
artifactVersion=2.1.0-SNAPSHOT
44

55

66
##### GRADLE #####

src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,9 @@ final class AccountAPIProvider extends BasicProvider implements AccountAPI {
5050
AccountAPIProvider(RequestQueueManager requestQueueManager,
5151
String baseUrl,
5252
EthHttpClient executor,
53-
Converter converter) {
54-
super(requestQueueManager, "account", baseUrl, executor, converter);
53+
Converter converter,
54+
int retryCount) {
55+
super(requestQueueManager, "account", baseUrl, executor, converter, retryCount);
5556
}
5657

5758
@NotNull

src/main/java/io/goodforgod/api/etherscan/BasicProvider.java

+45-6
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,23 @@ abstract class BasicProvider {
3030
private final EthHttpClient executor;
3131
private final RequestQueueManager queue;
3232
private final Converter converter;
33+
private final int retryCountLimit;
3334

3435
BasicProvider(RequestQueueManager requestQueueManager,
3536
String module,
3637
String baseUrl,
3738
EthHttpClient ethHttpClient,
38-
Converter converter) {
39+
Converter converter,
40+
int retryCountLimit) {
3941
this.queue = requestQueueManager;
4042
this.module = "&module=" + module;
4143
this.baseUrl = baseUrl;
4244
this.executor = ethHttpClient;
4345
this.converter = converter;
46+
this.retryCountLimit = retryCountLimit;
4447
}
4548

46-
<T> T convert(byte[] json, Class<T> tClass) {
49+
private <T> T convert(byte[] json, Class<T> tClass) {
4750
try {
4851
final T t = converter.fromJson(json, tClass);
4952
if (t instanceof StringResponseTO && ((StringResponseTO) t).getResult().startsWith(MAX_RATE_LIMIT_REACHED)) {
@@ -66,23 +69,59 @@ <T> T convert(byte[] json, Class<T> tClass) {
6669
}
6770
}
6871

69-
byte[] getRequest(String urlParameters) {
72+
private byte[] getRequest(String urlParameters) {
7073
queue.takeTurn();
7174
final URI uri = URI.create(baseUrl + module + urlParameters);
7275
return executor.get(uri);
7376
}
7477

75-
byte[] postRequest(String urlParameters, String dataToPost) {
78+
private byte[] postRequest(String urlParameters, String dataToPost) {
7679
queue.takeTurn();
7780
final URI uri = URI.create(baseUrl + module + urlParameters);
7881
return executor.post(uri, dataToPost.getBytes(StandardCharsets.UTF_8));
7982
}
8083

8184
<T> T getRequest(String urlParameters, Class<T> tClass) {
82-
return convert(getRequest(urlParameters), tClass);
85+
return getRequest(urlParameters, tClass, 0);
86+
}
87+
88+
private <T> T getRequest(String urlParameters, Class<T> tClass, int retryCount) {
89+
try {
90+
return convert(getRequest(urlParameters), tClass);
91+
} catch (Exception e) {
92+
if (retryCount < retryCountLimit) {
93+
try {
94+
Thread.sleep(1150);
95+
} catch (InterruptedException ex) {
96+
throw new IllegalStateException(ex);
97+
}
98+
99+
return getRequest(urlParameters, tClass, retryCount + 1);
100+
} else {
101+
throw e;
102+
}
103+
}
83104
}
84105

85106
<T> T postRequest(String urlParameters, String dataToPost, Class<T> tClass) {
86-
return convert(postRequest(urlParameters, dataToPost), tClass);
107+
return postRequest(urlParameters, dataToPost, tClass, 0);
108+
}
109+
110+
private <T> T postRequest(String urlParameters, String dataToPost, Class<T> tClass, int retryCount) {
111+
try {
112+
return convert(postRequest(urlParameters, dataToPost), tClass);
113+
} catch (EtherScanRateLimitException e) {
114+
if (retryCount < retryCountLimit) {
115+
try {
116+
Thread.sleep(1150);
117+
} catch (InterruptedException ex) {
118+
throw new IllegalStateException(ex);
119+
}
120+
121+
return postRequest(urlParameters, dataToPost, tClass, retryCount + 1);
122+
} else {
123+
throw e;
124+
}
125+
}
87126
}
88127
}

src/main/java/io/goodforgod/api/etherscan/BlockAPIProvider.java

+4-8
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,17 @@ final class BlockAPIProvider extends BasicProvider implements BlockAPI {
2626
BlockAPIProvider(RequestQueueManager requestQueueManager,
2727
String baseUrl,
2828
EthHttpClient executor,
29-
Converter converter) {
30-
super(requestQueueManager, "block", baseUrl, executor, converter);
29+
Converter converter,
30+
int retryCount) {
31+
super(requestQueueManager, "block", baseUrl, executor, converter, retryCount);
3132
}
3233

3334
@NotNull
3435
@Override
3536
public Optional<BlockUncle> uncles(long blockNumber) throws EtherScanException {
3637
final String urlParam = ACT_BLOCK_PARAM + BLOCKNO_PARAM + blockNumber;
37-
final byte[] response = getRequest(urlParam);
38-
if (response.length == 0) {
39-
return Optional.empty();
40-
}
41-
4238
try {
43-
final UncleBlockResponseTO responseTO = convert(response, UncleBlockResponseTO.class);
39+
final UncleBlockResponseTO responseTO = getRequest(urlParam, UncleBlockResponseTO.class);
4440
if (responseTO.getMessage().startsWith("NOTOK")) {
4541
return Optional.empty();
4642
}

src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,9 @@ final class ContractAPIProvider extends BasicProvider implements ContractAPI {
3535
ContractAPIProvider(RequestQueueManager requestQueueManager,
3636
String baseUrl,
3737
EthHttpClient executor,
38-
Converter converter) {
39-
super(requestQueueManager, "contract", baseUrl, executor, converter);
38+
Converter converter,
39+
int retryCount) {
40+
super(requestQueueManager, "contract", baseUrl, executor, converter, retryCount);
4041
}
4142

4243
@NotNull

src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java

+12-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ final class EthScanAPIBuilder implements EtherScanAPI.Builder {
2626

2727
private final Gson gson = new GsonConfiguration().builder().create();
2828

29+
private int retryCountOnLimitReach = 0;
2930
private String apiKey = DEFAULT_KEY;
3031
private RequestQueueManager queueManager;
3132
private EthNetwork ethNetwork = EthNetworks.MAINNET;
@@ -87,6 +88,16 @@ public EtherScanAPI.Builder withConverter(@NotNull Supplier<Converter> converter
8788
return this;
8889
}
8990

91+
@NotNull
92+
public EtherScanAPI.Builder withRetryOnLimitReach(int maxRetryCount) {
93+
if (maxRetryCount < 0 || maxRetryCount > 20) {
94+
throw new IllegalStateException("maxRetryCount value must be in range from 0 to 20, but was: " + maxRetryCount);
95+
}
96+
97+
this.retryCountOnLimitReach = maxRetryCount;
98+
return this;
99+
}
100+
90101
@Override
91102
public @NotNull EtherScanAPI build() {
92103
RequestQueueManager requestQueueManager;
@@ -99,6 +110,6 @@ public EtherScanAPI.Builder withConverter(@NotNull Supplier<Converter> converter
99110
}
100111

101112
return new EtherScanAPIProvider(apiKey, ethNetwork, requestQueueManager, ethHttpClientSupplier.get(),
102-
converterSupplier.get());
113+
converterSupplier.get(), retryCountOnLimitReach);
103114
}
104115
}

src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java

+11
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package io.goodforgod.api.etherscan;
22

3+
import io.goodforgod.api.etherscan.error.EtherScanRateLimitException;
34
import io.goodforgod.api.etherscan.http.EthHttpClient;
45
import io.goodforgod.api.etherscan.manager.RequestQueueManager;
56
import java.util.function.Supplier;
67
import org.jetbrains.annotations.NotNull;
8+
import org.jetbrains.annotations.Range;
79

810
/**
911
* EtherScan full API Description <a href="https://etherscan.io/apis">...</a>
@@ -62,6 +64,15 @@ interface Builder {
6264
@NotNull
6365
Builder withConverter(@NotNull Supplier<Converter> converterSupplier);
6466

67+
/**
68+
* By default is disabled
69+
*
70+
* @param maxRetryCount to retry if {@link EtherScanRateLimitException} thrown
71+
* @return self
72+
*/
73+
@NotNull
74+
EtherScanAPI.Builder withRetryOnLimitReach(@Range(from = 0, to = 20) int maxRetryCount);
75+
6576
@NotNull
6677
EtherScanAPI build();
6778
}

src/main/java/io/goodforgod/api/etherscan/EtherScanAPIProvider.java

+10-9
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,20 @@ final class EtherScanAPIProvider implements EtherScanAPI {
2626
EthNetwork network,
2727
RequestQueueManager queue,
2828
EthHttpClient ethHttpClient,
29-
Converter converter) {
29+
Converter converter,
30+
int retryCount) {
3031
// EtherScan 1request\5sec limit support by queue manager
3132
final String baseUrl = network.domain() + "?apikey=" + apiKey;
3233

3334
this.requestQueueManager = queue;
34-
this.account = new AccountAPIProvider(queue, baseUrl, ethHttpClient, converter);
35-
this.block = new BlockAPIProvider(queue, baseUrl, ethHttpClient, converter);
36-
this.contract = new ContractAPIProvider(queue, baseUrl, ethHttpClient, converter);
37-
this.logs = new LogsAPIProvider(queue, baseUrl, ethHttpClient, converter);
38-
this.proxy = new ProxyAPIProvider(queue, baseUrl, ethHttpClient, converter);
39-
this.stats = new StatisticAPIProvider(queue, baseUrl, ethHttpClient, converter);
40-
this.txs = new TransactionAPIProvider(queue, baseUrl, ethHttpClient, converter);
41-
this.gasTracker = new GasTrackerAPIProvider(queue, baseUrl, ethHttpClient, converter);
35+
this.account = new AccountAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount);
36+
this.block = new BlockAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount);
37+
this.contract = new ContractAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount);
38+
this.logs = new LogsAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount);
39+
this.proxy = new ProxyAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount);
40+
this.stats = new StatisticAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount);
41+
this.txs = new TransactionAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount);
42+
this.gasTracker = new GasTrackerAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount);
4243
}
4344

4445
@NotNull

src/main/java/io/goodforgod/api/etherscan/GasTrackerAPIProvider.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@ final class GasTrackerAPIProvider extends BasicProvider implements GasTrackerAPI
2828
GasTrackerAPIProvider(RequestQueueManager queue,
2929
String baseUrl,
3030
EthHttpClient ethHttpClient,
31-
Converter converter) {
32-
super(queue, "gastracker", baseUrl, ethHttpClient, converter);
31+
Converter converter,
32+
int retryCount) {
33+
super(queue, "gastracker", baseUrl, ethHttpClient, converter, retryCount);
3334
}
3435

3536
@Override

src/main/java/io/goodforgod/api/etherscan/LogsAPIProvider.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ final class LogsAPIProvider extends BasicProvider implements LogsAPI {
2525
LogsAPIProvider(RequestQueueManager queue,
2626
String baseUrl,
2727
EthHttpClient executor,
28-
Converter converter) {
29-
super(queue, "logs", baseUrl, executor, converter);
28+
Converter converter,
29+
int retryCount) {
30+
super(queue, "logs", baseUrl, executor, converter, retryCount);
3031
}
3132

3233
@NotNull

src/main/java/io/goodforgod/api/etherscan/ProxyAPIProvider.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,9 @@ final class ProxyAPIProvider extends BasicProvider implements ProxyAPI {
6060
ProxyAPIProvider(RequestQueueManager queue,
6161
String baseUrl,
6262
EthHttpClient executor,
63-
Converter converter) {
64-
super(queue, "proxy", baseUrl, executor, converter);
63+
Converter converter,
64+
int retryCount) {
65+
super(queue, "proxy", baseUrl, executor, converter, retryCount);
6566
}
6667

6768
@Override

src/main/java/io/goodforgod/api/etherscan/StatisticAPIProvider.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ final class StatisticAPIProvider extends BasicProvider implements StatisticAPI {
3333
StatisticAPIProvider(RequestQueueManager queue,
3434
String baseUrl,
3535
EthHttpClient executor,
36-
Converter converter) {
37-
super(queue, "stats", baseUrl, executor, converter);
36+
Converter converter,
37+
int retry) {
38+
super(queue, "stats", baseUrl, executor, converter, retry);
3839
}
3940

4041
@NotNull

src/main/java/io/goodforgod/api/etherscan/TransactionAPIProvider.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ final class TransactionAPIProvider extends BasicProvider implements TransactionA
2727
TransactionAPIProvider(RequestQueueManager queue,
2828
String baseUrl,
2929
EthHttpClient executor,
30-
Converter converter) {
31-
super(queue, "transaction", baseUrl, executor, converter);
30+
Converter converter,
31+
int retryCount) {
32+
super(queue, "transaction", baseUrl, executor, converter, retryCount);
3233
}
3334

3435
@NotNull

src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java

+5-5
Original file line numberDiff line numberDiff line change
@@ -17,27 +17,27 @@ public interface RequestQueueManager extends AutoCloseable {
1717
* Is used by default when no API KEY is provided
1818
*/
1919
static RequestQueueManager anonymous() {
20-
return new SemaphoreRequestQueueManager(1, Duration.ofMillis(5015L));
20+
return new SemaphoreRequestQueueManager(1, Duration.ofMillis(5045L));
2121
}
2222

2323
/**
2424
* Is available for all registered free API KEYs
2525
* <a href="https://docs.etherscan.io/getting-started/viewing-api-usage-statistics">Free API KEY</a>
2626
*/
2727
static RequestQueueManager planFree() {
28-
return new SemaphoreRequestQueueManager(5, Duration.ofMillis(1015L));
28+
return new SemaphoreRequestQueueManager(5, Duration.ofMillis(1045L));
2929
}
3030

3131
static RequestQueueManager planStandard() {
32-
return new SemaphoreRequestQueueManager(10, Duration.ofMillis(1015L));
32+
return new SemaphoreRequestQueueManager(10, Duration.ofMillis(1045L));
3333
}
3434

3535
static RequestQueueManager planAdvanced() {
36-
return new SemaphoreRequestQueueManager(20, Duration.ofMillis(1015L));
36+
return new SemaphoreRequestQueueManager(20, Duration.ofMillis(1045L));
3737
}
3838

3939
static RequestQueueManager planProfessional() {
40-
return new SemaphoreRequestQueueManager(30, Duration.ofMillis(1015L));
40+
return new SemaphoreRequestQueueManager(30, Duration.ofMillis(1045L));
4141
}
4242

4343
static RequestQueueManager unlimited() {

src/main/java/io/goodforgod/api/etherscan/model/Abi.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public int hashCode() {
5555
@Override
5656
public String toString() {
5757
return "Abi{" +
58-
"contractAbi='" + contractAbi + '\'' +
58+
"contractAbi=" + contractAbi +
5959
", isVerified=" + isVerified +
6060
'}';
6161
}

src/main/java/io/goodforgod/api/etherscan/model/Balance.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public int hashCode() {
4545
@Override
4646
public String toString() {
4747
return "Balance{" +
48-
"address='" + address + '\'' +
48+
"address=" + address +
4949
", balance=" + balance +
5050
'}';
5151
}

src/main/java/io/goodforgod/api/etherscan/model/Block.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public String toString() {
5858
return "Block{" +
5959
"blockNumber=" + blockNumber +
6060
", blockReward=" + blockReward +
61-
", timeStamp='" + timeStamp + '\'' +
61+
", timeStamp=" + timeStamp +
6262
'}';
6363
}
6464

src/main/java/io/goodforgod/api/etherscan/model/BlockUncle.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public static class Uncle {
1919
private BigInteger blockreward;
2020
private int unclePosition;
2121

22-
private Uncle() {}
22+
protected Uncle() {}
2323

2424
// <editor-fold desc="Getters">
2525
public String getMiner() {
@@ -54,7 +54,7 @@ public int hashCode() {
5454
@Override
5555
public String toString() {
5656
return "Uncle{" +
57-
"miner='" + miner + '\'' +
57+
"miner=" + miner +
5858
", blockreward=" + blockreward +
5959
", unclePosition=" + unclePosition +
6060
'}';
@@ -128,9 +128,9 @@ public String getUncleInclusionReward() {
128128
@Override
129129
public String toString() {
130130
return "UncleBlock{" +
131-
"blockMiner='" + blockMiner + '\'' +
131+
"blockMiner=" + blockMiner +
132132
", uncles=" + uncles +
133-
", uncleInclusionReward='" + uncleInclusionReward + '\'' +
133+
", uncleInclusionReward=" + uncleInclusionReward +
134134
'}';
135135
}
136136

0 commit comments

Comments
 (0)