com.turbospaces.resteasy.AbstractJaxRsClient Maven / Gradle / Ivy
The newest version!
package com.turbospaces.resteasy;
import java.io.IOException;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.cloud.service.UriBasedServiceInfo;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.netflix.archaius.api.Property;
import com.turbospaces.cfg.ApplicationProperties;
import com.turbospaces.ups.UPSs;
import io.github.resilience4j.ratelimiter.RateLimiterRegistry;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.binder.cache.GuavaCacheMetrics;
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Sinks;
@Slf4j
public abstract class AbstractJaxRsClient implements InitializingBean, DisposableBean, BeanNameAware {
private final Sinks.Many> processor = Sinks.many().replay().latest();
private final Flux> flux = processor.asFlux();
private final AtomicBoolean alive = new AtomicBoolean();
protected final ApplicationProperties props;
protected final MeterRegistry meterRegistry;
protected final RateLimiterRegistry rateLimiterRegistry;
protected final CloseableHttpClient httpClient;
protected final Supplier mapper;
protected String beanName;
protected AbstractJaxRsClient(
ApplicationProperties props,
MeterRegistry meterRegistry,
RateLimiterRegistry rateLimiterRegistry,
CloseableHttpClient httpClient,
Supplier mapper) {
this.props = Objects.requireNonNull(props);
this.meterRegistry = Objects.requireNonNull(meterRegistry);
this.rateLimiterRegistry = Objects.requireNonNull(rateLimiterRegistry);
this.httpClient = Objects.requireNonNull(httpClient);
this.mapper = Objects.requireNonNull(mapper);
}
@Override
public void setBeanName(String name) {
this.beanName = name;
}
@Override
public void afterPropertiesSet() throws Exception {
LoadingCache newInstance = createCache();
processor.tryEmitNext(newInstance);
accept(newInstance);
alive.set(true);
}
@Override
public void destroy() throws Exception {
alive.set(false);
}
protected Flux> asFlux() {
return flux;
}
protected void accept(LoadingCache cache) {
}
private LoadingCache createCache() {
LoadingCache cache = CacheBuilder.newBuilder().build(new CacheLoader() {
@Override
public ResteasyWebTarget load(WebTargetConfig key) throws Exception {
subscribeOnChange(key.connectionTimeout);
subscribeOnChange(key.socketTimeout);
return newWebTarget(key.ups, key.connectionTimeout, key.socketTimeout);
}
});
GuavaCacheMetrics.monitor(meterRegistry, cache, this.getClass().getSimpleName());
return cache;
}
private void subscribeOnChange(Property property) {
property.subscribe(new Consumer() {
@Override
public void accept(Integer val) {
if (alive.get() && val > 0) {
try {
log.debug("re-creating Jax-RS client: {}, {} changed to: {}", beanName, property.getKey(), val);
processor.tryEmitNext(createCache());
} catch (Exception err) {
log.error(err.getMessage(), err);
}
}
}
});
}
private ResteasyWebTarget newWebTarget(UriBasedServiceInfo ups, Property connectionTimeout, Property socketTimeout) throws Exception {
URIBuilder uri = new URIBuilder().setScheme(ups.getScheme()).setHost(ups.getHost()).setPort(ups.getPort());
//
// path might be relevant to distinguish
//
if (StringUtils.isNotEmpty(ups.getPath())) {
uri = uri.setPath(ups.getPath());
}
JaxrsClientFactoryBean jaxrs = new JaxrsClientFactoryBean(props, rateLimiterRegistry, httpClient);
jaxrs.setMapper(mapper.get());
jaxrs.setRequestConfig(new Consumer() {
@Override
public void accept(RequestConfig.Builder cfg) {
cfg.setConnectTimeout((int) TimeUnit.SECONDS.toMillis(connectionTimeout.get()));
cfg.setSocketTimeout((int) TimeUnit.SECONDS.toMillis(socketTimeout.get()));
}
});
ResteasyClient resteasyClient = jaxrs.getObject().build();
return resteasyClient.target(uri.build());
}
public static class WebTargetConfig {
private final UriBasedServiceInfo ups;
private final Property connectionTimeout;
private final Property socketTimeout;
public WebTargetConfig(UriBasedServiceInfo ups, Property connectionTimeout, Property socketTimeout) {
this.ups = Objects.requireNonNull(ups);
this.connectionTimeout = Objects.requireNonNull(connectionTimeout);
this.socketTimeout = Objects.requireNonNull(socketTimeout);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
for (;;) {
try {
WebTargetConfig that = (WebTargetConfig) o;
return UPSs.isEquals(ups, that.ups)
&& Objects.equals(connectionTimeout.get(), that.connectionTimeout.get())
&& Objects.equals(socketTimeout.get(), that.socketTimeout.get());
} catch (IOException err) {
ExceptionUtils.wrapAndThrow(err);
}
}
}
@Override
public int hashCode() {
for (;;) {
try {
return Objects.hash(UPSs.hashCode(ups), connectionTimeout.get(), socketTimeout.get());
} catch (IOException err) {
ExceptionUtils.wrapAndThrow(err);
}
}
}
}
}