diff --git a/duniter4j-client/src/main/filtered-resources/log4j.properties b/duniter4j-client/src/main/filtered-resources/log4j.properties index ad5e6c445328789634532fb9fbdeb5111cc9fa29..9fadfc719442bcb25c61e00ba76c550b041aaa37 100644 --- a/duniter4j-client/src/main/filtered-resources/log4j.properties +++ b/duniter4j-client/src/main/filtered-resources/log4j.properties @@ -27,4 +27,7 @@ log4j.logger.org.apache.http=ERROR log4j.logger.org.nuiton.util=WARN log4j.logger.org.nuiton.config=WARN log4j.logger.org.nuiton.converter=WARN -log4j.logger.org.nuiton.i18n=ERROR \ No newline at end of file +log4j.logger.org.nuiton.i18n=ERROR + +# Http client connection debug +#log4j.logger.org.apache.http.impl.conn=DEBUG \ No newline at end of file diff --git a/duniter4j-client/src/main/java/org/duniter/client/actions/NetworkAction.java b/duniter4j-client/src/main/java/org/duniter/client/actions/NetworkAction.java index ccf311b3f9fdd32c036db2c93afacefb4a53c085..51574c1e584c3f2621eabcf8ac51f8673302afdc 100644 --- a/duniter4j-client/src/main/java/org/duniter/client/actions/NetworkAction.java +++ b/duniter4j-client/src/main/java/org/duniter/client/actions/NetworkAction.java @@ -94,8 +94,10 @@ public class NetworkAction extends AbstractAction { NetworkService service = ServiceLocator.instance().getNetworkService(); if (!autoRefresh) { + Long now = System.currentTimeMillis(); List<Peer> peers = service.getPeers(mainPeer); showPeersTable(peers, false); + log.info(I18n.t("duniter4j.client.network.executionTime", -System.currentTimeMillis() - now)); } else { service.addPeersChangeListener(mainPeer, peers -> showPeersTable(peers, true)); diff --git a/duniter4j-client/src/main/resources/i18n/duniter4j-client_en_GB.properties b/duniter4j-client/src/main/resources/i18n/duniter4j-client_en_GB.properties index e161ce0fb1fdd770a4208f13de9e97195a58c051..25bd1dd4332cc5c8162cb97d5a53d4e8fe101415 100644 --- a/duniter4j-client/src/main/resources/i18n/duniter4j-client_en_GB.properties +++ b/duniter4j-client/src/main/resources/i18n/duniter4j-client_en_GB.properties @@ -3,6 +3,7 @@ duniter4j.client.info.peer.fallback=Fallback to default Duniter peer\: [%s\:%d] duniter4j.client.network.action=Display network peers duniter4j.client.network.cesiumPlus=Cs+ duniter4j.client.network.error.outputFieNotWritable=Output file not writable +duniter4j.client.network.executionTime=Execution time\: %s ms duniter4j.client.network.header=Main block [%1$s] computed at [%2$s] validated by [%3$3.2f%%] of peers duniter4j.client.network.loadingPeers=Reading network peers... duniter4j.client.network.mirror=Mirror diff --git a/duniter4j-client/src/main/resources/i18n/duniter4j-client_fr_FR.properties b/duniter4j-client/src/main/resources/i18n/duniter4j-client_fr_FR.properties index 0e5a4cb730831dd2df1b544677d7b1a345dc45bb..dc53bef7f855b0b67a890dc8e39cc4b5bb1b26e8 100644 --- a/duniter4j-client/src/main/resources/i18n/duniter4j-client_fr_FR.properties +++ b/duniter4j-client/src/main/resources/i18n/duniter4j-client_fr_FR.properties @@ -3,6 +3,7 @@ duniter4j.client.info.peer.fallback=Noeud Duniter (par défaut) \: [%s\:%d] duniter4j.client.network.action=Afficher les noeuds Duniter duniter4j.client.network.cesiumPlus=Cs+ duniter4j.client.network.error.outputFieNotWritable=Fichier de sortie non inscriptible +duniter4j.client.network.executionTime=Temps d'execution \: %s ms duniter4j.client.network.header=Bloc principal [%1$s] calculé à [%2$s] validé par [%3$3.2f%%] des noeuds duniter4j.client.network.loadingPeers=Lecture des noeuds du réseau... duniter4j.client.network.mirror=Mirroir diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/BlockchainBlock.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/BlockchainBlock.java index cf0977ce6102b504aff828b19b0f5dbdcfdd2230..cc8db5de2ef7a6652303f14eac57b97ddcae238c 100644 --- a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/BlockchainBlock.java +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/BlockchainBlock.java @@ -41,6 +41,17 @@ import java.math.BigInteger; @JsonIgnoreProperties(ignoreUnknown=true) public class BlockchainBlock implements Serializable { + public static final String PROPERTY_NUMBER = "number"; + public static final String PROPERTY_DIVIDEND = "dividend"; + public static final String PROPERTY_IDENTITIES = "identities"; + public static final String PROPERTY_JOINERS = "joiners"; + public static final String PROPERTY_ACTIVES = "actives"; + public static final String PROPERTY_LEAVERS = "leavers"; + public static final String PROPERTY_REVOKED = "revoked"; + public static final String PROPERTY_EXCLUDED = "excluded"; + public static final String PROPERTY_MEDIAN_TIME = "medianTime"; + + private static final long serialVersionUID = -5598140972293452669L; private Integer version; diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/HttpServiceImpl.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/HttpServiceImpl.java index 4491bae080f5fcc967f1ada63197a273a9850ade..c63df3006872929b8bf33a6832e9f60171085a15 100644 --- a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/HttpServiceImpl.java +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/HttpServiceImpl.java @@ -26,48 +26,35 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Charsets; import com.google.common.base.Joiner; -import org.apache.http.HttpEntityEnclosingRequest; -import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.client.HttpClient; -import org.apache.http.client.HttpRequestRetryHandler; -import org.apache.http.client.config.CookieSpecs; -import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.client.utils.URIBuilder; -import org.apache.http.config.SocketConfig; import org.apache.http.conn.ConnectTimeoutException; -import org.apache.http.conn.HttpHostConnectException; import org.apache.http.entity.ContentType; import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.apache.http.protocol.HttpContext; import org.apache.http.util.EntityUtils; import org.duniter.core.beans.InitializingBean; import org.duniter.core.client.config.Configuration; -import org.duniter.core.client.config.ConfigurationOption; import org.duniter.core.client.model.bma.Constants; import org.duniter.core.client.model.bma.Error; import org.duniter.core.client.model.bma.jackson.JacksonUtils; import org.duniter.core.client.model.local.Peer; import org.duniter.core.client.service.bma.BmaTechnicalException; import org.duniter.core.client.service.exception.*; +import org.duniter.core.client.util.http.HttpClients; import org.duniter.core.exception.BusinessException; import org.duniter.core.exception.TechnicalException; import org.duniter.core.util.ObjectUtils; import org.duniter.core.util.StringUtils; -import org.duniter.core.util.cache.SimpleCache; import org.duniter.core.util.websocket.WebsocketClientEndpoint; import org.nuiton.i18n.I18n; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.net.ssl.SSLException; import java.io.*; import java.net.*; import java.nio.charset.StandardCharsets; @@ -84,15 +71,10 @@ public class HttpServiceImpl implements HttpService, Closeable, InitializingBean public static final String URL_PEER_ALIVE = "/blockchain/parameters"; - private PoolingHttpClientConnectionManager connectionManager; - protected ObjectMapper objectMapper; protected Peer defaultPeer; private boolean debug; protected Joiner pathJoiner = Joiner.on('/'); - protected SimpleCache<Integer, RequestConfig> requestConfigCache; - protected SimpleCache<Integer, HttpClient> httpClientCache; - protected int defaultTimeout; protected Map<URI, WebsocketClientEndpoint> wsEndPoints = new HashMap<>(); @@ -115,30 +97,6 @@ public class HttpServiceImpl implements HttpService, Closeable, InitializingBean */ protected void initCaches() { Configuration config = Configuration.instance(); - int cacheTimeInMillis = config.getNetworkCacheTimeInMillis(); - defaultTimeout = config.getNetworkTimeout() > 0 ? - config.getNetworkTimeout() : - Integer.parseInt(ConfigurationOption.NETWORK_TIMEOUT.getDefaultValue()); - - requestConfigCache = new SimpleCache<Integer, RequestConfig>(cacheTimeInMillis*100) { - @Override - public RequestConfig load(Integer timeout) { - // Use config default timeout, if 0 - if (timeout <= 0) timeout = defaultTimeout; - return createRequestConfig(timeout); - } - }; - - httpClientCache = new SimpleCache<Integer, HttpClient>(cacheTimeInMillis*100) { - @Override - public HttpClient load(Integer timeout) { - return createHttpClient(timeout); - } - }; - httpClientCache.registerRemoveListener(item -> { - log.debug("Closing HttpClient..."); - closeQuietly(item); - }); } public void connect(Peer peer) throws PeerConnectionException { @@ -152,7 +110,7 @@ public class HttpServiceImpl implements HttpService, Closeable, InitializingBean HttpGet httpGet = new HttpGet(getPath(peer, URL_PEER_ALIVE)); boolean isPeerAlive; try { - isPeerAlive = executeRequest(httpClientCache.get(0/*=default timeout*/), httpGet); + isPeerAlive = executeRequest(HttpClients.getThreadHttpClient(0), httpGet); } catch(TechnicalException e) { this.defaultPeer = null; throw new PeerConnectionException(e); @@ -170,37 +128,40 @@ public class HttpServiceImpl implements HttpService, Closeable, InitializingBean @Override public void close() throws IOException { - httpClientCache.clear(); - requestConfigCache.clear(); - - if (wsEndPoints.size() != 0) { - for (WebsocketClientEndpoint clientEndPoint: wsEndPoints.values()) { - clientEndPoint.close(); - } - wsEndPoints.clear(); - } - - connectionManager.close(); + HttpClients.getThreadHttpClient() +// httpClientCache.clear(); +// requestConfigCache.clear(); +// +// if (wsEndPoints.size() != 0) { +// for (WebsocketClientEndpoint clientEndPoint: wsEndPoints.values()) { +// clientEndPoint.close(); +// } +// wsEndPoints.clear(); +// } +// +// if (connectionManager != null) { +// connectionManager.close(); +// } } public <T> T executeRequest(HttpUriRequest request, Class<? extends T> resultClass) { - return executeRequest(httpClientCache.get(0), request, resultClass); + return executeRequest(HttpClients.getThreadHttpClient(0), request, resultClass); } public <T> T executeRequest(HttpUriRequest request, Class<? extends T> resultClass, Class<?> errorClass) { - //return executeRequest(httpClientCache.get(0), request, resultClass, errorClass); - return executeRequest( createHttpClient(0), request, resultClass, errorClass); + //return executeRequest(HttpClients.getThreadHttpClient(0), request, resultClass, errorClass); + return executeRequest( HttpClients.getThreadHttpClient(0), request, resultClass, errorClass); } public <T> T executeRequest(String absolutePath, Class<? extends T> resultClass) { HttpGet httpGet = new HttpGet(getPath(absolutePath)); - return executeRequest(httpClientCache.get(0), httpGet, resultClass); + return executeRequest(HttpClients.getThreadHttpClient(0), httpGet, resultClass); } public <T> T executeRequest(Peer peer, String absolutePath, Class<? extends T> resultClass) { HttpGet httpGet = new HttpGet(peer.getUrl() + absolutePath); - return executeRequest(httpClientCache.get(0), httpGet, resultClass); + return executeRequest(HttpClients.getThreadHttpClient(0), httpGet, resultClass); } public String getPath(Peer peer, String... absolutePath) { @@ -243,94 +204,96 @@ public class HttpServiceImpl implements HttpService, Closeable, InitializingBean } } - protected PoolingHttpClientConnectionManager createConnectionManager( - int maxTotalConnections, - int maxConnectionsPerRoute, - int timeout) { - PoolingHttpClientConnectionManager connectionManager - = new PoolingHttpClientConnectionManager(); - connectionManager.setMaxTotal(maxTotalConnections); - connectionManager.setDefaultMaxPerRoute(maxConnectionsPerRoute); - connectionManager.setDefaultSocketConfig(SocketConfig.custom() - .setSoTimeout(timeout).build()); - return connectionManager; - } - - protected HttpClient createHttpClient(int timeout) { - if (connectionManager == null) { - Configuration config = Configuration.instance(); - connectionManager = createConnectionManager( - config.getNetworkMaxTotalConnections(), - config.getNetworkMaxConnectionsPerRoute(), - config.getNetworkTimeout()); - } - - return HttpClients.custom() - .setConnectionManager(connectionManager) - .setDefaultRequestConfig(requestConfigCache.get(timeout)) - .setRetryHandler(createRetryHandler(timeout)) - .build(); - } - - protected HttpRequestRetryHandler createRetryHandler(int timeout) { - if (timeout <= 0) timeout = defaultTimeout; - final int maxRetryCount = (timeout < defaultTimeout) ? 2 : 3; - return new HttpRequestRetryHandler() { - public boolean retryRequest(IOException exception, int executionCount, HttpContext context) { - - boolean retrying = true; - if (exception instanceof NoRouteToHostException) { - // Bad DNS name - retrying =false; - } - else if (exception instanceof InterruptedIOException) { - // Timeout - retrying = false; - } - else if (exception instanceof UnknownHostException) { - // Unknown host - retrying = false; - } - else if (exception instanceof SSLException) { - // SSL handshake exception - retrying = false; - } - else if (exception instanceof HttpHostConnectException) { - // Host connect error - retrying = false; - } - - if (retrying && executionCount >= maxRetryCount) { - // Do not retry if over max retry count - return false; - } - - - HttpClientContext clientContext = HttpClientContext.adapt(context); - HttpRequest request = clientContext.getRequest(); - if (!retrying) { - if (debug) log.debug("Failed request to " + request.getRequestLine() + ": " + exception.getMessage()); - return false; - } - - boolean idempotent = !(request instanceof HttpEntityEnclosingRequest); - if (idempotent) { - // Retry if the request is considered idempotent - if (debug) log.debug("Failed (but will retry) request to " + request.getRequestLine() + ": " + exception.getMessage()); - return true; - } - return false; - } - }; - } - - protected RequestConfig createRequestConfig(int timeout) { - return RequestConfig.custom() - .setSocketTimeout(timeout).setConnectTimeout(timeout) - .setMaxRedirects(1) - .setCookieSpec(CookieSpecs.IGNORE_COOKIES) - .build(); - } +// protected PoolingHttpClientConnectionManager createConnectionManager( +// int maxTotalConnections, +// int maxConnectionsPerRoute, +// int timeout) { +// PoolingHttpClientConnectionManager connectionManager +// = new PoolingHttpClientConnectionManager(); +// connectionManager.setMaxTotal(maxTotalConnections); +// connectionManager.setDefaultMaxPerRoute(maxConnectionsPerRoute); +// connectionManager.setDefaultSocketConfig(SocketConfig.custom() +// .setSoTimeout(timeout).build()); +// return connectionManager; +// } +// +// protected HttpClient createHttpClient(int timeout) { +// if (connectionManager == null) { +// Configuration config = Configuration.instance(); +// connectionManager = createConnectionManager( +// config.getNetworkMaxTotalConnections(), +// config.getNetworkMaxConnectionsPerRoute(), +// config.getNetworkTimeout()); +// } +// +// return HttpClients.custom() +// .setConnectionManager(connectionManager) +// .setDefaultRequestConfig(requestConfigCache.get(timeout)) +// .setRetryHandler(httpRetryHandlerCache.get(timeout)) +// .build(); +// } +// +// protected HttpRequestRetryHandler createRetryHandler(int timeout) { +// if (timeout <= 0) timeout = defaultTimeout; +// final int maxRetryCount = (timeout < defaultTimeout) ? 2 : 3; +// return new HttpRequestRetryHandler() { +// public boolean retryRequest(IOException exception, int executionCount, HttpContext context) { +// +// log.warn("Failed request: " + exception.getMessage()); +// +// boolean retrying = true; +// if (exception instanceof NoRouteToHostException) { +// // Bad DNS name +// retrying =false; +// } +// else if (exception instanceof InterruptedIOException) { +// // Timeout +// retrying = false; +// } +// else if (exception instanceof UnknownHostException) { +// // Unknown host +// retrying = false; +// } +// else if (exception instanceof SSLException) { +// // SSL handshake exception +// retrying = false; +// } +// else if (exception instanceof HttpHostConnectException) { +// // Host connect error +// retrying = false; +// } +// +// if (retrying && executionCount >= maxRetryCount) { +// // Do not retry if over max retry count +// return false; +// } +// +// +// HttpClientContext clientContext = HttpClientContext.adapt(context); +// HttpRequest request = clientContext.getRequest(); +// if (!retrying) { +// if (debug) log.debug("Failed request to " + request.getRequestLine() + ": " + exception.getMessage()); +// return false; +// } +// +// boolean idempotent = !(request instanceof HttpEntityEnclosingRequest); +// if (idempotent) { +// // Retry if the request is considered idempotent +// if (debug) log.debug("Failed (but will retry) request to " + request.getRequestLine() + ": " + exception.getMessage()); +// return true; +// } +// return false; +// } +// }; +// } +// +// protected RequestConfig createRequestConfig(int timeout) { +// return RequestConfig.custom() +// .setSocketTimeout(timeout).setConnectTimeout(timeout) +// .setMaxRedirects(1) +// .setCookieSpec(CookieSpecs.IGNORE_COOKIES) +// .build(); +// } protected <T> T executeRequest(HttpClient httpClient, HttpUriRequest request, Class<? extends T> resultClass) { return executeRequest(httpClient, request, resultClass, Error.class); diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/local/NetworkServiceImpl.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/local/NetworkServiceImpl.java index c042280affe3935a43fb07ff4122e6c2e8ea5d97..3d92ee1dc1b68dbb70dcca2d2704c9dc5eeeb559 100644 --- a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/local/NetworkServiceImpl.java +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/local/NetworkServiceImpl.java @@ -115,8 +115,6 @@ public class NetworkServiceImpl extends BaseRemoteServiceImpl implements Network @Override public List<Peer> getPeers(final Peer mainPeer, Filter filter, Sort sort) { - //int availableProcessors = Math.min(32, Runtime.getRuntime().availableProcessors()); - //ExecutorService pool = new ScheduledThreadPoolExecutor(availableProcessors); return getPeers(mainPeer, filter, sort, null); } @@ -148,7 +146,7 @@ public class NetworkServiceImpl extends BaseRemoteServiceImpl implements Network @Override public Comparator<Peer> peerComparator(final Sort sort) { - return Comparator.comparing(peer -> computePeerStatsScore(peer, sort), (score1, score2) -> score2.compareTo(score1)); + return Comparator.comparing(peer -> computePeerStatsScore(peer, sort), Comparator.reverseOrder()); } @Override diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/util/http/HttpClients.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/util/http/HttpClients.java new file mode 100644 index 0000000000000000000000000000000000000000..70d7a6608ed3ef1c830727b1f9ad13d66d6e4081 --- /dev/null +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/util/http/HttpClients.java @@ -0,0 +1,192 @@ +package org.duniter.core.client.util.http; + +import org.apache.http.HttpEntityEnclosingRequest; +import org.apache.http.HttpRequest; +import org.apache.http.client.HttpClient; +import org.apache.http.client.HttpRequestRetryHandler; +import org.apache.http.client.config.CookieSpecs; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.protocol.HttpClientContext; +import org.apache.http.config.SocketConfig; +import org.apache.http.conn.HttpClientConnectionManager; +import org.apache.http.conn.HttpHostConnectException; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.http.protocol.HttpContext; +import org.duniter.core.client.config.Configuration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.net.ssl.SSLException; +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.NoRouteToHostException; +import java.net.UnknownHostException; + +public abstract class HttpClients { + + private static final Logger log = LoggerFactory.getLogger(HttpClients.class); + + private static ThreadLocal<HttpClientConnectionManager> connectionManagerMapper = new ThreadLocal<HttpClientConnectionManager>() { + @Override + public HttpClientConnectionManager initialValue() { + Configuration config = Configuration.instance(); + + return createConnectionManager( + config.getNetworkMaxTotalConnections(), + config.getNetworkMaxConnectionsPerRoute(), + config.getNetworkTimeout()); + } + }; + + private static ThreadLocal<HttpClient> httpClientsMapper = new ThreadLocal<HttpClient>() { + @Override + public HttpClient initialValue() { + HttpClientConnectionManager connectionManager= connectionManagerMapper.get(); + return createHttpClient(connectionManager, 0); + } + }; + + public static HttpClient getThreadHttpClient(final Integer timeout) { + if (timeout <= 0) return getThreadHttpClient(); + + final HttpClientConnectionManager connectionManager = connectionManagerMapper.get(); + return createHttpClient(connectionManager, timeout); + } + + public static HttpClient getThreadHttpClient() { + return httpClientsMapper.get(); + } + + /** + * Remlove client from the thread + */ + public static void remove() { + connectionManagerMapper.remove(); + httpClientsMapper.remove(); + } + + protected static HttpClient createHttpClient(HttpClientConnectionManager connectionManager, int timeout) { + if (timeout <= 0) { + Configuration config = Configuration.instance(); + timeout = config.getNetworkTimeout(); + } + + return org.apache.http.impl.client.HttpClients.custom() + .setConnectionManager(connectionManager) + .setDefaultRequestConfig(createRequestConfig(timeout)) + .setRetryHandler(createRetryHandler(timeout)) + .build(); + } + + + protected static PoolingHttpClientConnectionManager createConnectionManager( + int maxTotalConnections, + int maxConnectionsPerRoute, + int timeout) { + PoolingHttpClientConnectionManager connectionManager + = new PoolingHttpClientConnectionManager(); + connectionManager.setMaxTotal(maxTotalConnections); + connectionManager.setDefaultMaxPerRoute(maxConnectionsPerRoute); + connectionManager.setDefaultSocketConfig(SocketConfig.custom() + .setSoTimeout(timeout).build()); + return connectionManager; + } + + protected static RequestConfig createRequestConfig(int timeout) { + return RequestConfig.custom() + .setSocketTimeout(timeout).setConnectTimeout(timeout) + .setMaxRedirects(1) + .setCookieSpec(CookieSpecs.IGNORE_COOKIES) + .build(); + } + + + protected static HttpRequestRetryHandler createRetryHandler(int timeout) { + final int maxRetryCount = (timeout < 1000) ? 2 : 3; + return new HttpRequestRetryHandler() { + public boolean retryRequest(IOException exception, int executionCount, HttpContext context) { + + boolean retrying = true; + if (exception instanceof NoRouteToHostException) { + // Bad DNS name + retrying =false; + } + else if (exception instanceof InterruptedIOException) { + // Timeout + retrying = false; + } + else if (exception instanceof UnknownHostException) { + // Unknown host + retrying = false; + } + else if (exception instanceof SSLException) { + // SSL handshake exception + retrying = false; + } + else if (exception instanceof HttpHostConnectException) { + // Host connect error + retrying = false; + } + + if (retrying && executionCount >= maxRetryCount) { + // Do not retry if over max retry count + return false; + } + + if (!retrying) { + if (log.isDebugEnabled()) { + log.debug("Failed request to " + HttpClientContext.adapt(context).getRequest().getRequestLine() + ": " + exception.getMessage()); + } + return false; + } + + HttpClientContext clientContext = HttpClientContext.adapt(context); + HttpRequest request = clientContext.getRequest(); + boolean idempotent = !(request instanceof HttpEntityEnclosingRequest); + if (idempotent) { + // Retry if the request is considered idempotent + if (log.isDebugEnabled()) log.debug("Failed (but will retry) request to " + request.getRequestLine() + ": " + exception.getMessage()); + return true; + } + return false; + } + }; + } + +// +// private PoolingHttpClientConnectionManager connectionManager; +// +// private static final ThreadLocal<SimpleCache<Integer, HttpClient>> threadLocalCache = new ThreadLocal<SimpleCache<Integer, HttpClient>>() { +// @Override +// protected SimpleCache<Integer, HttpClient> initialValue() { +// int cacheTimeInMillis = 1000 * 60 * 5; // 5 min cache +// SimpleCache<Integer, HttpClient> cache = new SimpleCache<Integer, HttpClient>(cacheTimeInMillis*100) { +// @Override +// public HttpClient load(Integer timeout) { +// return createHttpClient(timeout); +// } +// }; +// cache.registerRemoveListener(item -> { +// log.debug("Closing HttpClient..."); +// closeQuietly(item); +// }); +// } +// }; +// +// +// protected static HttpClient createHttpClient(int timeout) { +// if (connectionManager == null) { +// Configuration config = Configuration.instance(); +// connectionManager = createConnectionManager( +// config.getNetworkMaxTotalConnections(), +// config.getNetworkMaxConnectionsPerRoute(), +// config.getNetworkTimeout()); +// } +// +// return org.apache.http.impl.client.HttpClients.custom() +// .setConnectionManager(connectionManager) +// .setDefaultRequestConfig(requestConfigCache.get(timeout)) +// .setRetryHandler(createRetryHandler(timeout)) +// .build(); +// } +} diff --git a/duniter4j-core-client/src/main/resources/log4j.properties b/duniter4j-core-client/src/main/resources/log4j.properties index 851ce1841f2e72d55c69d6629ccf4fb3558d4b79..4339099cfb37654969a61ecd7010ea8ad01f853b 100644 --- a/duniter4j-core-client/src/main/resources/log4j.properties +++ b/duniter4j-core-client/src/main/resources/log4j.properties @@ -11,6 +11,9 @@ log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} %5p (%c:%L) - %m%n log4j.logger.org.duniter=DEBUG log4j.logger.org.duniter.core.client.service.bma.AbstractNetworkService=WARN +# Http client connection debug +#log4j.logger.org.apache.http.impl.conn=DEBUG + log4j.appender.file=org.apache.log4j.RollingFileAppender log4j.appender.file.file=duniter4j-core-client.log log4j.appender.file.MaxFileSize=10MB diff --git a/pom.xml b/pom.xml index 7e1d4e7f0292eaa13c3ee599b7695668d6cdc7b9..b28a463c33c7b0f8cb518b2b488cf96d857d244f 100644 --- a/pom.xml +++ b/pom.xml @@ -244,7 +244,7 @@ <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> - <version>4.3.3</version> + <version>4.5.6</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId>