Fix cleaner clean reference to service before call on it - chain api (#3041)
* Fix cleaner clean reference to service before call on it - chain api Signed-off-by: vasilchev <vasil.ilchev@bosch.com> * style and comment fix Signed-off-by: vasilchev <vasil.ilchev@bosch.com> --------- Signed-off-by: vasilchev <vasil.ilchev@bosch.com>
This commit is contained in:
@@ -17,6 +17,7 @@ import java.io.OutputStream;
|
||||
import java.io.Reader;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.ref.Cleaner;
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
@@ -245,7 +246,7 @@ public class HawkbitClient {
|
||||
final HttpClientKey key = new HttpClientKey(
|
||||
url.startsWith("https://"), controller == null ? null : controller.getCertificate(), tenant.getTenantCA());
|
||||
final HttpClient httpClient = httpClient(key);
|
||||
final T service = Feign.builder()
|
||||
final T rawService = Feign.builder()
|
||||
.client(new ApacheHttp5Client(httpClient))
|
||||
.encoder(encoder)
|
||||
.decoder(decoder)
|
||||
@@ -253,8 +254,29 @@ public class HawkbitClient {
|
||||
.contract(contract)
|
||||
.requestInterceptor(requestInterceptorFn.apply(tenant, controller))
|
||||
.target(serviceType, url);
|
||||
CLEANER.register(service, key::release);
|
||||
return service;
|
||||
// JIT can consider a local proxy variable dead before the HTTP call it dispatched
|
||||
// returns, causing CLEANER to fire mid-request and close the connection pool.
|
||||
// Wrapping in a dynamic proxy and calling Reference.reachabilityFence(proxy) in
|
||||
// every method's finally block prevents that: the proxy is live for the
|
||||
// full duration of each call, so CLEANER only fires after the call completes, after 'finally' block.
|
||||
final T wrapped = wrapWithReachabilityFence(serviceType, rawService);
|
||||
CLEANER.register(wrapped, key::release);
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> T wrapWithReachabilityFence(final Class<T> serviceType, final T delegate) {
|
||||
return (T) Proxy.newProxyInstance(
|
||||
delegate.getClass().getClassLoader(), new Class<?>[] { serviceType },
|
||||
(proxy, method, args) -> {
|
||||
try {
|
||||
return method.invoke(delegate, args);
|
||||
} catch (final InvocationTargetException e) {
|
||||
throw e.getTargetException() == null ? e : e.getTargetException();
|
||||
} finally {
|
||||
Reference.reachabilityFence(proxy);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -516,6 +538,7 @@ public class HawkbitClient {
|
||||
HTTP_CLIENTS.remove(key);
|
||||
}
|
||||
try {
|
||||
log.debug("Closing http client for {} - no pointers left", key);
|
||||
closeableHttpClient.close();
|
||||
} catch (final IOException e) {
|
||||
log.error("Failed to close http client", e);
|
||||
|
||||
Reference in New Issue
Block a user