io.quarkus.rest.client.reactive.runtime.RestClientCDIDelegateBuilder Maven / Gradle / Ivy
package io.quarkus.rest.client.reactive.runtime;
import static io.quarkus.rest.client.reactive.runtime.Constants.DEFAULT_MAX_CHUNK_SIZE;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.HostnameVerifier;
import org.eclipse.microprofile.rest.client.ext.QueryParamStyle;
import org.jboss.resteasy.reactive.client.api.QuarkusRestClientProperties;
import io.netty.handler.codec.http.multipart.HttpPostRequestEncoder;
import io.quarkus.rest.client.reactive.QuarkusRestClientBuilder;
import io.quarkus.restclient.config.RestClientConfig;
import io.quarkus.restclient.config.RestClientsConfig;
public class RestClientCDIDelegateBuilder {
private static final String REST_URL_FORMAT = "quarkus.rest-client.%s.url";
private static final String REST_URI_FORMAT = "quarkus.rest-client.%s.uri";
private static final String NONE = "none";
private final Class jaxrsInterface;
private final String baseUriFromAnnotation;
private final String configKey;
private final RestClientsConfig configRoot;
public static T createDelegate(Class jaxrsInterface, String baseUriFromAnnotation, String configKey) {
return new RestClientCDIDelegateBuilder(jaxrsInterface, baseUriFromAnnotation, configKey).build();
}
private RestClientCDIDelegateBuilder(Class jaxrsInterface, String baseUriFromAnnotation, String configKey) {
this(jaxrsInterface, baseUriFromAnnotation, configKey, RestClientsConfig.getInstance());
}
RestClientCDIDelegateBuilder(Class jaxrsInterface, String baseUriFromAnnotation, String configKey,
RestClientsConfig configRoot) {
this.jaxrsInterface = jaxrsInterface;
this.baseUriFromAnnotation = baseUriFromAnnotation;
this.configKey = configKey;
this.configRoot = configRoot;
}
private T build() {
QuarkusRestClientBuilder builder = QuarkusRestClientBuilder.newBuilder();
configureBuilder(builder);
return builder.build(jaxrsInterface);
}
void configureBuilder(QuarkusRestClientBuilder builder) {
configureBaseUrl(builder);
configureTimeouts(builder);
configureProviders(builder);
configureSsl(builder);
configureRedirects(builder);
configureQueryParamStyle(builder);
configureProxy(builder);
configureShared(builder);
configureCustomProperties(builder);
}
private void configureCustomProperties(QuarkusRestClientBuilder builder) {
Optional encoder = configRoot.multipartPostEncoderMode;
if (encoder != null && encoder.isPresent()) {
HttpPostRequestEncoder.EncoderMode mode = HttpPostRequestEncoder.EncoderMode
.valueOf(encoder.get().toUpperCase(Locale.ROOT));
builder.property(QuarkusRestClientProperties.MULTIPART_ENCODER_MODE, mode);
}
Optional poolSize = oneOf(clientConfigByClassName().connectionPoolSize,
clientConfigByConfigKey().connectionPoolSize, configRoot.connectionPoolSize);
if (poolSize.isPresent()) {
builder.property(QuarkusRestClientProperties.CONNECTION_POOL_SIZE, poolSize.get());
}
Optional connectionTTL = oneOf(clientConfigByClassName().connectionTTL,
clientConfigByConfigKey().connectionTTL, configRoot.connectionTTL);
if (connectionTTL.isPresent()) {
// configuration bean contains value in milliseconds
int connectionTTLSeconds = connectionTTL.get() / 1000;
builder.property(QuarkusRestClientProperties.CONNECTION_TTL, connectionTTLSeconds);
}
Optional keepAliveEnabled = oneOf(clientConfigByClassName().keepAliveEnabled,
clientConfigByConfigKey().keepAliveEnabled, configRoot.keepAliveEnabled);
if (keepAliveEnabled.isPresent()) {
builder.property(QuarkusRestClientProperties.KEEP_ALIVE_ENABLED, keepAliveEnabled.get());
}
Map headers = clientConfigByClassName().headers;
if (headers == null || headers.isEmpty()) {
headers = clientConfigByConfigKey().headers;
}
if (headers == null || headers.isEmpty()) {
headers = configRoot.headers;
}
if (headers != null && !headers.isEmpty()) {
builder.property(QuarkusRestClientProperties.STATIC_HEADERS, headers);
}
builder.property(QuarkusRestClientProperties.DISABLE_CONTEXTUAL_ERROR_MESSAGES,
configRoot.disableContextualErrorMessages);
Optional userAgent = oneOf(clientConfigByClassName().userAgent,
clientConfigByConfigKey().userAgent, configRoot.userAgent);
if (userAgent.isPresent()) {
builder.property(QuarkusRestClientProperties.USER_AGENT, userAgent.get());
}
Optional maxChunkSize = oneOf(clientConfigByClassName().multipart.maxChunkSize,
clientConfigByConfigKey().multipart.maxChunkSize, configRoot.multipart.maxChunkSize);
builder.property(QuarkusRestClientProperties.MAX_CHUNK_SIZE, maxChunkSize.orElse(DEFAULT_MAX_CHUNK_SIZE));
Boolean http2 = oneOf(clientConfigByClassName().http2,
clientConfigByConfigKey().http2).orElse(configRoot.http2);
builder.property(QuarkusRestClientProperties.HTTP2, http2);
Optional alpn = oneOf(clientConfigByClassName().alpn,
clientConfigByConfigKey().alpn, configRoot.alpn);
if (alpn.isPresent()) {
builder.property(QuarkusRestClientProperties.ALPN, alpn.get());
}
Boolean captureStacktrace = oneOf(clientConfigByClassName().captureStacktrace,
clientConfigByConfigKey().captureStacktrace).orElse(configRoot.captureStacktrace);
builder.property(QuarkusRestClientProperties.CAPTURE_STACKTRACE, captureStacktrace);
}
private void configureProxy(QuarkusRestClientBuilder builder) {
Optional maybeProxy = oneOf(clientConfigByClassName().proxyAddress, clientConfigByConfigKey().proxyAddress,
configRoot.proxyAddress);
if (maybeProxy.isEmpty()) {
return;
}
String proxyAddress = maybeProxy.get();
if (proxyAddress.equals("none")) {
builder.proxyAddress("none", 0);
} else {
ProxyAddressUtil.HostAndPort hostAndPort = ProxyAddressUtil.parseAddress(proxyAddress);
builder.proxyAddress(hostAndPort.host, hostAndPort.port);
oneOf(clientConfigByClassName().proxyUser, clientConfigByConfigKey().proxyUser, configRoot.proxyUser)
.ifPresent(builder::proxyUser);
oneOf(clientConfigByClassName().proxyPassword, clientConfigByConfigKey().proxyPassword, configRoot.proxyPassword)
.ifPresent(builder::proxyPassword);
oneOf(clientConfigByClassName().nonProxyHosts, clientConfigByConfigKey().nonProxyHosts, configRoot.nonProxyHosts)
.ifPresent(builder::nonProxyHosts);
}
}
private void configureQueryParamStyle(QuarkusRestClientBuilder builder) {
Optional maybeQueryParamStyle = oneOf(clientConfigByClassName().queryParamStyle,
clientConfigByConfigKey().queryParamStyle, configRoot.queryParamStyle);
if (maybeQueryParamStyle.isPresent()) {
QueryParamStyle queryParamStyle = maybeQueryParamStyle.get();
builder.queryParamStyle(queryParamStyle);
}
}
private void configureRedirects(QuarkusRestClientBuilder builder) {
Optional maxRedirects = oneOf(clientConfigByClassName().maxRedirects,
clientConfigByConfigKey().maxRedirects, configRoot.maxRedirects);
if (maxRedirects.isPresent()) {
builder.property(QuarkusRestClientProperties.MAX_REDIRECTS, maxRedirects.get());
}
Optional maybeFollowRedirects = oneOf(clientConfigByClassName().followRedirects,
clientConfigByConfigKey().followRedirects, configRoot.followRedirects);
if (maybeFollowRedirects.isPresent()) {
builder.followRedirects(maybeFollowRedirects.get());
}
}
private void configureShared(QuarkusRestClientBuilder builder) {
Optional shared = oneOf(clientConfigByClassName().shared,
clientConfigByConfigKey().shared);
if (shared.isPresent()) {
builder.property(QuarkusRestClientProperties.SHARED, shared.get());
if (shared.get()) {
// Name is only used if shared = true
Optional name = oneOf(clientConfigByClassName().name,
clientConfigByConfigKey().name);
if (name.isPresent()) {
builder.property(QuarkusRestClientProperties.NAME, name.get());
}
}
}
}
private void configureSsl(QuarkusRestClientBuilder builder) {
Optional maybeTrustStore = oneOf(clientConfigByClassName().trustStore, clientConfigByConfigKey().trustStore,
configRoot.trustStore);
if (maybeTrustStore.isPresent() && !maybeTrustStore.get().isBlank() && !NONE.equals(maybeTrustStore.get())) {
registerTrustStore(maybeTrustStore.get(), builder);
}
Optional maybeKeyStore = oneOf(clientConfigByClassName().keyStore, clientConfigByConfigKey().keyStore,
configRoot.keyStore);
if (maybeKeyStore.isPresent() && !maybeKeyStore.get().isBlank() && !NONE.equals(maybeKeyStore.get())) {
registerKeyStore(maybeKeyStore.get(), builder);
}
Optional maybeHostnameVerifier = oneOf(clientConfigByClassName().hostnameVerifier,
clientConfigByConfigKey().hostnameVerifier, configRoot.hostnameVerifier);
if (maybeHostnameVerifier.isPresent()) {
registerHostnameVerifier(maybeHostnameVerifier.get(), builder);
}
oneOf(clientConfigByClassName().verifyHost, clientConfigByConfigKey().verifyHost, configRoot.verifyHost)
.ifPresent(builder::verifyHost);
}
private void registerHostnameVerifier(String verifier, QuarkusRestClientBuilder builder) {
try {
Class> verifierClass = Thread.currentThread().getContextClassLoader().loadClass(verifier);
builder.hostnameVerifier((HostnameVerifier) verifierClass.getDeclaredConstructor().newInstance());
} catch (NoSuchMethodException e) {
throw new RuntimeException(
"Could not find a public, no-argument constructor for the hostname verifier class " + verifier, e);
} catch (ClassNotFoundException e) {
throw new RuntimeException("Could not find hostname verifier class " + verifier, e);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(
"Failed to instantiate hostname verifier class " + verifier
+ ". Make sure it has a public, no-argument constructor",
e);
} catch (ClassCastException e) {
throw new RuntimeException("The provided hostname verifier " + verifier + " is not an instance of HostnameVerifier",
e);
}
}
private void registerKeyStore(String keyStorePath, QuarkusRestClientBuilder builder) {
Optional keyStorePassword = oneOf(clientConfigByClassName().keyStorePassword,
clientConfigByConfigKey().keyStorePassword, configRoot.keyStorePassword);
Optional keyStoreType = oneOf(clientConfigByClassName().keyStoreType,
clientConfigByConfigKey().keyStoreType, configRoot.keyStoreType);
try {
KeyStore keyStore = KeyStore.getInstance(keyStoreType.orElse("JKS"));
if (keyStorePassword.isEmpty()) {
throw new IllegalArgumentException("No password provided for keystore");
}
String password = keyStorePassword.get();
try (InputStream input = locateStream(keyStorePath)) {
keyStore.load(input, password.toCharArray());
} catch (IOException | CertificateException | NoSuchAlgorithmException e) {
throw new IllegalArgumentException("Failed to initialize trust store from classpath resource " + keyStorePath,
e);
}
builder.keyStore(keyStore, password);
} catch (KeyStoreException e) {
throw new IllegalArgumentException("Failed to initialize trust store from " + keyStorePath, e);
}
}
private void registerTrustStore(String trustStorePath, QuarkusRestClientBuilder builder) {
Optional maybeTrustStorePassword = oneOf(clientConfigByClassName().trustStorePassword,
clientConfigByConfigKey().trustStorePassword, configRoot.trustStorePassword);
Optional maybeTrustStoreType = oneOf(clientConfigByClassName().trustStoreType,
clientConfigByConfigKey().trustStoreType, configRoot.trustStoreType);
try {
KeyStore trustStore = KeyStore.getInstance(maybeTrustStoreType.orElse("JKS"));
if (maybeTrustStorePassword.isEmpty()) {
throw new IllegalArgumentException("No password provided for truststore");
}
String password = maybeTrustStorePassword.get();
try (InputStream input = locateStream(trustStorePath)) {
trustStore.load(input, password.toCharArray());
} catch (IOException | CertificateException | NoSuchAlgorithmException e) {
throw new IllegalArgumentException("Failed to initialize trust store from classpath resource " + trustStorePath,
e);
}
builder.trustStore(trustStore, password);
} catch (KeyStoreException e) {
throw new IllegalArgumentException("Failed to initialize trust store from " + trustStorePath, e);
}
}
private InputStream locateStream(String path) throws FileNotFoundException {
if (path.startsWith("classpath:")) {
path = path.replaceFirst("classpath:", "");
InputStream resultStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
if (resultStream == null) {
resultStream = getClass().getResourceAsStream(path);
}
if (resultStream == null) {
throw new IllegalArgumentException(
"Classpath resource " + path + " not found for MicroProfile Rest Client SSL configuration");
}
return resultStream;
} else {
if (path.startsWith("file:")) {
path = path.replaceFirst("file:", "");
}
File certificateFile = new File(path);
if (!certificateFile.isFile()) {
throw new IllegalArgumentException(
"Certificate file: " + path + " not found for MicroProfile Rest Client SSL configuration");
}
return new FileInputStream(certificateFile);
}
}
private void configureProviders(QuarkusRestClientBuilder builder) {
Optional maybeProviders = oneOf(clientConfigByClassName().providers, clientConfigByConfigKey().providers,
configRoot.providers);
if (maybeProviders.isPresent()) {
registerProviders(builder, maybeProviders.get());
}
}
private void registerProviders(QuarkusRestClientBuilder builder, String providersAsString) {
for (String s : providersAsString.split(",")) {
builder.register(providerClassForName(s.trim()));
}
}
private Class> providerClassForName(String name) {
try {
return Class.forName(name, true, Thread.currentThread().getContextClassLoader());
} catch (ClassNotFoundException e) {
throw new RuntimeException("Could not find provider class: " + name);
}
}
private void configureTimeouts(QuarkusRestClientBuilder builder) {
Long connectTimeout = oneOf(clientConfigByClassName().connectTimeout,
clientConfigByConfigKey().connectTimeout).orElse(this.configRoot.connectTimeout);
if (connectTimeout != null) {
builder.connectTimeout(connectTimeout, TimeUnit.MILLISECONDS);
}
Long readTimeout = oneOf(clientConfigByClassName().readTimeout,
clientConfigByConfigKey().readTimeout).orElse(this.configRoot.readTimeout);
if (readTimeout != null) {
builder.readTimeout(readTimeout, TimeUnit.MILLISECONDS);
}
}
private void configureBaseUrl(QuarkusRestClientBuilder builder) {
Optional propertyOptional = oneOf(clientConfigByClassName().uri,
clientConfigByConfigKey().uri);
if (propertyOptional.isEmpty()) {
propertyOptional = oneOf(clientConfigByClassName().url,
clientConfigByConfigKey().url);
}
if (((baseUriFromAnnotation == null) || baseUriFromAnnotation.isEmpty())
&& propertyOptional.isEmpty()) {
String propertyPrefix = configKey != null ? configKey : "\"" + jaxrsInterface.getName() + "\"";
throw new IllegalArgumentException(
String.format(
"Unable to determine the proper baseUrl/baseUri. " +
"Consider registering using @RegisterRestClient(baseUri=\"someuri\"), @RegisterRestClient(configKey=\"orkey\"), "
+
"or by adding '%s' or '%s' to your Quarkus configuration",
String.format(REST_URL_FORMAT, propertyPrefix), String.format(REST_URI_FORMAT, propertyPrefix)));
}
String baseUrl = propertyOptional.orElse(baseUriFromAnnotation);
try {
builder.baseUri(new URI(baseUrl));
} catch (URISyntaxException e) {
throw new IllegalArgumentException("The value of URL was invalid " + baseUrl, e);
}
}
private RestClientConfig clientConfigByConfigKey() {
return this.configRoot.getClientConfig(configKey);
}
private RestClientConfig clientConfigByClassName() {
return this.configRoot.getClientConfig(jaxrsInterface);
}
@SafeVarargs
private static Optional oneOf(Optional... optionals) {
for (Optional o : optionals) {
if (o != null && o.isPresent()) {
return o;
}
}
return Optional.empty();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy