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.io.Reader;
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.ref.Cleaner;
|
import java.lang.ref.Cleaner;
|
||||||
|
import java.lang.ref.Reference;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.ParameterizedType;
|
import java.lang.reflect.ParameterizedType;
|
||||||
@@ -245,7 +246,7 @@ public class HawkbitClient {
|
|||||||
final HttpClientKey key = new HttpClientKey(
|
final HttpClientKey key = new HttpClientKey(
|
||||||
url.startsWith("https://"), controller == null ? null : controller.getCertificate(), tenant.getTenantCA());
|
url.startsWith("https://"), controller == null ? null : controller.getCertificate(), tenant.getTenantCA());
|
||||||
final HttpClient httpClient = httpClient(key);
|
final HttpClient httpClient = httpClient(key);
|
||||||
final T service = Feign.builder()
|
final T rawService = Feign.builder()
|
||||||
.client(new ApacheHttp5Client(httpClient))
|
.client(new ApacheHttp5Client(httpClient))
|
||||||
.encoder(encoder)
|
.encoder(encoder)
|
||||||
.decoder(decoder)
|
.decoder(decoder)
|
||||||
@@ -253,8 +254,29 @@ public class HawkbitClient {
|
|||||||
.contract(contract)
|
.contract(contract)
|
||||||
.requestInterceptor(requestInterceptorFn.apply(tenant, controller))
|
.requestInterceptor(requestInterceptorFn.apply(tenant, controller))
|
||||||
.target(serviceType, url);
|
.target(serviceType, url);
|
||||||
CLEANER.register(service, key::release);
|
// JIT can consider a local proxy variable dead before the HTTP call it dispatched
|
||||||
return service;
|
// 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")
|
@SuppressWarnings("unchecked")
|
||||||
@@ -516,6 +538,7 @@ public class HawkbitClient {
|
|||||||
HTTP_CLIENTS.remove(key);
|
HTTP_CLIENTS.remove(key);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
log.debug("Closing http client for {} - no pointers left", key);
|
||||||
closeableHttpClient.close();
|
closeableHttpClient.close();
|
||||||
} catch (final IOException e) {
|
} catch (final IOException e) {
|
||||||
log.error("Failed to close http client", e);
|
log.error("Failed to close http client", e);
|
||||||
|
|||||||
Reference in New Issue
Block a user