Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleanup ExplorerApiClient and ApiClient, share OkHttp instance #156

Merged
merged 1 commit into from
Apr 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions MIGRATION
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[4.0.9]
- ExplorerAndPoolUnspentBoxesLoader moved from package org.ergoplatform.appkit.impl to org.ergoplatform.appkit
- DefaultApi.getApiV1AddressesP1Transactions new parameter "concise" - use false for old behaviour
- RestApiErgoClient.createWithProxy method replaced by RestApiErgoClient.createWithHttpClientBuilder
To create an instance using a proxy, use createWithHttpClientBuilder with a Builder created with
new OkHttpClient.Builder().proxy(...)
53 changes: 29 additions & 24 deletions appkit/src/main/java/org/ergoplatform/appkit/RestApiErgoClient.java
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
package org.ergoplatform.appkit;

import com.google.common.base.Strings;

import org.ergoplatform.appkit.config.ErgoNodeConfig;
import org.ergoplatform.appkit.impl.BlockchainContextBuilderImpl;
import org.ergoplatform.appkit.impl.NodeAndExplorerDataSourceImpl;
import org.ergoplatform.explorer.client.ExplorerApiClient;
import org.ergoplatform.restapi.client.ApiClient;

import java.net.Proxy;
import java.util.function.Function;

import javax.annotation.Nullable;

import okhttp3.OkHttpClient;

