Skip to content

Commit f21324c

Browse files
committed
Initial
1 parent f1f3fc7 commit f21324c

3 files changed

Lines changed: 71 additions & 1 deletion

File tree

cloudplatform/connectivity-apache-httpclient4/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@
9696
</exclusion>
9797
</exclusions>
9898
</dependency>
99+
<dependency>
100+
<groupId>org.apache.commons</groupId>
101+
<artifactId>commons-lang3</artifactId>
102+
</dependency>
99103
<!-- scope "provided" -->
100104
<dependency>
101105
<groupId>org.projectlombok</groupId>

cloudplatform/connectivity-apache-httpclient4/src/main/java/com/sap/cloud/sdk/cloudplatform/connectivity/AbstractHttpClientCache.java

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,19 @@
66
import javax.annotation.Nonnull;
77
import javax.annotation.Nullable;
88

9+
import org.apache.commons.lang3.reflect.FieldUtils;
910
import org.apache.http.client.HttpClient;
11+
import org.apache.http.impl.client.CloseableHttpClient;
12+
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
13+
import org.apache.http.pool.AbstractConnPool;
1014

1115
import com.github.benmanes.caffeine.cache.Cache;
1216
import com.sap.cloud.sdk.cloudplatform.cache.CacheKey;
1317
import com.sap.cloud.sdk.cloudplatform.connectivity.exception.HttpClientInstantiationException;
1418

1519
import io.vavr.control.Try;
1620
import lombok.extern.slf4j.Slf4j;
21+
import lombok.val;
1722

1823
/**
1924
* Provides caching functionality to the {@code HttpClientAccessor}.
@@ -89,9 +94,16 @@ private Try<HttpClient> tryGetOrCreateHttpClient(
8994
final Cache<CacheKey, HttpClient> cache = maybeCache.get();
9095
final CacheKey cacheKey = maybeKey.get();
9196

92-
final HttpClient httpClient;
97+
HttpClient httpClient;
9398
try {
9499
httpClient = cache.get(cacheKey, anyKey -> createHttpClient.get());
100+
101+
if( !isHealthy(httpClient) ) {
102+
log.warn("The HttpClient retrieved from the cache has a shutdown connection pool.");
103+
cache.invalidate(cacheKey);
104+
httpClient = cache.get(cacheKey, anyKey -> createHttpClient.get());
105+
}
106+
95107
Objects
96108
.requireNonNull(
97109
httpClient,
@@ -109,6 +121,22 @@ private Try<HttpClient> tryGetOrCreateHttpClient(
109121
return Try.success(httpClient);
110122
}
111123

124+
private static boolean isHealthy( final HttpClient httpClient )
125+
throws IllegalArgumentException,
126+
NullPointerException
127+
{
128+
try {
129+
val hc = (CloseableHttpClient) FieldUtils.readField(httpClient, "httpClient", true);
130+
val cm = (PoolingHttpClientConnectionManager) FieldUtils.readField(hc, "connManager", true);
131+
val cp = (AbstractConnPool<?, ?, ?>) FieldUtils.readField(cm, "pool", true);
132+
return !cp.isShutdown();
133+
}
134+
catch( final Exception e ) {
135+
log.warn("Failed to access connection manager.", e);
136+
return true;
137+
}
138+
}
139+
112140
/**
113141
* Getter for the cache to be used.
114142
* <p>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.sap.cloud.sdk.cloudplatform.connectivity;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
5+
6+
import org.apache.http.client.methods.HttpHead;
7+
import org.apache.http.impl.client.CloseableHttpClient;
8+
import org.junit.jupiter.api.Test;
9+
10+
import lombok.SneakyThrows;
11+
import lombok.val;
12+
13+
public class AbstractHttpClientCacheTest
14+
{
15+
@SneakyThrows
16+
@Test
17+
void testClosedPool()
18+
{
19+
val d = DefaultHttpDestination.builder("https://sap.com").build();
20+
21+
val httpClient1 = HttpClientAccessor.getHttpClient(d);
22+
httpClient1.execute(new HttpHead());
23+
httpClient1.execute(new HttpHead());
24+
25+
((CloseableHttpClient) httpClient1).close();
26+
assertThatThrownBy(() -> httpClient1.execute(new HttpHead()))
27+
.isInstanceOf(IllegalStateException.class)
28+
.hasMessage("Connection pool shut down");
29+
assertThatThrownBy(() -> httpClient1.execute(new HttpHead()))
30+
.isInstanceOf(IllegalStateException.class)
31+
.hasMessage("Connection pool shut down");
32+
33+
val httpClient2 = HttpClientAccessor.getHttpClient(d);
34+
assertThat(httpClient1).isNotSameAs(httpClient2);
35+
httpClient2.execute(new HttpHead());
36+
httpClient2.execute(new HttpHead());
37+
}
38+
}

0 commit comments

Comments
 (0)