/**
* This implementation of {@link ErgoClient} uses REST API of Ergo node for communication.
*/
public class RestApiErgoClient implements ErgoClient {
private final String _nodeUrl;
private final NetworkType _networkType;
private final NodeAndExplorerDataSourceImpl apiClient;
private final String _explorerUrl;

public final static String defaultMainnetExplorerUrl = "https://api.ergoplatform.com";
public final static String defaultTestnetExplorerUrl = "https://api-testnet.ergoplatform.com";
Expand All @@ -34,24 +35,26 @@ public class RestApiErgoClient implements ErgoClient {
* form `https://host[:port]` where port is optional.
* If `null` or empty string passed then the Explorer client is not
* initialized and the client works in the `node only` mode.
* @param proxy Requests are passed through this proxy (if non-null).
* @param httpClientBuilder Builder used to construct http client instances. If null, a new
* OkHttpClient with default parameters is used.
*/
RestApiErgoClient(String nodeUrl, NetworkType networkType, String apiKey, String explorerUrl, @Nullable Proxy proxy) {
_nodeUrl = nodeUrl;
RestApiErgoClient(String nodeUrl, NetworkType networkType, String apiKey, String explorerUrl, @Nullable OkHttpClient.Builder httpClientBuilder) {
_networkType = networkType;
ApiClient nodeClient = new ApiClient(_nodeUrl, "ApiKeyAuth", apiKey);
if (proxy != null) {
nodeClient.createDefaultAdapter(proxy);

// if no httpClientBuilder is set, we use a single one for both api clients
if (httpClientBuilder == null) {
// just using the same builder is not enough - we have to derive the builder from an
// actual OkHttpClient instance to share the thread pools.
httpClientBuilder = new OkHttpClient().newBuilder();
}
_explorerUrl = explorerUrl;

ApiClient nodeClient = new ApiClient(nodeUrl, "ApiKeyAuth", apiKey);
nodeClient.configureFromOkClientBuilder(httpClientBuilder);

ExplorerApiClient explorerClient;
if (!Strings.isNullOrEmpty(_explorerUrl)) {
if (proxy != null) {
explorerClient = new ExplorerApiClient(_explorerUrl, proxy);
}
else {
explorerClient = new ExplorerApiClient(_explorerUrl);
}
if (!Strings.isNullOrEmpty(explorerUrl)) {
explorerClient = new ExplorerApiClient(explorerUrl);
explorerClient.configureFromOkClientBuilder(httpClientBuilder);
} else {
explorerClient = null;
}
Expand Down Expand Up @@ -121,11 +124,12 @@ public static ErgoClient create(String nodeUrl, NetworkType networkType, String
* `https://host:port/`. If null or empty, then explorer connection
* is not initialized so that the resulting {@link ErgoClient} can
* work in `node-only` mode.
* @param proxy Requests are passed through this proxy (if non-null).
* @param httpClientBuilder Builder used to construct http client instances. If null, a new
* OkHttpClient with default parameters is used.
* @return a new instance of {@link ErgoClient} connected to a given node
*/
public static ErgoClient createWithProxy(String nodeUrl, NetworkType networkType, String apiKey, String explorerUrl, @Nullable Proxy proxy) {
return new RestApiErgoClient(nodeUrl, networkType, apiKey, explorerUrl, proxy);
public static ErgoClient createWithHttpClientBuilder(String nodeUrl, NetworkType networkType, String apiKey, String explorerUrl, @Nullable OkHttpClient.Builder httpClientBuilder) {
return new RestApiErgoClient(nodeUrl, networkType, apiKey, explorerUrl, httpClientBuilder);
}

/**
Expand Down Expand Up @@ -155,15 +159,16 @@ public static ErgoClient create(ErgoNodeConfig nodeConf, String explorerUrl) {
* `https://host:port/`. If null or empty, then explorer connection
* is not initialized so that the resulting {@link ErgoClient} can
* work in `node-only` mode.
* @param proxy Requests are passed through this proxy (if non-null).
* @param httpClientBuilder Builder used to construct http client instances. If null, a new
* OkHttpClient with default parameters is used.
*/
public static ErgoClient createWithProxy(ErgoNodeConfig nodeConf, String explorerUrl, @Nullable Proxy proxy) {
return RestApiErgoClient.createWithProxy(
public static ErgoClient createWithHttpClientBuilder(ErgoNodeConfig nodeConf, String explorerUrl, @Nullable OkHttpClient.Builder httpClientBuilder) {
return RestApiErgoClient.createWithHttpClientBuilder(
nodeConf.getNodeApi().getApiUrl(),
nodeConf.getNetworkType(),
nodeConf.getNodeApi().getApiKey(),
explorerUrl,
proxy
httpClientBuilder
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,84 +2,40 @@

import com.google.gson.Gson;
import com.google.gson.JsonParseException;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.Converter;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.converter.scalars.ScalarsConverterFactory;
import org.ergoplatform.explorer.client.auth.HttpBasicAuth;
import org.ergoplatform.explorer.client.auth.ApiKeyAuth;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.net.Proxy;
import java.text.DateFormat;
import java.time.format.DateTimeFormatter;
import java.util.LinkedHashMap;
import java.util.Map;

import okhttp3.OkHttpClient;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.Converter;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.converter.scalars.ScalarsConverterFactory;

public class ExplorerApiClient {

private String _hostUrl;
private Map<String, Interceptor> apiAuthorizations;
private OkHttpClient.Builder okBuilder;
private Retrofit.Builder adapterBuilder;
private JSON json;
private Proxy proxy;

public ExplorerApiClient(String hostUrl) {
_hostUrl = hostUrl;
apiAuthorizations = new LinkedHashMap<String, Interceptor>();
createDefaultAdapter();
this(hostUrl, null);
}

public ExplorerApiClient(String hostUrl, Proxy proxy) {
_hostUrl = hostUrl;
apiAuthorizations = new LinkedHashMap<String, Interceptor>();
this.proxy = proxy;
createDefaultAdapter();
}

public ExplorerApiClient(String hostUrl, String[] authNames) {
this(hostUrl);
for(String authName : authNames) {
throw new RuntimeException("auth name \"" + authName + "\" not found in available auth names");
}
}

/**
* Basic constructor for single auth name
* @param authName Authentication name
*/
public ExplorerApiClient(String hostUrl, String authName) {
this(hostUrl, new String[]{authName});
}

/**
* Helper constructor for single api key
* @param authName Authentication name
* @param apiKey API key
*/
public ExplorerApiClient(String hostUrl, String authName, String apiKey) {
this(hostUrl, authName);
this.setApiKey(apiKey);
}

/**
* Helper constructor for single basic auth or password oauth2
* @param authName Authentication name
* @param username Username
* @param password Password
*/
public ExplorerApiClient(String hostUrl, String authName, String username, String password) {
this(hostUrl, authName);
this.setCredentials(username, password);
}

public void createDefaultAdapter() {
json = new JSON();
okBuilder = new OkHttpClient.Builder();
Expand Down Expand Up @@ -123,73 +79,6 @@ public ExplorerApiClient setLocalDateFormat(DateTimeFormatter dateFormat) {
return this;
}


/**
* Helper method to configure the first api key found
* @param apiKey API key
* @return ApiClient
*/
public ExplorerApiClient setApiKey(String apiKey) {
for(Interceptor apiAuthorization : apiAuthorizations.values()) {
if (apiAuthorization instanceof ApiKeyAuth) {
ApiKeyAuth keyAuth = (ApiKeyAuth) apiAuthorization;
keyAuth.setApiKey(apiKey);
return this;
}
}
return this;
}

/**
* Helper method to configure the username/password for basic auth or password oauth
* @param username Username
* @param password Password
* @return ApiClient
*/
public ExplorerApiClient setCredentials(String username, String password) {
for(Interceptor apiAuthorization : apiAuthorizations.values()) {
if (apiAuthorization instanceof HttpBasicAuth) {
HttpBasicAuth basicAuth = (HttpBasicAuth) apiAuthorization;
basicAuth.setCredentials(username, password);
return this;
}
}
return this;
}

/**
* Helper method to pre-set the oauth access token of the first oauth found in the apiAuthorizations (there should be only one)
* @param accessToken Access token
* @return ApiClient
*/
public ExplorerApiClient setAccessToken(String accessToken) {
return this;
}

/**
* Adds an authorization to be used by the client
* @param authName Authentication name
* @param authorization Authorization interceptor
* @return ApiClient
*/
public ExplorerApiClient addAuthorization(String authName, Interceptor authorization) {
if (apiAuthorizations.containsKey(authName)) {
throw new RuntimeException("auth name \"" + authName + "\" already in api authorizations");
}
apiAuthorizations.put(authName, authorization);
okBuilder.addInterceptor(authorization);
return this;
}

public Map<String, Interceptor> getApiAuthorizations() {
return apiAuthorizations;
}

public ExplorerApiClient setApiAuthorizations(Map<String, Interceptor> apiAuthorizations) {
this.apiAuthorizations = apiAuthorizations;
return this;
}

public Retrofit.Builder getAdapterBuilder() {
return adapterBuilder;
}
Expand All @@ -203,20 +92,21 @@ public OkHttpClient.Builder getOkBuilder() {
return okBuilder;
}

public void addAuthsToOkBuilder(OkHttpClient.Builder okBuilder) {
for(Interceptor apiAuthorization : apiAuthorizations.values()) {
okBuilder.addInterceptor(apiAuthorization);
}
}

/**
* Clones the okBuilder given in parameter, adds the auth interceptors and uses it to configure the Retrofit
* @param okClient An instance of OK HTTP client
*/
public void configureFromOkclient(OkHttpClient okClient) {
this.okBuilder = okClient.newBuilder();
addAuthsToOkBuilder(this.okBuilder);
}

/**
* Uses the okBuilder given in parameter, adds the auth interceptors and uses it to configure the Retrofit
* @param okClientBuilder An instance of OK HTTP client builder
*/
public void configureFromOkClientBuilder(OkHttpClient.Builder okClientBuilder) {
this.okBuilder = okClientBuilder;
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import com.google.gson.Gson;
import com.google.gson.JsonParseException;
import com.google.gson.JsonElement;

import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.RequestBody;
Expand All @@ -21,8 +21,6 @@
import java.text.DateFormat;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.HashMap;
import java.net.Proxy;

public class ApiClient {

Expand Down Expand Up @@ -109,24 +107,6 @@ public void createDefaultAdapter() {
.addConverterFactory(GsonCustomConverterFactory.create(json.getGson()));
}

public void createDefaultAdapter(Proxy proxy) {
json = new JSON();
okBuilder = new OkHttpClient.Builder();

if (proxy != null) {
okBuilder.proxy(proxy);
}

if (!_hostUrl.endsWith("/"))
_hostUrl = _hostUrl + "/";

adapterBuilder = new Retrofit
.Builder()
.baseUrl(_hostUrl)
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonCustomConverterFactory.create(json.getGson()));
}

public <S> S createService(Class<S> serviceClass) {
return adapterBuilder
.client(okBuilder.build())
Expand Down Expand Up @@ -245,9 +225,17 @@ public void addAuthsToOkBuilder(OkHttpClient.Builder okBuilder) {
* @param okClient An instance of OK HTTP client
*/
public void configureFromOkclient(OkHttpClient okClient) {
this.okBuilder = okClient.newBuilder();
addAuthsToOkBuilder(this.okBuilder);
configureFromOkClientBuilder(okClient.newBuilder());
}

/**
* Uses the okBuilder given in parameter, adds the auth interceptors and uses it to configure the Retrofit
* @param okClientBuilder An instance of OK HTTP client builder
*/
public void configureFromOkClientBuilder(OkHttpClient.Builder okClientBuilder) {
this.okBuilder = okClientBuilder;
addAuthsToOkBuilder(this.okBuilder);
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@ public class NodeAndExplorerDataSourceImpl implements BlockchainDataSource {

public NodeAndExplorerDataSourceImpl(ApiClient nodeClient, @Nullable ExplorerApiClient explorerClient) {

OkHttpClient _ok = nodeClient.getOkBuilder().build();
OkHttpClient ok = nodeClient.getOkBuilder().build();
Retrofit nodeRetrofit = nodeClient.getAdapterBuilder()
.client(_ok)
.client(ok)
.build();

nodeInfoApi = nodeRetrofit.create(InfoApi.class);
Expand All @@ -72,10 +72,10 @@ public NodeAndExplorerDataSourceImpl(ApiClient nodeClient, @Nullable ExplorerApi

if (explorerClient != null) {
OkHttpClient okExplorer = explorerClient.getOkBuilder().build();
Retrofit _retrofitExplorer = explorerClient.getAdapterBuilder()
Retrofit retrofitExplorer = explorerClient.getAdapterBuilder()
.client(okExplorer)
.build();
explorerApi = _retrofitExplorer.create(DefaultApi.class);
explorerApi = retrofitExplorer.create(DefaultApi.class);
} else
explorerApi = null;

Expand Down