From 4f31e0d923853fe15fc1da7664e52e2874a8b48b Mon Sep 17 00:00:00 2001 From: Roberto Cortez Date: Wed, 24 Jul 2024 13:21:41 +0100 Subject: [PATCH] Move REST Client configuration to use @ConfigMapping --- .../deployment/RestClientConfigUtils.java | 51 ++ .../AbstractRestClientConfigBuilder.java | 40 ++ .../config/QueryParamStyleConverter.java | 11 - .../config/RegisteredRestClient.java | 29 ++ .../config/RestClientBuildConfig.java | 50 -- .../config/RestClientBuilderFactory.java | 8 +- .../restclient/config/RestClientConfig.java | 463 ------------------ ...ClientFallbackConfigSourceInterceptor.java | 10 +- .../config/RestClientLoggingConfig.java | 36 -- .../config/RestClientMultipartConfig.java | 22 - ...ntNameFallbackConfigSourceInterceptor.java | 87 ++++ .../config/RestClientsBuildTimeConfig.java | 69 ++- .../restclient/config/RestClientsConfig.java | 458 ++++++++++++----- ....eclipse.microprofile.config.spi.Converter | 1 - .../config/RestClientConfigTest.java | 80 +-- .../src/test/resources/application.properties | 3 +- .../deployment/RestClientProcessor.java | 73 +-- .../GlobalConfigurationTest.java | 49 +- .../QuarkusConfigurationTest.java | 54 +- .../RestClientConfigNotationTest.java | 42 +- .../RestClientOverrideRuntimeConfigTest.java | 4 +- .../configuration/UnknownConfigTest.java | 15 +- .../VaultScenarioRestClientConfigTest.java | 2 +- .../restclient/runtime/RestClientBase.java | 82 ++-- .../runtime/RestClientBaseTest.java | 162 +++--- .../deployment/RestClientBuildItem.java | 28 +- .../RestClientReactiveProcessor.java | 25 +- ...vServicesRestClientHttpProxyProcessor.java | 21 +- .../client/reactive/ConfigurationTest.java | 69 +-- .../reactive/GlobalConfigurationTest.java | 49 +- .../reactive/LegacyConfigurationTest.java | 19 +- .../runtime/RestClientBuilderImpl.java | 48 +- .../runtime/RestClientCDIDelegateBuilder.java | 154 +++--- .../RestClientCDIDelegateBuilderTest.java | 266 +++++----- 34 files changed, 1276 insertions(+), 1304 deletions(-) create mode 100644 extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/AbstractRestClientConfigBuilder.java delete mode 100644 extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/QueryParamStyleConverter.java create mode 100644 extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RegisteredRestClient.java delete mode 100644 extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientBuildConfig.java delete mode 100644 extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientConfig.java delete mode 100644 extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientLoggingConfig.java delete mode 100644 extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientMultipartConfig.java create mode 100644 extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientNameFallbackConfigSourceInterceptor.java delete mode 100644 extensions/resteasy-classic/rest-client-config/runtime/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.Converter diff --git a/extensions/resteasy-classic/rest-client-config/deployment/src/main/java/io/quarkus/restclient/config/deployment/RestClientConfigUtils.java b/extensions/resteasy-classic/rest-client-config/deployment/src/main/java/io/quarkus/restclient/config/deployment/RestClientConfigUtils.java index f46f0befb527d..66e1a3009690f 100644 --- a/extensions/resteasy-classic/rest-client-config/deployment/src/main/java/io/quarkus/restclient/config/deployment/RestClientConfigUtils.java +++ b/extensions/resteasy-classic/rest-client-config/deployment/src/main/java/io/quarkus/restclient/config/deployment/RestClientConfigUtils.java @@ -4,11 +4,26 @@ import static io.quarkus.restclient.config.Constants.MP_REST_SCOPE_FORMAT; import static io.quarkus.restclient.config.Constants.QUARKUS_REST_SCOPE_FORMAT; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; import org.eclipse.microprofile.config.Config; import org.jboss.jandex.ClassInfo; +import io.quarkus.deployment.GeneratedClassGizmoAdaptor; +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.builditem.GeneratedClassBuildItem; +import io.quarkus.deployment.builditem.RunTimeConfigBuilderBuildItem; +import io.quarkus.deployment.builditem.StaticInitConfigBuilderBuildItem; +import io.quarkus.gizmo.ClassCreator; +import io.quarkus.gizmo.MethodCreator; +import io.quarkus.gizmo.MethodDescriptor; +import io.quarkus.gizmo.ResultHandle; +import io.quarkus.restclient.config.AbstractRestClientConfigBuilder; +import io.quarkus.restclient.config.RegisteredRestClient; +import io.quarkus.runtime.configuration.ConfigBuilder; + public final class RestClientConfigUtils { private RestClientConfigUtils() { @@ -50,4 +65,40 @@ public static Optional getDefaultScope(Config config) { return config.getOptionalValue(GLOBAL_REST_SCOPE_FORMAT, String.class); } + public static void generateRestClientConfigBuilder( + List restClients, + BuildProducer generatedClass, + BuildProducer staticInitConfigBuilder, + BuildProducer runTimeConfigBuilder) { + + String className = "io.quarkus.runtime.generated.RestClientConfigBuilder"; + try (ClassCreator classCreator = ClassCreator.builder() + .classOutput(new GeneratedClassGizmoAdaptor(generatedClass, true)) + .className(className) + .superClass(AbstractRestClientConfigBuilder.class) + .interfaces(ConfigBuilder.class) + .setFinal(true) + .build()) { + + MethodCreator method = classCreator.getMethodCreator( + MethodDescriptor.ofMethod(AbstractRestClientConfigBuilder.class, "getRestClients", List.class)); + + ResultHandle list = method.newInstance(MethodDescriptor.ofConstructor(ArrayList.class)); + for (RegisteredRestClient restClient : restClients) { + ResultHandle restClientElement = method.newInstance( + MethodDescriptor.ofConstructor(RegisteredRestClient.class, String.class, String.class, String.class), + method.load(restClient.getFullName()), + method.load(restClient.getSimpleName()), + restClient.getConfigKey() != null ? method.load(restClient.getConfigKey()) : method.loadNull()); + + method.invokeVirtualMethod(MethodDescriptor.ofMethod(ArrayList.class, "add", boolean.class, Object.class), list, + restClientElement); + } + + method.returnValue(list); + } + + staticInitConfigBuilder.produce(new StaticInitConfigBuilderBuildItem(className)); + runTimeConfigBuilder.produce(new RunTimeConfigBuilderBuildItem(className)); + } } diff --git a/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/AbstractRestClientConfigBuilder.java b/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/AbstractRestClientConfigBuilder.java new file mode 100644 index 0000000000000..f477e6d78e5f1 --- /dev/null +++ b/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/AbstractRestClientConfigBuilder.java @@ -0,0 +1,40 @@ +package io.quarkus.restclient.config; + +import java.util.List; + +import io.quarkus.runtime.configuration.ConfigBuilder; +import io.smallrye.config.SmallRyeConfigBuilder; + +/** + * Registers and force load REST Client configuration. + *

+ * Usually, named configuration is mapped using a Map because the names are dynamic and unknown to + * Quarkus. In the case of the REST Client, configuration names are fixed and known at build time, but not to the point + * where the names can be mapped statically, so they still need to be mapped in a Map. + *

+ * To populate a Map, because the names are dynamic, the Config system has to rely on the list of + * property names provided by each source. This also applies to the REST Client, but since the names are known to + * Quarkus, the REST Client configuration could be loaded even for sources that don't provide a list of property + * names. To achieve such behaviour, we provide a dummy configuration under each REST Client name to force + * the Config system to look up the remaining configuration in the same tree. + *

+ * The concrete implementation is bytecode generated in + * io.quarkus.restclient.config.deployment.RestClientConfigUtils#generateRestClientConfigBuilder + */ +public abstract class AbstractRestClientConfigBuilder implements ConfigBuilder { + @Override + public SmallRyeConfigBuilder configBuilder(final SmallRyeConfigBuilder builder) { + List restClients = getRestClients(); + builder.withInterceptors(new RestClientNameFallbackConfigSourceInterceptor(restClients)); + for (RegisteredRestClient restClient : restClients) { + builder.withDefaultValue("quarkus.rest-client.\"" + restClient.getFullName() + "\".force", "true"); + builder.withDefaultValue("quarkus.rest-client." + restClient.getSimpleName() + ".force", "true"); + if (restClient.getConfigKey() != null) { + builder.withDefaultValue("quarkus.rest-client." + restClient.getConfigKey() + ".force", "true"); + } + } + return builder; + } + + public abstract List getRestClients(); +} diff --git a/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/QueryParamStyleConverter.java b/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/QueryParamStyleConverter.java deleted file mode 100644 index 9c25f4075291f..0000000000000 --- a/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/QueryParamStyleConverter.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.quarkus.restclient.config; - -import org.eclipse.microprofile.config.spi.Converter; -import org.eclipse.microprofile.rest.client.ext.QueryParamStyle; - -public class QueryParamStyleConverter implements Converter { - @Override - public QueryParamStyle convert(String value) throws IllegalArgumentException, NullPointerException { - return QueryParamStyle.valueOf(value); - } -} diff --git a/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RegisteredRestClient.java b/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RegisteredRestClient.java new file mode 100644 index 0000000000000..a350fcd9d8b9c --- /dev/null +++ b/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RegisteredRestClient.java @@ -0,0 +1,29 @@ +package io.quarkus.restclient.config; + +public class RegisteredRestClient { + private final String fullName; + private final String simpleName; + private final String configKey; + + public RegisteredRestClient(final String fullName, final String simpleName) { + this(fullName, simpleName, null); + } + + public RegisteredRestClient(final String fullName, final String simpleName, final String configKey) { + this.fullName = fullName; + this.simpleName = simpleName; + this.configKey = configKey; + } + + public String getFullName() { + return fullName; + } + + public String getSimpleName() { + return simpleName; + } + + public String getConfigKey() { + return configKey; + } +} diff --git a/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientBuildConfig.java b/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientBuildConfig.java deleted file mode 100644 index d24deacf25833..0000000000000 --- a/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientBuildConfig.java +++ /dev/null @@ -1,50 +0,0 @@ -package io.quarkus.restclient.config; - -import java.util.Optional; - -import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; - -import io.quarkus.runtime.annotations.ConfigGroup; -import io.quarkus.runtime.annotations.ConfigItem; - -@ConfigGroup -public class RestClientBuildConfig { - - /** - * The CDI scope to use for injection. This property can contain either a fully qualified class name of a CDI scope - * annotation (such as "jakarta.enterprise.context.ApplicationScoped") or its simple name (such as - * "ApplicationScoped"). - * By default, this is not set which means the interface is not registered as a bean unless it is annotated with - * {@link RegisterRestClient}. - * If an interface is not annotated with {@link RegisterRestClient} and this property is set, then Quarkus will make the - * interface - * a bean of the configured scope. - */ - @ConfigItem - public Optional scope; - - /** - * If set to true, then Quarkus will ensure that all calls from the rest client go through a local proxy - * server (that is managed by Quarkus). - * This can be very useful for capturing network traffic to a service that use HTTPS. - *

- * This property is not applicable to the RESTEasy Client, only the Quarkus Rest client (formerly RESTEasy Reactive client). - *

- * This property only applicable to dev and test mode. - */ - @ConfigItem(defaultValue = "false") - public boolean enableLocalProxy; - - /** - * This setting is used to select which proxy provider to use if there are multiple ones. - * It only applies if {@code enable-local-proxy} is true. - *

- * The algorithm for picking between multiple provider is the following: - *

    - *
  • If only the default is around, use it (it's name is {@code default})
  • - *
  • If there is only one besides the default, use it
  • - *
  • If there are multiple ones, fail
  • - *
- */ - public Optional localProxyProvider; -} diff --git a/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientBuilderFactory.java b/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientBuilderFactory.java index 3e9222f6f4106..0e3df7e6b6407 100644 --- a/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientBuilderFactory.java +++ b/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientBuilderFactory.java @@ -2,18 +2,22 @@ import java.util.ServiceLoader; +import org.eclipse.microprofile.config.ConfigProvider; import org.eclipse.microprofile.rest.client.RestClientBuilder; +import io.smallrye.config.SmallRyeConfig; + /** * Factory which creates MicroProfile RestClientBuilder instance configured according to current Quarkus application * configuration. - * + *

* The builder instance can be further tweaked, if needed, before building the rest client proxy. */ public interface RestClientBuilderFactory { default RestClientBuilder newBuilder(Class proxyType) { - return newBuilder(proxyType, RestClientsConfig.getInstance()); + return newBuilder(proxyType, + ConfigProvider.getConfig().unwrap(SmallRyeConfig.class).getConfigMapping(RestClientsConfig.class)); } RestClientBuilder newBuilder(Class proxyType, RestClientsConfig restClientsConfigRoot); diff --git a/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientConfig.java b/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientConfig.java deleted file mode 100644 index 3165fdb1c643c..0000000000000 --- a/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientConfig.java +++ /dev/null @@ -1,463 +0,0 @@ -package io.quarkus.restclient.config; - -import java.util.Collections; -import java.util.Map; -import java.util.Optional; - -import org.eclipse.microprofile.config.Config; -import org.eclipse.microprofile.config.ConfigProvider; -import org.eclipse.microprofile.rest.client.ext.QueryParamStyle; - -import io.quarkus.runtime.annotations.ConfigDocDefault; -import io.quarkus.runtime.annotations.ConfigDocMapKey; -import io.quarkus.runtime.annotations.ConfigGroup; -import io.quarkus.runtime.annotations.ConfigItem; -import io.quarkus.runtime.configuration.MemorySize; -import io.smallrye.config.SmallRyeConfig; - -@ConfigGroup -public class RestClientConfig { - - public static final RestClientConfig EMPTY; - - static { - EMPTY = new RestClientConfig(); - EMPTY.url = Optional.empty(); - EMPTY.uri = Optional.empty(); - EMPTY.overrideUri = Optional.empty(); - EMPTY.providers = Optional.empty(); - EMPTY.connectTimeout = Optional.empty(); - EMPTY.readTimeout = Optional.empty(); - EMPTY.followRedirects = Optional.empty(); - EMPTY.multipartPostEncoderMode = Optional.empty(); - EMPTY.proxyAddress = Optional.empty(); - EMPTY.proxyUser = Optional.empty(); - EMPTY.proxyPassword = Optional.empty(); - EMPTY.nonProxyHosts = Optional.empty(); - EMPTY.queryParamStyle = Optional.empty(); - EMPTY.verifyHost = Optional.empty(); - EMPTY.trustStore = Optional.empty(); - EMPTY.trustStorePassword = Optional.empty(); - EMPTY.trustStoreType = Optional.empty(); - EMPTY.keyStore = Optional.empty(); - EMPTY.keyStorePassword = Optional.empty(); - EMPTY.keyStoreType = Optional.empty(); - EMPTY.hostnameVerifier = Optional.empty(); - EMPTY.tlsConfigurationName = Optional.empty(); - EMPTY.connectionTTL = Optional.empty(); - EMPTY.connectionPoolSize = Optional.empty(); - EMPTY.keepAliveEnabled = Optional.empty(); - EMPTY.maxRedirects = Optional.empty(); - EMPTY.multipart = new RestClientMultipartConfig(); - EMPTY.multipart.maxChunkSize = Optional.empty(); - EMPTY.headers = Collections.emptyMap(); - EMPTY.shared = Optional.empty(); - EMPTY.name = Optional.empty(); - EMPTY.userAgent = Optional.empty(); - EMPTY.http2 = Optional.empty(); - EMPTY.maxChunkSize = Optional.empty(); - EMPTY.alpn = Optional.empty(); - EMPTY.captureStacktrace = Optional.empty(); - } - - public RestClientMultipartConfig multipart; - - /** - * The base URL to use for this service. This property or the `uri` property is considered required, unless - * the `baseUri` attribute is configured in the `@RegisterRestClient` annotation. - */ - @ConfigItem - public Optional url; - - /** - * The base URI to use for this service. This property or the `url` property is considered required, unless - * the `baseUri` attribute is configured in the `@RegisterRestClient` annotation. - */ - @ConfigItem - public Optional uri; - - /** - * This property is only meant to be set by advanced configurations to override whatever value was set for the uri or url. - * The override is done using the REST Client class name configuration syntax. - *

- * This property is not applicable to the RESTEasy Client, only the Quarkus Rest client (formerly RESTEasy Reactive client). - */ - @ConfigItem - public Optional overrideUri; - - /** - * Map where keys are fully-qualified provider classnames to include in the client, and values are their integer - * priorities. The equivalent of the `@RegisterProvider` annotation. - */ - @ConfigItem - public Optional providers; - - /** - * Timeout specified in milliseconds to wait to connect to the remote endpoint. - */ - @ConfigItem - public Optional connectTimeout; - - /** - * Timeout specified in milliseconds to wait for a response from the remote endpoint. - */ - @ConfigItem - public Optional readTimeout; - - /** - * A boolean value used to determine whether the client should follow HTTP redirect responses. - */ - @ConfigItem - public Optional followRedirects; - - /** - * Mode in which the form data are encoded. Possible values are `HTML5`, `RFC1738` and `RFC3986`. - * The modes are described in the - * Netty - * documentation - *

- * By default, Rest Client Reactive uses RFC1738. - *

- * This property is not applicable to the RESTEasy Client. - */ - @ConfigItem - public Optional multipartPostEncoderMode; - - /** - * A string value in the form of `:` that specifies the HTTP proxy server hostname - * (or IP address) and port for requests of this client to use. - * - * Use `none` to disable proxy - */ - @ConfigItem - public Optional proxyAddress; - - /** - * Proxy username. - *

- * This property is not applicable to the RESTEasy Client. - */ - @ConfigItem - public Optional proxyUser; - - /** - * Proxy password. - *

- * This property is not applicable to the RESTEasy Client. - */ - @ConfigItem - public Optional proxyPassword; - - /** - * Hosts to access without proxy - *

- * This property is not applicable to the RESTEasy Client. - */ - @ConfigItem - public Optional nonProxyHosts; - - /** - * An enumerated type string value with possible values of "MULTI_PAIRS" (default), "COMMA_SEPARATED", - * or "ARRAY_PAIRS" that specifies the format in which multiple values for the same query parameter is used. - */ - @ConfigItem - public Optional queryParamStyle; - - /** - * Set whether hostname verification is enabled. Default is enabled. - * This setting should not be disabled in production as it makes the client vulnerable to MITM attacks. - */ - @ConfigItem - public Optional verifyHost; - - /** - * The trust store location. Can point to either a classpath resource or a file. - */ - @ConfigItem - public Optional trustStore; - - /** - * The trust store password. - */ - @ConfigItem - public Optional trustStorePassword; - - /** - * The type of the trust store. Defaults to "JKS". - */ - @ConfigItem - public Optional trustStoreType; - - /** - * The key store location. Can point to either a classpath resource or a file. - */ - @ConfigItem - public Optional keyStore; - - /** - * The key store password. - */ - @ConfigItem - public Optional keyStorePassword; - - /** - * The type of the key store. Defaults to "JKS". - */ - @ConfigItem - public Optional keyStoreType; - - /** - * The class name of the host name verifier. The class must have a public no-argument constructor. - */ - @ConfigItem - public Optional hostnameVerifier; - - /** - * The name of the TLS configuration to use. - *

- * If not set and the default TLS configuration is configured ({@code quarkus.tls.*}) then that will be used. - * If a name is configured, it uses the configuration from {@code quarkus.tls..*} - * If a name is configured, but no TLS configuration is found with that name then an error will be thrown. - *

- * If no TLS configuration is set, then the keys-tore, trust-store, etc. properties will be used. - *

- * This property is not applicable to the RESTEasy Client. - */ - @ConfigItem - public Optional tlsConfigurationName; - - /** - * The time in ms for which a connection remains unused in the connection pool before being evicted and closed. - * A timeout of {@code 0} means there is no timeout. - */ - @ConfigItem - public Optional connectionTTL; - - /** - * The size of the connection pool for this client. - */ - @ConfigItem - public Optional connectionPoolSize; - - /** - * If set to false disables the keep alive completely. - */ - @ConfigItem - public Optional keepAliveEnabled; - - /** - * The maximum number of redirection a request can follow. - *

- * This property is not applicable to the RESTEasy Client. - */ - @ConfigItem - public Optional maxRedirects; - - /** - * The HTTP headers that should be applied to all requests of the rest client. - *

- * This property is not applicable to the RESTEasy Client. - */ - @ConfigItem - @ConfigDocMapKey("header-name") - public Map headers; - - /** - * Set to true to share the HTTP client between REST clients. - * There can be multiple shared clients distinguished by name, when no specific name is set, - * the name __vertx.DEFAULT is used. - *

- * This property is not applicable to the RESTEasy Client. - */ - @ConfigItem - public Optional shared; - - /** - * Set the HTTP client name, used when the client is shared, otherwise ignored. - *

- * This property is not applicable to the RESTEasy Client. - */ - @ConfigItem - public Optional name; - - /** - * Configure the HTTP user-agent header to use. - *

- * This property is not applicable to the RESTEasy Client. - */ - @ConfigItem - public Optional userAgent; - - /** - * If this is true then HTTP/2 will be enabled. - */ - @ConfigItem - public Optional http2; - - /** - * The max HTTP chunk size (8096 bytes by default). - *

- * This property is not applicable to the RESTEasy Client. - */ - @ConfigItem - @ConfigDocDefault("8K") - public Optional maxChunkSize; - - /** - * If the Application-Layer Protocol Negotiation is enabled, the client will negotiate which protocol to use over the - * protocols exposed by the server. By default, it will try to use HTTP/2 first and if it's not enabled, it will - * use HTTP/1.1. - * When the property `http2` is enabled, this flag will be automatically enabled. - */ - @ConfigItem - public Optional alpn; - - /** - * If {@code true}, the stacktrace of the invocation of the REST Client method is captured. - * This stacktrace will be used if the invocation throws an exception - */ - @ConfigItem - public Optional captureStacktrace; - - public static RestClientConfig load(String configKey) { - final RestClientConfig instance = new RestClientConfig(); - - instance.url = getConfigValue(configKey, "url", String.class); - instance.uri = getConfigValue(configKey, "uri", String.class); - instance.overrideUri = getConfigValue(configKey, "override-uri", String.class); - instance.providers = getConfigValue(configKey, "providers", String.class); - instance.connectTimeout = getConfigValue(configKey, "connect-timeout", Long.class); - instance.readTimeout = getConfigValue(configKey, "read-timeout", Long.class); - instance.followRedirects = getConfigValue(configKey, "follow-redirects", Boolean.class); - instance.multipartPostEncoderMode = getConfigValue(configKey, "multipart-post-encoder-mode", String.class); - instance.proxyAddress = getConfigValue(configKey, "proxy-address", String.class); - instance.proxyUser = getConfigValue(configKey, "proxy-user", String.class); - instance.proxyPassword = getConfigValue(configKey, "proxy-password", String.class); - instance.nonProxyHosts = getConfigValue(configKey, "non-proxy-hosts", String.class); - instance.queryParamStyle = getConfigValue(configKey, "query-param-style", QueryParamStyle.class); - instance.verifyHost = getConfigValue(configKey, "verify-host", Boolean.class); - instance.trustStore = getConfigValue(configKey, "trust-store", String.class); - instance.trustStorePassword = getConfigValue(configKey, "trust-store-password", String.class); - instance.trustStoreType = getConfigValue(configKey, "trust-store-type", String.class); - instance.keyStore = getConfigValue(configKey, "key-store", String.class); - instance.keyStorePassword = getConfigValue(configKey, "key-store-password", String.class); - instance.keyStoreType = getConfigValue(configKey, "key-store-type", String.class); - instance.hostnameVerifier = getConfigValue(configKey, "hostname-verifier", String.class); - instance.tlsConfigurationName = getConfigValue(configKey, "tls-configuration-name", String.class); - instance.connectionTTL = getConfigValue(configKey, "connection-ttl", Integer.class); - instance.connectionPoolSize = getConfigValue(configKey, "connection-pool-size", Integer.class); - instance.keepAliveEnabled = getConfigValue(configKey, "keep-alive-enabled", Boolean.class); - instance.maxRedirects = getConfigValue(configKey, "max-redirects", Integer.class); - instance.headers = getConfigValues(configKey, "headers", String.class, String.class); - instance.shared = getConfigValue(configKey, "shared", Boolean.class); - instance.name = getConfigValue(configKey, "name", String.class); - instance.userAgent = getConfigValue(configKey, "user-agent", String.class); - instance.http2 = getConfigValue(configKey, "http2", Boolean.class); - instance.maxChunkSize = getConfigValue(configKey, "max-chunk-size", MemorySize.class); - instance.alpn = getConfigValue(configKey, "alpn", Boolean.class); - instance.captureStacktrace = getConfigValue(configKey, "capture-stacktrace", Boolean.class); - - instance.multipart = new RestClientMultipartConfig(); - instance.multipart.maxChunkSize = getConfigValue(configKey, "multipart.max-chunk-size", Integer.class); - - return instance; - } - - public static RestClientConfig load(Class interfaceClass) { - final RestClientConfig instance = new RestClientConfig(); - - instance.url = getConfigValue(interfaceClass, "url", String.class); - instance.uri = getConfigValue(interfaceClass, "uri", String.class); - instance.overrideUri = getConfigValue(interfaceClass, "override-uri", String.class); - instance.providers = getConfigValue(interfaceClass, "providers", String.class); - instance.connectTimeout = getConfigValue(interfaceClass, "connect-timeout", Long.class); - instance.readTimeout = getConfigValue(interfaceClass, "read-timeout", Long.class); - instance.followRedirects = getConfigValue(interfaceClass, "follow-redirects", Boolean.class); - instance.proxyAddress = getConfigValue(interfaceClass, "proxy-address", String.class); - instance.proxyUser = getConfigValue(interfaceClass, "proxy-user", String.class); - instance.proxyPassword = getConfigValue(interfaceClass, "proxy-password", String.class); - instance.nonProxyHosts = getConfigValue(interfaceClass, "non-proxy-hosts", String.class); - instance.queryParamStyle = getConfigValue(interfaceClass, "query-param-style", QueryParamStyle.class); - instance.verifyHost = getConfigValue(interfaceClass, "verify-host", Boolean.class); - instance.trustStore = getConfigValue(interfaceClass, "trust-store", String.class); - instance.trustStorePassword = getConfigValue(interfaceClass, "trust-store-password", String.class); - instance.trustStoreType = getConfigValue(interfaceClass, "trust-store-type", String.class); - instance.keyStore = getConfigValue(interfaceClass, "key-store", String.class); - instance.keyStorePassword = getConfigValue(interfaceClass, "key-store-password", String.class); - instance.keyStoreType = getConfigValue(interfaceClass, "key-store-type", String.class); - instance.hostnameVerifier = getConfigValue(interfaceClass, "hostname-verifier", String.class); - instance.tlsConfigurationName = getConfigValue(interfaceClass, "tls-configuration-name", String.class); - instance.connectionTTL = getConfigValue(interfaceClass, "connection-ttl", Integer.class); - instance.connectionPoolSize = getConfigValue(interfaceClass, "connection-pool-size", Integer.class); - instance.keepAliveEnabled = getConfigValue(interfaceClass, "keep-alive-enabled", Boolean.class); - instance.maxRedirects = getConfigValue(interfaceClass, "max-redirects", Integer.class); - instance.headers = getConfigValues(interfaceClass, "headers", String.class, String.class); - instance.shared = getConfigValue(interfaceClass, "shared", Boolean.class); - instance.name = getConfigValue(interfaceClass, "name", String.class); - instance.userAgent = getConfigValue(interfaceClass, "user-agent", String.class); - instance.http2 = getConfigValue(interfaceClass, "http2", Boolean.class); - instance.maxChunkSize = getConfigValue(interfaceClass, "max-chunk-size", MemorySize.class); - instance.alpn = getConfigValue(interfaceClass, "alpn", Boolean.class); - instance.captureStacktrace = getConfigValue(interfaceClass, "capture-stacktrace", Boolean.class); - - instance.multipart = new RestClientMultipartConfig(); - instance.multipart.maxChunkSize = getConfigValue(interfaceClass, "multipart.max-chunk-size", Integer.class); - - return instance; - } - - public static Optional getConfigValue(String configKey, String fieldName, Class type) { - final Config config = ConfigProvider.getConfig(); - Optional optional = config.getOptionalValue(composePropertyKey(configKey, fieldName), type); - if (optional.isEmpty()) { // try to find property with quoted configKey - optional = config.getOptionalValue(composePropertyKey('"' + configKey + '"', fieldName), type); - } - return optional; - } - - public static Optional getConfigValue(Class clientInterface, String fieldName, Class type) { - final Config config = ConfigProvider.getConfig(); - // first try interface full name - Optional optional = config.getOptionalValue(composePropertyKey('"' + clientInterface.getName() + '"', fieldName), - type); - if (optional.isEmpty()) { // then interface simple name - optional = config.getOptionalValue(composePropertyKey(clientInterface.getSimpleName(), fieldName), type); - } - if (optional.isEmpty()) { // lastly quoted interface simple name - optional = config.getOptionalValue(composePropertyKey('"' + clientInterface.getSimpleName() + '"', fieldName), - type); - } - return optional; - } - - private static Map getConfigValues(String configKey, String fieldName, Class keyType, Class valueType) { - final SmallRyeConfig config = (SmallRyeConfig) ConfigProvider.getConfig(); - Optional> optional = config.getOptionalValues(composePropertyKey(configKey, fieldName), keyType, valueType); - if (optional.isEmpty()) { // try to find property with quoted configKey - optional = config.getOptionalValues(composePropertyKey('"' + configKey + '"', fieldName), keyType, valueType); - } - return optional.isPresent() ? optional.get() : Collections.emptyMap(); - } - - private static Map getConfigValues(Class clientInterface, String fieldName, Class keyType, - Class valueType) { - final SmallRyeConfig config = (SmallRyeConfig) ConfigProvider.getConfig(); - // first try interface full name - Optional> optional = config.getOptionalValues( - composePropertyKey('"' + clientInterface.getName() + '"', fieldName), - keyType, valueType); - if (optional.isEmpty()) { // then interface simple name - optional = config.getOptionalValues(composePropertyKey(clientInterface.getSimpleName(), fieldName), keyType, - valueType); - } - if (optional.isEmpty()) { // lastly quoted interface simple name - optional = config.getOptionalValues(composePropertyKey('"' + clientInterface.getSimpleName() + '"', fieldName), - keyType, valueType); - } - return optional.isPresent() ? optional.get() : Collections.emptyMap(); - } - - private static String composePropertyKey(String key, String fieldName) { - return Constants.QUARKUS_CONFIG_PREFIX + key + "." + fieldName; - } -} diff --git a/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientFallbackConfigSourceInterceptor.java b/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientFallbackConfigSourceInterceptor.java index 84507edb3cb36..45e317299c5d6 100644 --- a/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientFallbackConfigSourceInterceptor.java +++ b/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientFallbackConfigSourceInterceptor.java @@ -59,9 +59,9 @@ public RestClientFallbackConfigSourceInterceptor() { /** * If an MP-style property is detected (e.g. "prefix/mp-rest/url"), * we need to include the relevant Quarkus-style property name ("quarkus.rest-client.prefix.url") in the iteration. - * + *

* This is required so that the BuildTimeConfigurationReader is aware that it should create the configuration objects for - * REST clients ({@link RestClientConfig}). + * REST clients ({@link RestClientsConfig.RestClientConfig}). */ @Override public Iterator iterateNames(final ConfigSourceInterceptorContext context) { @@ -94,9 +94,9 @@ public Iterator iterateNames(final ConfigSourceInterceptorContext contex } /** - * Splits a property key into client prefix and property name. If given key doesn't contain a client prefix, null will be - * returned in the first array item. - * + * Splits a property key into client prefix and property name. If given key doesn't contain a client prefix, null + * will be returned in the first array item. + *

* Examples: *

  • `client-prefix.url` will return `String[] {"client-prefix", "url"}`
  • *
  • `"client.prefix".url` will return `String[] {"client.prefix", "url"}`
  • diff --git a/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientLoggingConfig.java b/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientLoggingConfig.java deleted file mode 100644 index fd4887126fb47..0000000000000 --- a/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientLoggingConfig.java +++ /dev/null @@ -1,36 +0,0 @@ -package io.quarkus.restclient.config; - -import java.util.Optional; - -import io.quarkus.runtime.annotations.ConfigGroup; -import io.quarkus.runtime.annotations.ConfigItem; - -@ConfigGroup -public class RestClientLoggingConfig { - /** - * Scope of logging for the client. - *
    - * WARNING: beware of logging sensitive data - *
    - * The possible values are: - *
      - *
    • {@code request-response} - enables logging request and responses, including redirect responses
    • - *
    • {@code all} - enables logging requests and responses and lower-level logging
    • - *
    • {@code none} - no additional logging
    • - *
    - * - * This property is applicable to reactive REST clients only. - */ - @ConfigItem - public Optional scope; - - /** - * How many characters of the body should be logged. Message body can be large and can easily pollute the logs. - *

    - * By default, set to 100. - *

    - * This property is applicable to reactive REST clients only. - */ - @ConfigItem(defaultValue = "100") - public Integer bodyLimit; -} diff --git a/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientMultipartConfig.java b/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientMultipartConfig.java deleted file mode 100644 index 61e7932d7e704..0000000000000 --- a/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientMultipartConfig.java +++ /dev/null @@ -1,22 +0,0 @@ -package io.quarkus.restclient.config; - -import java.util.Optional; - -import io.quarkus.runtime.annotations.ConfigGroup; -import io.quarkus.runtime.annotations.ConfigItem; - -@ConfigGroup -public class RestClientMultipartConfig { - - /** - * The max HTTP chunk size (8096 bytes by default). - *

    - * This property is applicable to reactive REST clients only. - * - * @Deprecated Use {@code quarkus.rest-client.max-chunk-size} instead - */ - @Deprecated - @ConfigItem - public Optional maxChunkSize; - -} diff --git a/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientNameFallbackConfigSourceInterceptor.java b/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientNameFallbackConfigSourceInterceptor.java new file mode 100644 index 0000000000000..7ca19d8dbea6b --- /dev/null +++ b/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientNameFallbackConfigSourceInterceptor.java @@ -0,0 +1,87 @@ +package io.quarkus.restclient.config; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.UndeclaredThrowableException; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; + +import io.smallrye.config.ConfigMappingLoader; +import io.smallrye.config.ConfigMappingObject; +import io.smallrye.config.FallbackConfigSourceInterceptor; + +/** + * Fallbacks REST Client FQN to Simple Name. + *

    + * Ideally, this shouldn't be required. The old custom implementation allowed us to mix both names and merge them in a + * final configuration to use in the REST Client. The standard Config system does not support such a feature. If a + * configuration supports multiple names, the user has to use the same name across all configuration sources. No other + * Quarkus extension behaves this way because only the REST Client extension provides the custom code to make it work. + */ +public class RestClientNameFallbackConfigSourceInterceptor extends FallbackConfigSourceInterceptor { + public RestClientNameFallbackConfigSourceInterceptor(final List restClients) { + super(fallback(restClients)); + } + + private static Function fallback(final List restClients) { + Class implementationClass = ConfigMappingLoader + .getImplementationClass(RestClientsConfig.class); + Set ignoreNames = configMappingNames(implementationClass).get(RestClientsConfig.class.getName()).get("") + .stream() + .filter(s -> s.charAt(0) != '*') + .map(s -> "quarkus.rest-client." + s) + .collect(Collectors.toSet()); + + return new Function() { + @Override + public String apply(final String name) { + if (name.startsWith("quarkus.rest-client.")) { + if (ignoreNames.contains(name)) { + return name; + } + + for (RegisteredRestClient restClient : restClients) { + if (name.length() > 20 && name.charAt(20) == '"') { + String interfaceName = restClient.getFullName(); + if (name.regionMatches(21, interfaceName, 0, interfaceName.length())) { + if (name.length() > 21 + interfaceName.length() + && name.charAt(21 + interfaceName.length()) == '"') { + return "quarkus.rest-client." + restClient.getSimpleName() + + name.substring(21 + interfaceName.length() + 1); + } + } + } + } + } + return name; + } + }; + } + + /** + * Expose this as a public API in SmallRye Config + */ + @SuppressWarnings("unchecked") + @Deprecated(forRemoval = true) + private static Map>> configMappingNames(final Class implementationClass) { + try { + Method getNames = implementationClass.getDeclaredMethod("getNames"); + return (Map>>) getNames.invoke(null); + } catch (NoSuchMethodException e) { + throw new NoSuchMethodError(e.getMessage()); + } catch (IllegalAccessException e) { + throw new IllegalAccessError(e.getMessage()); + } catch (InvocationTargetException e) { + try { + throw e.getCause(); + } catch (RuntimeException | Error e2) { + throw e2; + } catch (Throwable t) { + throw new UndeclaredThrowableException(t); + } + } + } +} diff --git a/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientsBuildTimeConfig.java b/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientsBuildTimeConfig.java index 16ac5b0776c4e..0c3070015b00d 100644 --- a/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientsBuildTimeConfig.java +++ b/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientsBuildTimeConfig.java @@ -1,18 +1,73 @@ package io.quarkus.restclient.config; -import java.util.Collections; import java.util.Map; +import java.util.Optional; -import io.quarkus.runtime.annotations.ConfigItem; +import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; + +import io.quarkus.runtime.annotations.ConfigDocIgnore; import io.quarkus.runtime.annotations.ConfigPhase; import io.quarkus.runtime.annotations.ConfigRoot; +import io.smallrye.config.ConfigMapping; +import io.smallrye.config.WithDefault; +import io.smallrye.config.WithDefaults; +import io.smallrye.config.WithParentName; -@ConfigRoot(name = "rest-client", phase = ConfigPhase.BUILD_TIME) -public class RestClientsBuildTimeConfig { - +@ConfigMapping(prefix = "quarkus.rest-client") +@ConfigRoot(phase = ConfigPhase.BUILD_TIME) +public interface RestClientsBuildTimeConfig { /** * Configurations of REST client instances. */ - @ConfigItem(name = ConfigItem.PARENT) - public Map configs = Collections.emptyMap(); + @WithParentName + @WithDefaults + Map clients(); + + interface RestClientBuildConfig { + + /** + * The CDI scope to use for injection. This property can contain either a fully qualified class name of a CDI scope + * annotation (such as "jakarta.enterprise.context.ApplicationScoped") or its simple name (such as + * "ApplicationScoped"). + * By default, this is not set which means the interface is not registered as a bean unless it is annotated with + * {@link RegisterRestClient}. + * If an interface is not annotated with {@link RegisterRestClient} and this property is set, then Quarkus will make the + * interface + * a bean of the configured scope. + */ + Optional scope(); + + /** + * If set to true, then Quarkus will ensure that all calls from the rest client go through a local proxy + * server (that is managed by Quarkus). + * This can be very useful for capturing network traffic to a service that use HTTPS. + *

    + * This property is not applicable to the RESTEasy Client, only the Quarkus Rest client (formerly RESTEasy Reactive + * client). + *

    + * This property only applicable to dev and test mode. + */ + @WithDefault("false") + boolean enableLocalProxy(); + + /** + * This setting is used to select which proxy provider to use if there are multiple ones. + * It only applies if {@code enable-local-proxy} is true. + *

    + * The algorithm for picking between multiple provider is the following: + *

      + *
    • If only the default is around, use it (it's name is {@code default})
    • + *
    • If there is only one besides the default, use it
    • + *
    • If there are multiple ones, fail
    • + *
    + */ + Optional localProxyProvider(); + + /** + * Collects unmapped properties in the REST Client namespace, including available runtime properties. + */ + @WithParentName + @ConfigDocIgnore + Map properties(); + } } diff --git a/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientsConfig.java b/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientsConfig.java index ab20ac8605a6b..81494a2ebe334 100644 --- a/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientsConfig.java +++ b/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientsConfig.java @@ -2,25 +2,23 @@ import java.util.Map; import java.util.Optional; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -import jakarta.enterprise.inject.CreationException; import org.eclipse.microprofile.rest.client.ext.QueryParamStyle; -import io.quarkus.arc.Arc; -import io.quarkus.arc.InstanceHandle; import io.quarkus.runtime.annotations.ConfigDocDefault; +import io.quarkus.runtime.annotations.ConfigDocIgnore; import io.quarkus.runtime.annotations.ConfigDocMapKey; -import io.quarkus.runtime.annotations.ConfigItem; import io.quarkus.runtime.annotations.ConfigPhase; import io.quarkus.runtime.annotations.ConfigRoot; import io.quarkus.runtime.configuration.MemorySize; +import io.smallrye.config.ConfigMapping; +import io.smallrye.config.WithDefault; +import io.smallrye.config.WithDefaults; +import io.smallrye.config.WithParentName; -@ConfigRoot(name = "rest-client", phase = ConfigPhase.RUN_TIME) -public class RestClientsConfig { - +@ConfigMapping(prefix = "quarkus.rest-client") +@ConfigRoot(phase = ConfigPhase.RUN_TIME) +public interface RestClientsConfig { /** * Configurations of REST client instances. *

    @@ -28,16 +26,9 @@ public class RestClientsConfig { * a class bearing that annotation, in which case it is possible to use the short name, as well as fully qualified * name. */ - // This variable is only here to avoid warnings about unrecognized configuration keys. The map is otherwise ignored, - // and the RestClientConfig instances are loaded via `RestClientConfig#load()` methods instead. - @ConfigItem(name = ConfigItem.PARENT) - Map configKey; - - @SuppressWarnings("DeprecatedIsStillUsed") - // The @Deprecated annotation prevents this field from being included in generated docs. We only want the `configKey` field - // above to be included. - @Deprecated - private final Map configs = new ConcurrentHashMap<>(); + @WithParentName + @WithDefaults + Map clients(); /** * Mode in which the form data are encoded. Possible values are `HTML5`, `RFC1738` and `RFC3986`. @@ -49,8 +40,7 @@ public class RestClientsConfig { *

    * This property is not applicable to the RESTEasy Client. */ - @ConfigItem - public Optional multipartPostEncoderMode; + Optional multipartPostEncoderMode(); /** * A string value in the form of `:` that specifies the HTTP proxy server hostname @@ -58,8 +48,7 @@ public class RestClientsConfig { *

    * Can be overwritten by client-specific settings. */ - @ConfigItem - public Optional proxyAddress; + Optional proxyAddress(); /** * Proxy username, equivalent to the http.proxy or https.proxy JVM settings. @@ -68,8 +57,7 @@ public class RestClientsConfig { *

    * This property is not applicable to the RESTEasy Client. */ - @ConfigItem - public Optional proxyUser; + Optional proxyUser(); /** * Proxy password, equivalent to the http.proxyPassword or https.proxyPassword JVM settings. @@ -78,8 +66,7 @@ public class RestClientsConfig { *

    * This property is not applicable to the RESTEasy Client. */ - @ConfigItem - public Optional proxyPassword; + Optional proxyPassword(); /** * Hosts to access without proxy, similar to the http.nonProxyHosts or https.nonProxyHosts JVM settings. @@ -89,28 +76,23 @@ public class RestClientsConfig { *

    * This property is not applicable to the RESTEasy Client. */ - @ConfigItem - public Optional nonProxyHosts; - - public RestClientLoggingConfig logging; - - public RestClientMultipartConfig multipart; + Optional nonProxyHosts(); /** * A timeout in milliseconds that REST clients should wait to connect to the remote endpoint. *

    * Can be overwritten by client-specific settings. */ - @ConfigItem(defaultValue = "15000", defaultValueDocumentation = "15000 ms") - public Long connectTimeout; + @WithDefault("15000") + Long connectTimeout(); /** * A timeout in milliseconds that REST clients should wait for a response from the remote endpoint. *

    * Can be overwritten by client-specific settings. */ - @ConfigItem(defaultValue = "30000", defaultValueDocumentation = "30000 ms") - public Long readTimeout; + @WithDefault("30000") + Long readTimeout(); /** * If true, the REST clients will not provide additional contextual information (like REST client class and method @@ -118,8 +100,8 @@ public class RestClientsConfig { *

    * This property is not applicable to the RESTEasy Client. */ - @ConfigItem(defaultValue = "false") - public boolean disableContextualErrorMessages; + @WithDefault("false") + boolean disableContextualErrorMessages(); /** * Default configuration for the HTTP user-agent header to use in all REST clients. @@ -128,23 +110,20 @@ public class RestClientsConfig { *

    * This property is not applicable to the RESTEasy Client. */ - @ConfigItem - public Optional userAgent; + Optional userAgent(); /** * The HTTP headers that should be applied to all requests of the rest client. */ - @ConfigItem @ConfigDocMapKey("header-name") - public Map headers; + Map headers(); /** * The class name of the host name verifier. The class must have a public no-argument constructor. *

    * Can be overwritten by client-specific settings. */ - @ConfigItem - public Optional hostnameVerifier; + Optional hostnameVerifier(); /** * The time in ms for which a connection remains unused in the connection pool before being evicted and closed. @@ -152,24 +131,22 @@ public class RestClientsConfig { *

    * Can be overwritten by client-specific settings. */ - @ConfigItem - public Optional connectionTTL; + Optional connectionTTL(); /** * The size of the connection pool for this client. *

    * Can be overwritten by client-specific settings. */ - @ConfigItem - public Optional connectionPoolSize; + Optional connectionPoolSize(); /** * If set to false disables the keep alive completely. *

    * Can be overwritten by client-specific settings. */ - @ConfigItem(defaultValue = "true") - public Optional keepAliveEnabled; + @WithDefault("true") + Optional keepAliveEnabled(); /** * The maximum number of redirection a request can follow. @@ -178,16 +155,14 @@ public class RestClientsConfig { *

    * This property is not applicable to the RESTEasy Client. */ - @ConfigItem - public Optional maxRedirects; + Optional maxRedirects(); /** * A boolean value used to determine whether the client should follow HTTP redirect responses. *

    * Can be overwritten by client-specific settings. */ - @ConfigItem - public Optional followRedirects; + Optional followRedirects(); /** * Map where keys are fully-qualified provider classnames to include in the client, and values are their integer @@ -195,8 +170,7 @@ public class RestClientsConfig { *

    * Can be overwritten by client-specific settings. */ - @ConfigItem - public Optional providers; + Optional providers(); /** * The CDI scope to use for injections of REST client instances. Value can be either a fully qualified class name of a CDI @@ -209,8 +183,7 @@ public class RestClientsConfig { *

    * Can be overwritten by client-specific settings. */ - @ConfigItem - public Optional scope; + Optional scope(); /** * An enumerated type string value with possible values of "MULTI_PAIRS" (default), "COMMA_SEPARATED", @@ -218,8 +191,7 @@ public class RestClientsConfig { *

    * Can be overwritten by client-specific settings. */ - @ConfigItem - public Optional queryParamStyle; + Optional queryParamStyle(); /** * Set whether hostname verification is enabled. Default is enabled. @@ -227,56 +199,49 @@ public class RestClientsConfig { *

    * Can be overwritten by client-specific settings. */ - @ConfigItem - public Optional verifyHost; + Optional verifyHost(); /** * The trust store location. Can point to either a classpath resource or a file. *

    * Can be overwritten by client-specific settings. */ - @ConfigItem - public Optional trustStore; + Optional trustStore(); /** * The trust store password. *

    * Can be overwritten by client-specific settings. */ - @ConfigItem - public Optional trustStorePassword; + Optional trustStorePassword(); /** * The type of the trust store. Defaults to "JKS". *

    * Can be overwritten by client-specific settings. */ - @ConfigItem - public Optional trustStoreType; + Optional trustStoreType(); /** * The key store location. Can point to either a classpath resource or a file. *

    * Can be overwritten by client-specific settings. */ - @ConfigItem - public Optional keyStore; + Optional keyStore(); /** * The key store password. *

    * Can be overwritten by client-specific settings. */ - @ConfigItem - public Optional keyStorePassword; + Optional keyStorePassword(); /** * The type of the key store. Defaults to "JKS". *

    * Can be overwritten by client-specific settings. */ - @ConfigItem - public Optional keyStoreType; + Optional keyStoreType(); /** * The name of the TLS configuration to use. @@ -289,23 +254,21 @@ public class RestClientsConfig { *

    * This property is not applicable to the RESTEasy Client. */ - @ConfigItem - public Optional tlsConfigurationName; + Optional tlsConfigurationName(); /** * If this is true then HTTP/2 will be enabled. */ - @ConfigItem(defaultValue = "false") - public boolean http2; + @WithDefault("false") + boolean http2(); /** * The max HTTP chunk size (8096 bytes by default). *

    * Can be overwritten by client-specific settings. */ - @ConfigItem @ConfigDocDefault("8k") - public Optional maxChunkSize; + Optional maxChunkSize(); /** * If the Application-Layer Protocol Negotiation is enabled, the client will negotiate which protocol to use over the @@ -313,54 +276,317 @@ public class RestClientsConfig { * use HTTP/1.1. * When the property `http2` is enabled, this flag will be automatically enabled. */ - @ConfigItem - public Optional alpn; + Optional alpn(); /** * If {@code true}, the stacktrace of the invocation of the REST Client method is captured. * This stacktrace will be used if the invocation throws an exception */ - @ConfigItem(defaultValue = "true") - public boolean captureStacktrace; + @WithDefault("true") + boolean captureStacktrace(); - public RestClientConfig getClientConfig(String configKey) { - if (configKey == null) { - return RestClientConfig.EMPTY; - } - return configs.computeIfAbsent(configKey, RestClientConfig::load); - } + /** + * Logging configuration. + */ + RestClientLoggingConfig logging(); + + /** + * Multipart configuration. + */ + RestClientMultipartConfig multipart(); - public RestClientConfig getClientConfig(Class clientInterface) { - return configs.computeIfAbsent(clientInterface.getName(), name -> RestClientConfig.load(clientInterface)); + default RestClientConfig getClient(final Class restClientInterface) { + // Check if the key is there first or else we will get the defaults from @WithDefaults + if (clients().containsKey(restClientInterface.getName())) { + return clients().get(restClientInterface.getName()); + } + return clients().get(restClientInterface.getSimpleName()); } - public void putClientConfig(String configKey, RestClientConfig clientConfig) { - configs.put(configKey, clientConfig); + default RestClientConfig getClient(final String restClientConfigKey) { + return clients().get(restClientConfigKey); } - public void putClientConfig(Class clientInterface, RestClientConfig clientConfig) { - configs.put(clientInterface.getName(), clientConfig); + interface RestClientLoggingConfig { + /** + * Scope of logging for the client. + *
    + * WARNING: beware of logging sensitive data + *
    + * The possible values are: + *

      + *
    • {@code request-response} - enables logging request and responses, including redirect responses
    • + *
    • {@code all} - enables logging requests and responses and lower-level logging
    • + *
    • {@code none} - no additional logging
    • + *
    + * + * This property is applicable to reactive REST clients only. + */ + Optional scope(); + + /** + * How many characters of the body should be logged. Message body can be large and can easily pollute the logs. + *

    + * By default, set to 100. + *

    + * This property is applicable to reactive REST clients only. + */ + @WithDefault("100") + Integer bodyLimit(); } - public Set getConfigKeys() { - return configs.keySet(); + interface RestClientMultipartConfig { + /** + * The max HTTP chunk size (8096 bytes by default). + *

    + * This property is applicable to reactive REST clients only. + * + * @deprecated Use {@code quarkus.rest-client.max-chunk-size} instead + */ + @Deprecated + Optional maxChunkSize(); } - public static RestClientsConfig getInstance() { - InstanceHandle configHandle; - try { - configHandle = Arc.container().instance(RestClientsConfig.class); - } catch (CreationException e) { - String message = "The Rest Client configuration cannot be initialized at this stage. " - + "Try to wrap your Rest Client injection in the Provider<> interface:\n\n" - + " @Inject\n" - + " @RestClient\n" - + " Provider myRestClient;\n"; - throw new RuntimeException(message, e); - } - if (!configHandle.isAvailable()) { - throw new IllegalStateException("Unable to find the RestClientConfigs"); - } - return configHandle.get(); + interface RestClientConfig { + /** + * Dummy configuration to force lookup of REST Client configurations. + * + * @see AbstractRestClientConfigBuilder + */ + @WithDefault("true") + @ConfigDocIgnore + boolean force(); + + /** + * Multipart configuration. + */ + RestClientMultipartConfig multipart(); + + /** + * The base URL to use for this service. This property or the `uri` property is considered required, unless + * the `baseUri` attribute is configured in the `@RegisterRestClient` annotation. + */ + Optional url(); + + /** + * The base URI to use for this service. This property or the `url` property is considered required, unless + * the `baseUri` attribute is configured in the `@RegisterRestClient` annotation. + */ + Optional uri(); + + /** + * This property is only meant to be set by advanced configurations to override whatever value was set for the uri or + * url. + * The override is done using the REST Client class name configuration syntax. + *

    + * This property is not applicable to the RESTEasy Client, only the Quarkus Rest client (formerly RESTEasy Reactive + * client). + */ + Optional overrideUri(); + + /** + * Map where keys are fully-qualified provider classnames to include in the client, and values are their integer + * priorities. The equivalent of the `@RegisterProvider` annotation. + */ + Optional providers(); + + /** + * Timeout specified in milliseconds to wait to connect to the remote endpoint. + */ + Optional connectTimeout(); + + /** + * Timeout specified in milliseconds to wait for a response from the remote endpoint. + */ + Optional readTimeout(); + + /** + * A boolean value used to determine whether the client should follow HTTP redirect responses. + */ + Optional followRedirects(); + + /** + * Mode in which the form data are encoded. Possible values are `HTML5`, `RFC1738` and `RFC3986`. + * The modes are described in the + * Netty + * documentation + *

    + * By default, Rest Client Reactive uses RFC1738. + *

    + * This property is not applicable to the RESTEasy Client. + */ + Optional multipartPostEncoderMode(); + + /** + * A string value in the form of `:` that specifies the HTTP proxy server hostname + * (or IP address) and port for requests of this client to use. + *

    + * Use `none` to disable proxy + */ + Optional proxyAddress(); + + /** + * Proxy username. + *

    + * This property is not applicable to the RESTEasy Client. + */ + Optional proxyUser(); + + /** + * Proxy password. + *

    + * This property is not applicable to the RESTEasy Client. + */ + Optional proxyPassword(); + + /** + * Hosts to access without proxy + *

    + * This property is not applicable to the RESTEasy Client. + */ + Optional nonProxyHosts(); + + /** + * An enumerated type string value with possible values of "MULTI_PAIRS" (default), "COMMA_SEPARATED", + * or "ARRAY_PAIRS" that specifies the format in which multiple values for the same query parameter is used. + */ + Optional queryParamStyle(); + + /** + * Set whether hostname verification is enabled. Default is enabled. + * This setting should not be disabled in production as it makes the client vulnerable to MITM attacks. + */ + Optional verifyHost(); + + /** + * The trust store location. Can point to either a classpath resource or a file. + */ + Optional trustStore(); + + /** + * The trust store password. + */ + Optional trustStorePassword(); + + /** + * The type of the trust store. Defaults to "JKS". + */ + Optional trustStoreType(); + + /** + * The key store location. Can point to either a classpath resource or a file. + */ + Optional keyStore(); + + /** + * The key store password. + */ + Optional keyStorePassword(); + + /** + * The type of the key store. Defaults to "JKS". + */ + Optional keyStoreType(); + + /** + * The class name of the host name verifier. The class must have a public no-argument constructor. + */ + Optional hostnameVerifier(); + + /** + * The name of the TLS configuration to use. + *

    + * If not set and the default TLS configuration is configured ({@code quarkus.tls.*}) then that will be used. + * If a name is configured, it uses the configuration from {@code quarkus.tls..*} + * If a name is configured, but no TLS configuration is found with that name then an error will be thrown. + *

    + * If no TLS configuration is set, then the keys-tore, trust-store, etc. properties will be used. + *

    + * This property is not applicable to the RESTEasy Client. + */ + Optional tlsConfigurationName(); + + /** + * The time in ms for which a connection remains unused in the connection pool before being evicted and closed. + * A timeout of {@code 0} means there is no timeout. + */ + Optional connectionTTL(); + + /** + * The size of the connection pool for this client. + */ + Optional connectionPoolSize(); + + /** + * If set to false disables the keep alive completely. + */ + Optional keepAliveEnabled(); + + /** + * The maximum number of redirection a request can follow. + *

    + * This property is not applicable to the RESTEasy Client. + */ + Optional maxRedirects(); + + /** + * The HTTP headers that should be applied to all requests of the rest client. + *

    + * This property is not applicable to the RESTEasy Client. + */ + @ConfigDocMapKey("header-name") + Map headers(); + + /** + * Set to true to share the HTTP client between REST clients. + * There can be multiple shared clients distinguished by name, when no specific name is set, + * the name __vertx.DEFAULT is used. + *

    + * This property is not applicable to the RESTEasy Client. + */ + Optional shared(); + + /** + * Set the HTTP client name, used when the client is shared, otherwise ignored. + *

    + * This property is not applicable to the RESTEasy Client. + */ + Optional name(); + + /** + * Configure the HTTP user-agent header to use. + *

    + * This property is not applicable to the RESTEasy Client. + */ + Optional userAgent(); + + /** + * If this is true then HTTP/2 will be enabled. + */ + Optional http2(); + + /** + * The max HTTP ch + * unk size (8096 bytes by default). + *

    + * This property is not applicable to the RESTEasy Client. + */ + @ConfigDocDefault("8K") + Optional maxChunkSize(); + + /** + * If the Application-Layer Protocol Negotiation is enabled, the client will negotiate which protocol to use over the + * protocols exposed by the server. By default, it will try to use HTTP/2 first and if it's not enabled, it will + * use HTTP/1.1. + * When the property `http2` is enabled, this flag will be automatically enabled. + */ + Optional alpn(); + + /** + * If {@code true}, the stacktrace of the invocation of the REST Client method is captured. + * This stacktrace will be used if the invocation throws an exception + */ + Optional captureStacktrace(); } } diff --git a/extensions/resteasy-classic/rest-client-config/runtime/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.Converter b/extensions/resteasy-classic/rest-client-config/runtime/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.Converter deleted file mode 100644 index cd5453d3541f8..0000000000000 --- a/extensions/resteasy-classic/rest-client-config/runtime/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.Converter +++ /dev/null @@ -1 +0,0 @@ -io.quarkus.restclient.config.QueryParamStyleConverter \ No newline at end of file diff --git a/extensions/resteasy-classic/rest-client-config/runtime/src/test/java/io/quarkus/restclient/config/RestClientConfigTest.java b/extensions/resteasy-classic/rest-client-config/runtime/src/test/java/io/quarkus/restclient/config/RestClientConfigTest.java index 165cab90808e0..b15cfcb84cfa0 100644 --- a/extensions/resteasy-classic/rest-client-config/runtime/src/test/java/io/quarkus/restclient/config/RestClientConfigTest.java +++ b/extensions/resteasy-classic/rest-client-config/runtime/src/test/java/io/quarkus/restclient/config/RestClientConfigTest.java @@ -1,65 +1,65 @@ package io.quarkus.restclient.config; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertTrue; -import java.io.IOException; import java.math.BigInteger; -import java.net.URL; +import java.util.List; import java.util.Optional; import org.eclipse.microprofile.rest.client.ext.QueryParamStyle; import org.junit.jupiter.api.Test; -import io.smallrye.config.PropertiesConfigSource; +import io.quarkus.runtime.configuration.ConfigUtils; import io.smallrye.config.SmallRyeConfig; -import io.smallrye.config.SmallRyeConfigBuilder; public class RestClientConfigTest { @Test - public void testLoadRestClientConfig() throws IOException { - SmallRyeConfig config = createMPConfig(); + public void testLoadRestClientConfig() { + SmallRyeConfig config = ConfigUtils.emptyConfigBuilder() + .withMapping(RestClientsConfig.class) + .withInterceptors(new RestClientNameFallbackConfigSourceInterceptor( + List.of(new RegisteredRestClient(RestClientConfigTest.class.getName(), + RestClientConfigTest.class.getSimpleName())))) + .build(); Optional optionalValue = config.getOptionalValue("quarkus.rest-client.test-client.url", String.class); assertThat(optionalValue).isPresent(); - RestClientConfig configForKey = RestClientConfig.load("test-client"); + RestClientsConfig restClientsConfig = config.getConfigMapping(RestClientsConfig.class); + + RestClientsConfig.RestClientConfig configForKey = restClientsConfig.getClient("test-client"); verifyConfig(configForKey); - RestClientConfig configForClassName = RestClientConfig.load(RestClientConfigTest.class); - verifyConfig(configForClassName); - } - private void verifyConfig(RestClientConfig config) { - assertThat(config.url).isPresent(); - assertThat(config.url.get()).isEqualTo("http://localhost:8080"); - assertThat(config.uri).isPresent(); - assertThat(config.uri.get()).isEqualTo("http://localhost:8081"); - assertThat(config.providers).isPresent(); - assertThat(config.providers.get()).isEqualTo("io.quarkus.restclient.configuration.MyResponseFilter"); - assertThat(config.connectTimeout).isPresent(); - assertThat(config.connectTimeout.get()).isEqualTo(5000); - assertThat(config.readTimeout).isPresent(); - assertThat(config.readTimeout.get()).isEqualTo(6000); - assertThat(config.followRedirects).isPresent(); - assertThat(config.followRedirects.get()).isEqualTo(true); - assertThat(config.proxyAddress).isPresent(); - assertThat(config.proxyAddress.get()).isEqualTo("localhost:8080"); - assertThat(config.queryParamStyle).isPresent(); - assertThat(config.queryParamStyle.get()).isEqualTo(QueryParamStyle.COMMA_SEPARATED); - assertThat(config.hostnameVerifier).isPresent(); - assertThat(config.hostnameVerifier.get()).isEqualTo("io.quarkus.restclient.configuration.MyHostnameVerifier"); - assertThat(config.connectionTTL).isPresent(); - assertThat(config.connectionTTL.get()).isEqualTo(30000); - assertThat(config.connectionPoolSize).isPresent(); - assertThat(config.connectionPoolSize.get()).isEqualTo(10); - assertThat(config.maxChunkSize.get().asBigInteger()).isEqualTo(BigInteger.valueOf(1024)); + RestClientsConfig.RestClientConfig configForClassName = restClientsConfig.getClient(RestClientConfigTest.class); + verifyConfig(configForClassName); } - private static SmallRyeConfig createMPConfig() throws IOException { - SmallRyeConfigBuilder configBuilder = new SmallRyeConfigBuilder().addDefaultInterceptors(); - URL propertyFile = RestClientConfigTest.class.getClassLoader().getResource("application.properties"); - assertThat(propertyFile).isNotNull(); - configBuilder.withSources(new PropertiesConfigSource(propertyFile)); - return configBuilder.build(); + private void verifyConfig(RestClientsConfig.RestClientConfig config) { + assertTrue(config.url().isPresent()); + assertThat(config.url().get()).isEqualTo("http://localhost:8080"); + assertTrue(config.uri().isPresent()); + assertThat(config.uri().get()).isEqualTo("http://localhost:8081"); + assertTrue(config.providers().isPresent()); + assertThat(config.providers().get()).isEqualTo("io.quarkus.restclient.configuration.MyResponseFilter"); + assertTrue(config.connectTimeout().isPresent()); + assertThat(config.connectTimeout().get()).isEqualTo(5000); + assertTrue(config.readTimeout().isPresent()); + assertThat(config.readTimeout().get()).isEqualTo(6000); + assertTrue(config.followRedirects().isPresent()); + assertThat(config.followRedirects().get()).isEqualTo(true); + assertTrue(config.proxyAddress().isPresent()); + assertThat(config.proxyAddress().get()).isEqualTo("localhost:8080"); + assertTrue(config.queryParamStyle().isPresent()); + assertThat(config.queryParamStyle().get()).isEqualTo(QueryParamStyle.COMMA_SEPARATED); + assertTrue(config.hostnameVerifier().isPresent()); + assertThat(config.hostnameVerifier().get()).isEqualTo("io.quarkus.restclient.configuration.MyHostnameVerifier"); + assertTrue(config.connectionTTL().isPresent()); + assertThat(config.connectionTTL().get()).isEqualTo(30000); + assertTrue(config.connectionPoolSize().isPresent()); + assertThat(config.connectionPoolSize().get()).isEqualTo(10); + assertTrue(config.maxChunkSize().isPresent()); + assertThat(config.maxChunkSize().get().asBigInteger()).isEqualTo(BigInteger.valueOf(1024)); } } diff --git a/extensions/resteasy-classic/rest-client-config/runtime/src/test/resources/application.properties b/extensions/resteasy-classic/rest-client-config/runtime/src/test/resources/application.properties index 3806ef06855d3..9cab1d64f4b4a 100644 --- a/extensions/resteasy-classic/rest-client-config/runtime/src/test/resources/application.properties +++ b/extensions/resteasy-classic/rest-client-config/runtime/src/test/resources/application.properties @@ -13,7 +13,8 @@ quarkus.rest-client.test-client.connection-pool-size=10 quarkus.rest-client.test-client.max-chunk-size=1024 quarkus.rest-client."io.quarkus.restclient.config.RestClientConfigTest".url=http://localhost:8080 -quarkus.rest-client."RestClientConfigTest".uri=http://localhost:8081 +quarkus.rest-client."io.quarkus.restclient.config.RestClientConfigTest=x +quarkus.rest-client.RestClientConfigTest.uri=http://localhost:8081 quarkus.rest-client.RestClientConfigTest.scope=Singleton quarkus.rest-client."io.quarkus.restclient.config.RestClientConfigTest".providers=io.quarkus.restclient.configuration.MyResponseFilter quarkus.rest-client."io.quarkus.restclient.config.RestClientConfigTest".connect-timeout=5000 diff --git a/extensions/resteasy-classic/resteasy-client/deployment/src/main/java/io/quarkus/restclient/deployment/RestClientProcessor.java b/extensions/resteasy-classic/resteasy-client/deployment/src/main/java/io/quarkus/restclient/deployment/RestClientProcessor.java index 0b450da41ae0f..1d12d5479ccb5 100644 --- a/extensions/resteasy-classic/resteasy-client/deployment/src/main/java/io/quarkus/restclient/deployment/RestClientProcessor.java +++ b/extensions/resteasy-classic/resteasy-client/deployment/src/main/java/io/quarkus/restclient/deployment/RestClientProcessor.java @@ -72,6 +72,9 @@ import io.quarkus.deployment.builditem.CombinedIndexBuildItem; import io.quarkus.deployment.builditem.ExtensionSslNativeSupportBuildItem; import io.quarkus.deployment.builditem.FeatureBuildItem; +import io.quarkus.deployment.builditem.GeneratedClassBuildItem; +import io.quarkus.deployment.builditem.RunTimeConfigBuilderBuildItem; +import io.quarkus.deployment.builditem.StaticInitConfigBuilderBuildItem; import io.quarkus.deployment.builditem.nativeimage.NativeImageProxyDefinitionBuildItem; import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; @@ -82,6 +85,7 @@ import io.quarkus.gizmo.MethodDescriptor; import io.quarkus.gizmo.ResultHandle; import io.quarkus.restclient.NoopHostnameVerifier; +import io.quarkus.restclient.config.RegisteredRestClient; import io.quarkus.restclient.config.RestClientsConfig; import io.quarkus.restclient.config.deployment.RestClientConfigUtils; import io.quarkus.restclient.runtime.PathFeatureHandler; @@ -212,10 +216,6 @@ void processInterfaces( return; } - for (DotName interfaze : interfaces.keySet()) { - restClient.produce(new RestClientBuildItem(interfaze.toString())); - } - warnAboutNotWorkingFeaturesInNative(nativeConfig, interfaces); for (Map.Entry entry : interfaces.entrySet()) { @@ -249,19 +249,31 @@ void processInterfaces( for (Map.Entry entry : interfaces.entrySet()) { DotName restClientName = entry.getKey(); + ClassInfo classInfo = entry.getValue(); + + Optional configKey; + Optional baseUri; + AnnotationInstance instance = classInfo.declaredAnnotation(REGISTER_REST_CLIENT); + if (instance != null) { + AnnotationValue configKeyValue = instance.value("configKey"); + configKey = configKeyValue == null ? Optional.empty() : Optional.of(configKeyValue.asString()); + AnnotationValue baseUriValue = instance.value("baseUri"); + baseUri = baseUriValue == null ? Optional.empty() : Optional.of(baseUriValue.asString()); + } else { + configKey = Optional.empty(); + baseUri = Optional.empty(); + } + ExtendedBeanConfigurator configurator = SyntheticBeanBuildItem.configure(restClientName); // The spec is not clear whether we should add superinterfaces too - let's keep aligned with SmallRye for now configurator.addType(restClientName); configurator.addQualifier(REST_CLIENT); - final Optional configKey = getConfigKey(entry.getValue()); - final ScopeInfo scope = computeDefaultScope(capabilities, config, entry, configKey); - final List clientProviders = checkRestClientProviders(entry.getValue(), - restClientProviders); - configurator.scope(scope); + List clientProviders = checkRestClientProviders(entry.getValue(), restClientProviders); + configurator.scope(computeDefaultScope(capabilities, config, entry, configKey)); configurator.creator(m -> { // return new RestClientBase(proxyType, baseUri).create(); ResultHandle interfaceHandle = m.loadClassFromTCCL(restClientName.toString()); - ResultHandle baseUriHandle = m.load(getAnnotationParameter(entry.getValue(), "baseUri")); + ResultHandle baseUriHandle = baseUri.isPresent() ? m.load(baseUri.get()) : m.loadNull(); ResultHandle configKeyHandle = configKey.isPresent() ? m.load(configKey.get()) : m.loadNull(); ResultHandle restClientProvidersHandle; if (!clientProviders.isEmpty()) { @@ -284,9 +296,28 @@ void processInterfaces( configurator.destroyer(BeanDestroyer.CloseableDestroyer.class); syntheticBeans.produce(configurator.done()); + restClient.produce(new RestClientBuildItem(classInfo, configKey, baseUri)); } } + @BuildStep + void generateRestClientConfigBuilder( + List restClients, + BuildProducer generatedClass, + BuildProducer staticInitConfigBuilder, + BuildProducer runTimeConfigBuilder) { + + List registeredRestClients = restClients.stream() + .map(rc -> new RegisteredRestClient( + rc.getClassInfo().name().toString(), + rc.getClassInfo().simpleName(), + rc.getConfigKey().orElse(null))) + .toList(); + + RestClientConfigUtils.generateRestClientConfigBuilder(registeredRestClients, generatedClass, staticInitConfigBuilder, + runTimeConfigBuilder); + } + @BuildStep void clientTracingFeature(Capabilities capabilities, Optional metricsCapability, BuildProducer producer) { @@ -387,14 +418,6 @@ private void processInterfaceReturnTypes(ClassInfo classInfo, Set returnTy } } - private Optional getConfigKey(ClassInfo classInfo) { - String configKey = getAnnotationParameter(classInfo, "configKey"); - if (configKey.isEmpty()) { - return Optional.empty(); - } - return Optional.of(configKey); - } - private ScopeInfo computeDefaultScope(Capabilities capabilities, Config config, Map.Entry entry, Optional configKey) { ScopeInfo scopeToUse = null; @@ -451,20 +474,6 @@ private ScopeInfo computeDefaultScope(Capabilities capabilities, Config config, return scopeToUse != null ? scopeToUse : globalDefaultScope.getInfo(); } - private String getAnnotationParameter(ClassInfo classInfo, String parameterName) { - AnnotationInstance instance = classInfo.declaredAnnotation(REGISTER_REST_CLIENT); - if (instance == null) { - return ""; - } - - AnnotationValue value = instance.value(parameterName); - if (value == null) { - return ""; - } - - return value.asString(); - } - @BuildStep IgnoreClientProviderBuildItem ignoreMPPublisher() { // hack to remove a provider that is manually registered QuarkusRestClientBuilder diff --git a/extensions/resteasy-classic/resteasy-client/deployment/src/test/java/io/quarkus/restclient/configuration/GlobalConfigurationTest.java b/extensions/resteasy-classic/resteasy-client/deployment/src/test/java/io/quarkus/restclient/configuration/GlobalConfigurationTest.java index 38687c778997e..4b855ea061cda 100644 --- a/extensions/resteasy-classic/resteasy-client/deployment/src/test/java/io/quarkus/restclient/configuration/GlobalConfigurationTest.java +++ b/extensions/resteasy-classic/resteasy-client/deployment/src/test/java/io/quarkus/restclient/configuration/GlobalConfigurationTest.java @@ -50,35 +50,34 @@ void shouldRespond() { @Test void checkGlobalConfigValues() { // global properties: - assertThat(configRoot.multipartPostEncoderMode.get()).isEqualTo("HTML5"); - assertThat(configRoot.disableContextualErrorMessages).isTrue(); + assertThat(configRoot.multipartPostEncoderMode().get()).isEqualTo("HTML5"); + assertThat(configRoot.disableContextualErrorMessages()).isTrue(); // global defaults for client specific properties: - assertThat(configRoot.scope.get()).isEqualTo("Singleton"); - assertThat(configRoot.proxyAddress.get()).isEqualTo("host:123"); - assertThat(configRoot.proxyUser.get()).isEqualTo("proxyUser"); - assertThat(configRoot.proxyPassword.get()).isEqualTo("proxyPassword"); - assertThat(configRoot.nonProxyHosts.get()).isEqualTo("nonProxyHosts"); - assertThat(configRoot.connectTimeout).isEqualTo(2000); - assertThat(configRoot.readTimeout).isEqualTo(2001); - assertThat(configRoot.userAgent.get()).isEqualTo("agent"); - assertThat(configRoot.headers).isEqualTo(Collections.singletonMap("foo", "bar")); - assertThat(configRoot.hostnameVerifier.get()) + assertThat(configRoot.scope().get()).isEqualTo("Singleton"); + assertThat(configRoot.proxyAddress().get()).isEqualTo("host:123"); + assertThat(configRoot.proxyUser().get()).isEqualTo("proxyUser"); + assertThat(configRoot.proxyPassword().get()).isEqualTo("proxyPassword"); + assertThat(configRoot.nonProxyHosts().get()).isEqualTo("nonProxyHosts"); + assertThat(configRoot.connectTimeout()).isEqualTo(2000); + assertThat(configRoot.readTimeout()).isEqualTo(2001); + assertThat(configRoot.userAgent().get()).isEqualTo("agent"); + assertThat(configRoot.headers()).isEqualTo(Collections.singletonMap("foo", "bar")); + assertThat(configRoot.hostnameVerifier().get()) .isEqualTo("io.quarkus.restclient.configuration.MyHostnameVerifier"); - assertThat(configRoot.connectionTTL.get()).isEqualTo(20000); // value in ms, will be converted to seconds - assertThat(configRoot.connectionPoolSize.get()).isEqualTo(2); - assertThat(configRoot.maxRedirects.get()).isEqualTo(2); - assertThat(configRoot.followRedirects.get()).isTrue(); - assertThat(configRoot.providers.get()) + assertThat(configRoot.connectionTTL().get()).isEqualTo(20000); // value in ms, will be converted to seconds + assertThat(configRoot.connectionPoolSize().get()).isEqualTo(2); + assertThat(configRoot.maxRedirects().get()).isEqualTo(2); + assertThat(configRoot.followRedirects().get()).isTrue(); + assertThat(configRoot.providers().get()) .isEqualTo("io.quarkus.restclient.configuration.MyResponseFilter"); - assertThat(configRoot.queryParamStyle.get()).isEqualTo(QueryParamStyle.MULTI_PAIRS); + assertThat(configRoot.queryParamStyle().get()).isEqualTo(QueryParamStyle.MULTI_PAIRS); - assertThat(configRoot.trustStore.get()).isEqualTo("/path"); - assertThat(configRoot.trustStorePassword.get()).isEqualTo("password"); - assertThat(configRoot.trustStoreType.get()).isEqualTo("JKS"); - assertThat(configRoot.keyStore.get()).isEqualTo("/path"); - assertThat(configRoot.keyStorePassword.get()).isEqualTo("password"); - assertThat(configRoot.keyStoreType.get()).isEqualTo("JKS"); + assertThat(configRoot.trustStore().get()).isEqualTo("/path"); + assertThat(configRoot.trustStorePassword().get()).isEqualTo("password"); + assertThat(configRoot.trustStoreType().get()).isEqualTo("JKS"); + assertThat(configRoot.keyStore().get()).isEqualTo("/path"); + assertThat(configRoot.keyStorePassword().get()).isEqualTo("password"); + assertThat(configRoot.keyStoreType().get()).isEqualTo("JKS"); } - } diff --git a/extensions/resteasy-classic/resteasy-client/deployment/src/test/java/io/quarkus/restclient/configuration/QuarkusConfigurationTest.java b/extensions/resteasy-classic/resteasy-client/deployment/src/test/java/io/quarkus/restclient/configuration/QuarkusConfigurationTest.java index a333943a45e00..4e9d72b3ee2db 100644 --- a/extensions/resteasy-classic/resteasy-client/deployment/src/test/java/io/quarkus/restclient/configuration/QuarkusConfigurationTest.java +++ b/extensions/resteasy-classic/resteasy-client/deployment/src/test/java/io/quarkus/restclient/configuration/QuarkusConfigurationTest.java @@ -1,6 +1,7 @@ package io.quarkus.restclient.configuration; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Set; @@ -16,7 +17,6 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.arc.Arc; -import io.quarkus.restclient.config.RestClientConfig; import io.quarkus.restclient.config.RestClientsConfig; import io.quarkus.test.QuarkusUnitTest; @@ -45,38 +45,38 @@ void shouldHaveSingletonScope() { @Test void configurationsShouldBeLoaded() { - verifyClientConfig(configRoot.getClientConfig("echo-client"), true); - verifyClientConfig(configRoot.getClientConfig("io.quarkus.restclient.configuration.EchoClient"), true); - verifyClientConfig(configRoot.getClientConfig("EchoClient"), true); - verifyClientConfig(configRoot.getClientConfig("mp-client"), false); // non-standard properties cannot be set via MP style config - verifyClientConfig(configRoot.getClientConfig("a.b.c.Client"), false); + verifyClientConfig(configRoot.clients().get("echo-client"), true); + verifyClientConfig(configRoot.clients().get("io.quarkus.restclient.configuration.EchoClient"), true); + verifyClientConfig(configRoot.clients().get("EchoClient"), true); + verifyClientConfig(configRoot.clients().get("mp-client"), false); // non-standard properties cannot be set via MP style config + verifyClientConfig(configRoot.clients().get("a.b.c.Client"), false); } - void verifyClientConfig(RestClientConfig clientConfig, boolean verifyNonStandardProperties) { - assertThat(clientConfig.url).isPresent(); - assertThat(clientConfig.url.get()).contains("localhost"); - assertThat(clientConfig.providers).isPresent(); - assertThat(clientConfig.providers.get()) + void verifyClientConfig(RestClientsConfig.RestClientConfig clientConfig, boolean verifyNonStandardProperties) { + assertTrue(clientConfig.url().isPresent()); + assertThat(clientConfig.url().get()).contains("localhost"); + assertTrue(clientConfig.providers().isPresent()); + assertThat(clientConfig.providers().get()) .isEqualTo("io.quarkus.restclient.configuration.MyResponseFilter"); - assertThat(clientConfig.connectTimeout).isPresent(); - assertThat(clientConfig.connectTimeout.get()).isEqualTo(5000); - assertThat(clientConfig.readTimeout).isPresent(); - assertThat(clientConfig.readTimeout.get()).isEqualTo(6000); - assertThat(clientConfig.followRedirects).isPresent(); - assertThat(clientConfig.followRedirects.get()).isEqualTo(true); - assertThat(clientConfig.proxyAddress).isPresent(); - assertThat(clientConfig.proxyAddress.get()).isEqualTo("localhost:8080"); - assertThat(clientConfig.queryParamStyle).isPresent(); - assertThat(clientConfig.queryParamStyle.get()).isEqualTo(QueryParamStyle.COMMA_SEPARATED); - assertThat(clientConfig.hostnameVerifier).isPresent(); - assertThat(clientConfig.hostnameVerifier.get()) + assertTrue(clientConfig.connectTimeout().isPresent()); + assertThat(clientConfig.connectTimeout().get()).isEqualTo(5000); + assertTrue(clientConfig.readTimeout().isPresent()); + assertThat(clientConfig.readTimeout().get()).isEqualTo(6000); + assertTrue(clientConfig.followRedirects().isPresent()); + assertThat(clientConfig.followRedirects().get()).isEqualTo(true); + assertTrue(clientConfig.proxyAddress().isPresent()); + assertThat(clientConfig.proxyAddress().get()).isEqualTo("localhost:8080"); + assertTrue(clientConfig.queryParamStyle().isPresent()); + assertThat(clientConfig.queryParamStyle().get()).isEqualTo(QueryParamStyle.COMMA_SEPARATED); + assertTrue(clientConfig.hostnameVerifier().isPresent()); + assertThat(clientConfig.hostnameVerifier().get()) .isEqualTo("io.quarkus.restclient.configuration.MyHostnameVerifier"); if (verifyNonStandardProperties) { - assertThat(clientConfig.connectionTTL).isPresent(); - assertThat(clientConfig.connectionTTL.get()).isEqualTo(30000); - assertThat(clientConfig.connectionPoolSize).isPresent(); - assertThat(clientConfig.connectionPoolSize.get()).isEqualTo(10); + assertTrue(clientConfig.connectionTTL().isPresent()); + assertThat(clientConfig.connectionTTL().get()).isEqualTo(30000); + assertTrue(clientConfig.connectionPoolSize().isPresent()); + assertThat(clientConfig.connectionPoolSize().get()).isEqualTo(10); } } } diff --git a/extensions/resteasy-classic/resteasy-client/deployment/src/test/java/io/quarkus/restclient/configuration/RestClientConfigNotationTest.java b/extensions/resteasy-classic/resteasy-client/deployment/src/test/java/io/quarkus/restclient/configuration/RestClientConfigNotationTest.java index 98d30635cbae8..fba3b4fdf238d 100644 --- a/extensions/resteasy-classic/resteasy-client/deployment/src/test/java/io/quarkus/restclient/configuration/RestClientConfigNotationTest.java +++ b/extensions/resteasy-classic/resteasy-client/deployment/src/test/java/io/quarkus/restclient/configuration/RestClientConfigNotationTest.java @@ -6,32 +6,19 @@ import java.util.Map; import java.util.Set; -import org.eclipse.microprofile.config.ConfigProvider; import org.eclipse.microprofile.config.spi.ConfigSource; -import org.jboss.shrinkwrap.api.ShrinkWrap; -import org.jboss.shrinkwrap.api.spec.JavaArchive; -import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import com.google.common.collect.Iterators; - -import io.quarkus.restclient.config.RestClientConfig; import io.quarkus.restclient.config.RestClientsConfig; -import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.runtime.configuration.ConfigUtils; +import io.smallrye.config.SmallRyeConfig; import io.smallrye.config.common.AbstractConfigSource; public class RestClientConfigNotationTest { private static final String URL = "localhost:8080"; - @RegisterExtension - static final QuarkusUnitTest TEST = new QuarkusUnitTest().setArchiveProducer( - () -> ShrinkWrap.create(JavaArchive.class) - .addClasses(EchoClient.class, TestConfigSource.class) - .addAsServiceProvider("org.eclipse.microprofile.config.spi.ConfigSource", - "io.quarkus.restclient.configuration.RestClientConfigNotationTest$TestConfigSource")); - @ParameterizedTest @ValueSource(strings = { "quarkus.rest-client.\"io.quarkus.restclient.configuration.EchoClient\".url", @@ -41,9 +28,13 @@ public class RestClientConfigNotationTest { }) public void testInterfaceConfiguration(final String urlPropertyName) { TestConfigSource.urlPropertyName = urlPropertyName; + SmallRyeConfig config = ConfigUtils.emptyConfigBuilder() + .withMapping(RestClientsConfig.class) + .withSources(new TestConfigSource()) + .build(); - RestClientsConfig configRoot = new RestClientsConfig(); - RestClientConfig clientConfig = configRoot.getClientConfig(EchoClient.class); + RestClientsConfig configRoot = config.getConfigMapping(RestClientsConfig.class); + RestClientsConfig.RestClientConfig clientConfig = configRoot.getClient(EchoClient.class); verifyConfig(clientConfig, urlPropertyName); } @@ -55,20 +46,23 @@ public void testInterfaceConfiguration(final String urlPropertyName) { }) public void testConfigKeyConfiguration(final String urlPropertyName) { TestConfigSource.urlPropertyName = urlPropertyName; - RestClientsConfig configRoot = new RestClientsConfig(); - RestClientConfig clientConfig = configRoot.getClientConfig("echo-client"); + SmallRyeConfig config = ConfigUtils.emptyConfigBuilder() + .withMapping(RestClientsConfig.class) + .withSources(new TestConfigSource()) + .build(); + RestClientsConfig configRoot = config.getConfigMapping(RestClientsConfig.class); + RestClientsConfig.RestClientConfig clientConfig = configRoot.getClient("echo-client"); verifyConfig(clientConfig, urlPropertyName); } - private void verifyConfig(final RestClientConfig clientConfig, final String urlPropertyName) { - ConfigSource configSource = Iterators.find(ConfigProvider.getConfig().getConfigSources().iterator(), - c -> c.getName().equals(TestConfigSource.SOURCE_NAME)); + private void verifyConfig(final RestClientsConfig.RestClientConfig clientConfig, final String urlPropertyName) { + ConfigSource configSource = new TestConfigSource(); assertThat(configSource.getPropertyNames()).containsOnly(urlPropertyName); assertThat(configSource.getValue(urlPropertyName)).isEqualTo(URL); - assertThat(clientConfig.url).isPresent(); - assertThat(clientConfig.url.get()).isEqualTo(URL); + assertThat(clientConfig.url()).isPresent(); + assertThat(clientConfig.url().get()).isEqualTo(URL); } public static class TestConfigSource extends AbstractConfigSource { diff --git a/extensions/resteasy-classic/resteasy-client/deployment/src/test/java/io/quarkus/restclient/configuration/RestClientOverrideRuntimeConfigTest.java b/extensions/resteasy-classic/resteasy-client/deployment/src/test/java/io/quarkus/restclient/configuration/RestClientOverrideRuntimeConfigTest.java index f8e84e01aaf29..b471a07e5d2af 100644 --- a/extensions/resteasy-classic/resteasy-client/deployment/src/test/java/io/quarkus/restclient/configuration/RestClientOverrideRuntimeConfigTest.java +++ b/extensions/resteasy-classic/resteasy-client/deployment/src/test/java/io/quarkus/restclient/configuration/RestClientOverrideRuntimeConfigTest.java @@ -64,8 +64,8 @@ void overrideConfig() { // We use the Quarkus name, because that is the one that has priority assertEquals(quarkusValue.getName(), "quarkus.rest-client.\"io.quarkus.restclient.configuration.EchoClient\".url"); - assertTrue(restClientsConfig.getConfigKeys().contains("io.quarkus.restclient.configuration.EchoClient")); - Optional url = restClientsConfig.getClientConfig("io.quarkus.restclient.configuration.EchoClient").url; + assertTrue(restClientsConfig.clients().containsKey("io.quarkus.restclient.configuration.EchoClient")); + Optional url = restClientsConfig.clients().get("io.quarkus.restclient.configuration.EchoClient").url(); assertTrue(url.isPresent()); assertEquals(url.get(), mpValue.getValue()); assertEquals(url.get(), quarkusValue.getValue()); diff --git a/extensions/resteasy-classic/resteasy-client/deployment/src/test/java/io/quarkus/restclient/configuration/UnknownConfigTest.java b/extensions/resteasy-classic/resteasy-client/deployment/src/test/java/io/quarkus/restclient/configuration/UnknownConfigTest.java index 430b7e713a5ff..e792fbafcb251 100644 --- a/extensions/resteasy-classic/resteasy-client/deployment/src/test/java/io/quarkus/restclient/configuration/UnknownConfigTest.java +++ b/extensions/resteasy-classic/resteasy-client/deployment/src/test/java/io/quarkus/restclient/configuration/UnknownConfigTest.java @@ -12,7 +12,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; -import io.quarkus.restclient.config.RestClientConfig; import io.quarkus.restclient.config.RestClientsConfig; import io.quarkus.test.QuarkusUnitTest; @@ -31,14 +30,14 @@ public class UnknownConfigTest { @Test void testClientConfigsArePresent() { - verifyClientConfig(restClientsConfig.getClientConfig("echo-client")); - verifyClientConfig(restClientsConfig.getClientConfig("io.quarkus.restclient.configuration.EchoClient")); - verifyClientConfig(restClientsConfig.getClientConfig("EchoClient")); - verifyClientConfig(restClientsConfig.getClientConfig("a.b.c.Client")); + verifyClientConfig(restClientsConfig.clients().get("echo-client")); + verifyClientConfig(restClientsConfig.clients().get("io.quarkus.restclient.configuration.EchoClient")); + verifyClientConfig(restClientsConfig.clients().get("EchoClient")); + verifyClientConfig(restClientsConfig.clients().get("a.b.c.Client")); } - private void verifyClientConfig(RestClientConfig config) { - assertTrue(config.url.isPresent()); - assertEquals("http://localhost:8081", config.url.get()); + private void verifyClientConfig(RestClientsConfig.RestClientConfig config) { + assertTrue(config.url().isPresent()); + assertEquals("http://localhost:8081", config.url().get()); } } diff --git a/extensions/resteasy-classic/resteasy-client/deployment/src/test/java/io/quarkus/restclient/configuration/VaultScenarioRestClientConfigTest.java b/extensions/resteasy-classic/resteasy-client/deployment/src/test/java/io/quarkus/restclient/configuration/VaultScenarioRestClientConfigTest.java index 5a4e7ace7831b..ba353a963c468 100644 --- a/extensions/resteasy-classic/resteasy-client/deployment/src/test/java/io/quarkus/restclient/configuration/VaultScenarioRestClientConfigTest.java +++ b/extensions/resteasy-classic/resteasy-client/deployment/src/test/java/io/quarkus/restclient/configuration/VaultScenarioRestClientConfigTest.java @@ -21,7 +21,7 @@ /** * This test makes sure that the rest client configuration is loaded even if it's provided by a ConfigSource which doesn't * list its contents via {@link ConfigSource#getPropertyNames()} (e.g. {@link VaultLikeConfigSource}). - * + *

    * This wasn't working when the configuration was accessed through a ConfigSource map - rest client initialization would fail * because no URI/URL configuration was obtained. */ diff --git a/extensions/resteasy-classic/resteasy-client/runtime/src/main/java/io/quarkus/restclient/runtime/RestClientBase.java b/extensions/resteasy-classic/resteasy-client/runtime/src/main/java/io/quarkus/restclient/runtime/RestClientBase.java index df5ccb883169e..d399714f3b5ab 100644 --- a/extensions/resteasy-classic/resteasy-client/runtime/src/main/java/io/quarkus/restclient/runtime/RestClientBase.java +++ b/extensions/resteasy-classic/resteasy-client/runtime/src/main/java/io/quarkus/restclient/runtime/RestClientBase.java @@ -18,6 +18,7 @@ import javax.net.ssl.HostnameVerifier; +import org.eclipse.microprofile.config.ConfigProvider; import org.eclipse.microprofile.context.ManagedExecutor; import org.eclipse.microprofile.rest.client.RestClientBuilder; import org.eclipse.microprofile.rest.client.ext.QueryParamStyle; @@ -25,8 +26,8 @@ import io.quarkus.arc.Arc; import io.quarkus.arc.InstanceHandle; import io.quarkus.restclient.NoopHostnameVerifier; -import io.quarkus.restclient.config.RestClientConfig; import io.quarkus.restclient.config.RestClientsConfig; +import io.smallrye.config.SmallRyeConfig; public class RestClientBase { @@ -43,7 +44,7 @@ public class RestClientBase { public RestClientBase(Class proxyType, String baseUriFromAnnotation, String configKey, Class[] clientProviders) { this(proxyType, baseUriFromAnnotation, configKey, clientProviders, - RestClientsConfig.getInstance()); + ConfigProvider.getConfig().unwrap(SmallRyeConfig.class).getConfigMapping(RestClientsConfig.class)); } RestClientBase(Class proxyType, String baseUriFromAnnotation, String configKey, @@ -79,14 +80,14 @@ protected void configureBuilder(RestClientBuilder builder) { } protected void configureCustomProperties(RestClientBuilder builder) { - Optional connectionPoolSize = oneOf(clientConfigByClassName().connectionPoolSize, - clientConfigByConfigKey().connectionPoolSize, configRoot.connectionPoolSize); + Optional connectionPoolSize = oneOf(clientConfigByClassName().connectionPoolSize(), + clientConfigByConfigKey().connectionPoolSize(), configRoot.connectionPoolSize()); if (connectionPoolSize.isPresent()) { builder.property("resteasy.connectionPoolSize", connectionPoolSize.get()); } - Optional connectionTTL = oneOf(clientConfigByClassName().connectionTTL, - clientConfigByConfigKey().connectionTTL, configRoot.connectionTTL); + Optional connectionTTL = oneOf(clientConfigByClassName().connectionTTL(), + clientConfigByConfigKey().connectionTTL(), configRoot.connectionTTL()); if (connectionTTL.isPresent()) { builder.property("resteasy.connectionTTL", Arrays.asList(connectionTTL.get(), TimeUnit.MILLISECONDS)); @@ -94,8 +95,9 @@ protected void configureCustomProperties(RestClientBuilder builder) { } protected void configureProxy(RestClientBuilder builder) { - Optional proxyAddress = oneOf(clientConfigByClassName().proxyAddress, clientConfigByConfigKey().proxyAddress, - configRoot.proxyAddress); + Optional proxyAddress = oneOf(clientConfigByClassName().proxyAddress(), + clientConfigByConfigKey().proxyAddress(), + configRoot.proxyAddress()); if (proxyAddress.isPresent() && !NONE.equals(proxyAddress.get())) { String proxyString = proxyAddress.get(); @@ -118,42 +120,42 @@ protected void configureProxy(RestClientBuilder builder) { } protected void configureRedirects(RestClientBuilder builder) { - Optional followRedirects = oneOf(clientConfigByClassName().followRedirects, - clientConfigByConfigKey().followRedirects, configRoot.followRedirects); + Optional followRedirects = oneOf(clientConfigByClassName().followRedirects(), + clientConfigByConfigKey().followRedirects(), configRoot.followRedirects()); if (followRedirects.isPresent()) { builder.followRedirects(followRedirects.get()); } } protected void configureQueryParamStyle(RestClientBuilder builder) { - Optional queryParamStyle = oneOf(clientConfigByClassName().queryParamStyle, - clientConfigByConfigKey().queryParamStyle, configRoot.queryParamStyle); + Optional queryParamStyle = oneOf(clientConfigByClassName().queryParamStyle(), + clientConfigByConfigKey().queryParamStyle(), configRoot.queryParamStyle()); if (queryParamStyle.isPresent()) { builder.queryParamStyle(queryParamStyle.get()); } } protected void configureSsl(RestClientBuilder builder) { - Optional trustStore = oneOf(clientConfigByClassName().trustStore, clientConfigByConfigKey().trustStore, - configRoot.trustStore); + Optional trustStore = oneOf(clientConfigByClassName().trustStore(), clientConfigByConfigKey().trustStore(), + configRoot.trustStore()); if (trustStore.isPresent() && !trustStore.get().isBlank() && !NONE.equals(trustStore.get())) { registerTrustStore(trustStore.get(), builder); } - Optional keyStore = oneOf(clientConfigByClassName().keyStore, clientConfigByConfigKey().keyStore, - configRoot.keyStore); + Optional keyStore = oneOf(clientConfigByClassName().keyStore(), clientConfigByConfigKey().keyStore(), + configRoot.keyStore()); if (keyStore.isPresent() && !keyStore.get().isBlank() && !NONE.equals(keyStore.get())) { registerKeyStore(keyStore.get(), builder); } - Optional hostnameVerifier = oneOf(clientConfigByClassName().hostnameVerifier, - clientConfigByConfigKey().hostnameVerifier, configRoot.hostnameVerifier); + Optional hostnameVerifier = oneOf(clientConfigByClassName().hostnameVerifier(), + clientConfigByConfigKey().hostnameVerifier(), configRoot.hostnameVerifier()); if (hostnameVerifier.isPresent()) { registerHostnameVerifier(hostnameVerifier.get(), builder); } else { // If `verify-host` is disabled, we configure the client using the `NoopHostnameVerifier` verifier. - Optional verifyHost = oneOf(clientConfigByClassName().verifyHost, clientConfigByConfigKey().verifyHost, - configRoot.verifyHost); + Optional verifyHost = oneOf(clientConfigByClassName().verifyHost(), clientConfigByConfigKey().verifyHost(), + configRoot.verifyHost()); if (verifyHost.isPresent() && !verifyHost.get()) { registerHostnameVerifier(NoopHostnameVerifier.class.getName(), builder); } @@ -182,12 +184,12 @@ private void registerHostnameVerifier(String verifier, RestClientBuilder builder private void registerKeyStore(String keyStorePath, RestClientBuilder builder) { try { - Optional keyStoreType = oneOf(clientConfigByClassName().keyStoreType, - clientConfigByConfigKey().keyStoreType, configRoot.keyStoreType); + Optional keyStoreType = oneOf(clientConfigByClassName().keyStoreType(), + clientConfigByConfigKey().keyStoreType(), configRoot.keyStoreType()); KeyStore keyStore = KeyStore.getInstance(keyStoreType.orElse("JKS")); - Optional keyStorePassword = oneOf(clientConfigByClassName().keyStorePassword, - clientConfigByConfigKey().keyStorePassword, configRoot.keyStorePassword); + Optional keyStorePassword = oneOf(clientConfigByClassName().keyStorePassword(), + clientConfigByConfigKey().keyStorePassword(), configRoot.keyStorePassword()); if (keyStorePassword.isEmpty()) { throw new IllegalArgumentException("No password provided for keystore"); } @@ -208,12 +210,12 @@ private void registerKeyStore(String keyStorePath, RestClientBuilder builder) { private void registerTrustStore(String trustStorePath, RestClientBuilder builder) { try { - Optional trustStoreType = oneOf(clientConfigByClassName().trustStoreType, - clientConfigByConfigKey().trustStoreType, configRoot.trustStoreType); + Optional trustStoreType = oneOf(clientConfigByClassName().trustStoreType(), + clientConfigByConfigKey().trustStoreType(), configRoot.trustStoreType()); KeyStore trustStore = KeyStore.getInstance(trustStoreType.orElse("JKS")); - Optional trustStorePassword = oneOf(clientConfigByClassName().trustStorePassword, - clientConfigByConfigKey().trustStorePassword, configRoot.trustStorePassword); + Optional trustStorePassword = oneOf(clientConfigByClassName().trustStorePassword(), + clientConfigByConfigKey().trustStorePassword(), configRoot.trustStorePassword()); if (trustStorePassword.isEmpty()) { throw new IllegalArgumentException("No password provided for truststore"); } @@ -258,8 +260,8 @@ private InputStream locateStream(String path) throws FileNotFoundException { } protected void configureProviders(RestClientBuilder builder) { - Optional providers = oneOf(clientConfigByClassName().providers, clientConfigByConfigKey().providers, - configRoot.providers); + Optional providers = oneOf(clientConfigByClassName().providers(), clientConfigByConfigKey().providers(), + configRoot.providers()); if (providers.isPresent()) { registerProviders(builder, providers.get()); @@ -286,23 +288,23 @@ private Class providerClassForName(String name) { } protected void configureTimeouts(RestClientBuilder builder) { - Long connectTimeout = oneOf(clientConfigByClassName().connectTimeout, - clientConfigByConfigKey().connectTimeout).orElse(this.configRoot.connectTimeout); + 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); + Long readTimeout = oneOf(clientConfigByClassName().readTimeout(), + clientConfigByConfigKey().readTimeout()).orElse(this.configRoot.readTimeout()); if (readTimeout != null) { builder.readTimeout(readTimeout, TimeUnit.MILLISECONDS); } } protected void configureBaseUrl(RestClientBuilder builder) { - Optional baseUrlOptional = oneOf(clientConfigByClassName().uri, clientConfigByConfigKey().uri); + Optional baseUrlOptional = oneOf(clientConfigByClassName().uri(), clientConfigByConfigKey().uri()); if (baseUrlOptional.isEmpty()) { - baseUrlOptional = oneOf(clientConfigByClassName().url, clientConfigByConfigKey().url); + baseUrlOptional = oneOf(clientConfigByClassName().url(), clientConfigByConfigKey().url()); } if (((baseUriFromAnnotation == null) || baseUriFromAnnotation.isEmpty()) && baseUrlOptional.isEmpty()) { @@ -330,12 +332,12 @@ protected void configureBaseUrl(RestClientBuilder builder) { } } - private RestClientConfig clientConfigByConfigKey() { - return this.configRoot.getClientConfig(this.configKey); + private RestClientsConfig.RestClientConfig clientConfigByConfigKey() { + return this.configRoot.getClient(this.configKey); } - private RestClientConfig clientConfigByClassName() { - return this.configRoot.getClientConfig(this.proxyType); + private RestClientsConfig.RestClientConfig clientConfigByClassName() { + return this.configRoot.getClient(this.proxyType); } @SafeVarargs diff --git a/extensions/resteasy-classic/resteasy-client/runtime/src/test/java/io/quarkus/restclient/runtime/RestClientBaseTest.java b/extensions/resteasy-classic/resteasy-client/runtime/src/test/java/io/quarkus/restclient/runtime/RestClientBaseTest.java index 52f2b42a2aa3f..589bb7f3ebd05 100644 --- a/extensions/resteasy-classic/resteasy-client/runtime/src/test/java/io/quarkus/restclient/runtime/RestClientBaseTest.java +++ b/extensions/resteasy-classic/resteasy-client/runtime/src/test/java/io/quarkus/restclient/runtime/RestClientBaseTest.java @@ -1,5 +1,9 @@ package io.quarkus.restclient.runtime; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static org.eclipse.microprofile.rest.client.ext.QueryParamStyle.*; +import static org.mockito.Mockito.verify; + import java.io.IOException; import java.io.OutputStream; import java.net.MalformedURLException; @@ -11,8 +15,8 @@ import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import java.util.Arrays; -import java.util.Optional; -import java.util.concurrent.TimeUnit; +import java.util.HashMap; +import java.util.Map; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLSession; @@ -22,15 +26,14 @@ import jakarta.ws.rs.client.ClientResponseFilter; import org.eclipse.microprofile.rest.client.RestClientBuilder; -import org.eclipse.microprofile.rest.client.ext.QueryParamStyle; import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.mockito.Mockito; -import io.quarkus.restclient.config.RestClientConfig; import io.quarkus.restclient.config.RestClientsConfig; +import io.quarkus.runtime.configuration.ConfigUtils; public class RestClientBaseTest { @@ -83,8 +86,13 @@ public static void afterAll() { public void testClientSpecificConfigs() throws Exception { // given - RestClientsConfig configRoot = createSampleConfigRoot(); - configRoot.putClientConfig("test-client", createSampleClientConfig()); + RestClientsConfig configRoot = ConfigUtils.emptyConfigBuilder() + .setAddDefaultSources(false) + .withMapping(RestClientsConfig.class) + .withDefaultValues(createSampleConfigRoot()) + .withDefaultValues(createSampleClientConfig("test-client")) + .build() + .getConfigMapping(RestClientsConfig.class); // when @@ -98,26 +106,31 @@ public void testClientSpecificConfigs() throws Exception { // then - Mockito.verify(restClientBuilderMock).baseUrl(new URL("http://localhost")); - Mockito.verify(restClientBuilderMock).proxyAddress("host1", 123); - Mockito.verify(restClientBuilderMock).connectTimeout(100, TimeUnit.MILLISECONDS); - Mockito.verify(restClientBuilderMock).readTimeout(101, TimeUnit.MILLISECONDS); - Mockito.verify(restClientBuilderMock).hostnameVerifier(Mockito.any(MyHostnameVerifier1.class)); - Mockito.verify(restClientBuilderMock).property("resteasy.connectionTTL", Arrays.asList(102, TimeUnit.MILLISECONDS)); - Mockito.verify(restClientBuilderMock).property("resteasy.connectionPoolSize", 103); - Mockito.verify(restClientBuilderMock).followRedirects(true); - Mockito.verify(restClientBuilderMock).register(MyResponseFilter1.class); - Mockito.verify(restClientBuilderMock).queryParamStyle(QueryParamStyle.COMMA_SEPARATED); - - Mockito.verify(restClientBuilderMock).trustStore(Mockito.any()); - Mockito.verify(restClientBuilderMock).keyStore(Mockito.any(), Mockito.anyString()); + verify(restClientBuilderMock).baseUrl(new URL("http://localhost")); + verify(restClientBuilderMock).proxyAddress("host1", 123); + verify(restClientBuilderMock).connectTimeout(100, MILLISECONDS); + verify(restClientBuilderMock).readTimeout(101, MILLISECONDS); + verify(restClientBuilderMock).hostnameVerifier(Mockito.any(MyHostnameVerifier1.class)); + verify(restClientBuilderMock).property("resteasy.connectionTTL", Arrays.asList(102, MILLISECONDS)); + verify(restClientBuilderMock).property("resteasy.connectionPoolSize", 103); + verify(restClientBuilderMock).followRedirects(true); + verify(restClientBuilderMock).register(MyResponseFilter1.class); + verify(restClientBuilderMock).queryParamStyle(COMMA_SEPARATED); + + verify(restClientBuilderMock).trustStore(Mockito.any()); + verify(restClientBuilderMock).keyStore(Mockito.any(), Mockito.anyString()); } @Test public void testGlobalConfigs() throws MalformedURLException { // given - RestClientsConfig configRoot = createSampleConfigRoot(); + RestClientsConfig configRoot = ConfigUtils.emptyConfigBuilder() + .setAddDefaultSources(false) + .withMapping(RestClientsConfig.class) + .withDefaultValues(createSampleConfigRoot()) + .build() + .getConfigMapping(RestClientsConfig.class); // when @@ -131,69 +144,64 @@ public void testGlobalConfigs() throws MalformedURLException { // then - Mockito.verify(restClientBuilderMock).baseUrl(new URL("http://localhost:8080")); - Mockito.verify(restClientBuilderMock).proxyAddress("host2", 123); - Mockito.verify(restClientBuilderMock).connectTimeout(200, TimeUnit.MILLISECONDS); - Mockito.verify(restClientBuilderMock).readTimeout(201, TimeUnit.MILLISECONDS); - Mockito.verify(restClientBuilderMock).hostnameVerifier(Mockito.any(MyHostnameVerifier2.class)); - Mockito.verify(restClientBuilderMock).property("resteasy.connectionTTL", Arrays.asList(202, TimeUnit.MILLISECONDS)); - Mockito.verify(restClientBuilderMock).property("resteasy.connectionPoolSize", 203); - Mockito.verify(restClientBuilderMock).followRedirects(true); - Mockito.verify(restClientBuilderMock).register(MyResponseFilter2.class); - Mockito.verify(restClientBuilderMock).queryParamStyle(QueryParamStyle.MULTI_PAIRS); - - Mockito.verify(restClientBuilderMock).trustStore(Mockito.any()); - Mockito.verify(restClientBuilderMock).keyStore(Mockito.any(), Mockito.anyString()); + verify(restClientBuilderMock).baseUrl(new URL("http://localhost:8080")); + verify(restClientBuilderMock).proxyAddress("host2", 123); + verify(restClientBuilderMock).connectTimeout(200, MILLISECONDS); + verify(restClientBuilderMock).readTimeout(201, MILLISECONDS); + verify(restClientBuilderMock).hostnameVerifier(Mockito.any(MyHostnameVerifier2.class)); + verify(restClientBuilderMock).property("resteasy.connectionTTL", Arrays.asList(202, MILLISECONDS)); + verify(restClientBuilderMock).property("resteasy.connectionPoolSize", 203); + verify(restClientBuilderMock).followRedirects(true); + verify(restClientBuilderMock).register(MyResponseFilter2.class); + verify(restClientBuilderMock).queryParamStyle(MULTI_PAIRS); + + verify(restClientBuilderMock).trustStore(Mockito.any()); + verify(restClientBuilderMock).keyStore(Mockito.any(), Mockito.anyString()); } - private static RestClientsConfig createSampleConfigRoot() { - RestClientsConfig configRoot = new RestClientsConfig(); - - configRoot.proxyAddress = Optional.of("host2:123"); - configRoot.connectTimeout = 200L; - configRoot.readTimeout = 201L; - configRoot.hostnameVerifier = Optional.of("io.quarkus.restclient.runtime.RestClientBaseTest$MyHostnameVerifier2"); - configRoot.connectionTTL = Optional.of(202); - configRoot.connectionPoolSize = Optional.of(203); - configRoot.followRedirects = Optional.of(true); - configRoot.providers = Optional.of("io.quarkus.restclient.runtime.RestClientBaseTest$MyResponseFilter2"); - configRoot.queryParamStyle = Optional.of(QueryParamStyle.MULTI_PAIRS); - - configRoot.trustStore = Optional.of(truststorePath.toAbsolutePath().toString()); - configRoot.trustStorePassword = Optional.of("truststorePassword"); - configRoot.trustStoreType = Optional.of("JKS"); - configRoot.keyStore = Optional.of(keystorePath.toAbsolutePath().toString()); - configRoot.keyStorePassword = Optional.of("keystorePassword"); - configRoot.keyStoreType = Optional.of("JKS"); - - return configRoot; + private static Map createSampleConfigRoot() { + Map rootConfig = new HashMap<>(); + rootConfig.put("quarkus.rest-client.proxy-address", "host2:123"); + rootConfig.put("quarkus.rest-client.connect-timeout", "200"); + rootConfig.put("quarkus.rest-client.read-timeout", "201"); + rootConfig.put("quarkus.rest-client.hostname-verifier", + "io.quarkus.restclient.runtime.RestClientBaseTest$MyHostnameVerifier2"); + rootConfig.put("quarkus.rest-client.connection-ttl", "202"); + rootConfig.put("quarkus.rest-client.connection-pool-size", "203"); + rootConfig.put("quarkus.rest-client.follow-redirects", "true"); + rootConfig.put("quarkus.rest-client.providers", "io.quarkus.restclient.runtime.RestClientBaseTest$MyResponseFilter2"); + rootConfig.put("quarkus.rest-client.query-param-style", "multi-pairs"); + rootConfig.put("quarkus.rest-client.trust-store", truststorePath.toAbsolutePath().toString()); + rootConfig.put("quarkus.rest-client.trust-store-password", "truststorePassword"); + rootConfig.put("quarkus.rest-client.trust-store-type", "JKS"); + rootConfig.put("quarkus.rest-client.key-store", keystorePath.toAbsolutePath().toString()); + rootConfig.put("quarkus.rest-client.key-store-password", "keystorePassword"); + rootConfig.put("quarkus.rest-client.key-store-type", "JKS"); + return rootConfig; } - private static RestClientConfig createSampleClientConfig() { - RestClientConfig clientConfig = new RestClientConfig(); - + private static Map createSampleClientConfig(final String restClientName) { + Map clientConfig = new HashMap<>(); // properties only configurable via client config - clientConfig.url = Optional.of("http://localhost"); - clientConfig.uri = Optional.empty(); - + clientConfig.put("quarkus.rest-client." + restClientName + ".url", "http://localhost"); // properties that override configRoot counterparts - clientConfig.proxyAddress = Optional.of("host1:123"); - clientConfig.connectTimeout = Optional.of(100L); - clientConfig.readTimeout = Optional.of(101L); - clientConfig.hostnameVerifier = Optional.of("io.quarkus.restclient.runtime.RestClientBaseTest$MyHostnameVerifier1"); - clientConfig.connectionTTL = Optional.of(102); - clientConfig.connectionPoolSize = Optional.of(103); - clientConfig.followRedirects = Optional.of(true); - clientConfig.providers = Optional.of("io.quarkus.restclient.runtime.RestClientBaseTest$MyResponseFilter1"); - clientConfig.queryParamStyle = Optional.of(QueryParamStyle.COMMA_SEPARATED); - - clientConfig.trustStore = Optional.of(truststorePath.toAbsolutePath().toString()); - clientConfig.trustStorePassword = Optional.of("truststorePassword"); - clientConfig.trustStoreType = Optional.of("JKS"); - clientConfig.keyStore = Optional.of(keystorePath.toAbsolutePath().toString()); - clientConfig.keyStorePassword = Optional.of("keystorePassword"); - clientConfig.keyStoreType = Optional.of("JKS"); - + clientConfig.put("quarkus.rest-client." + restClientName + ".proxy-address", "host1:123"); + clientConfig.put("quarkus.rest-client." + restClientName + ".connect-timeout", "100"); + clientConfig.put("quarkus.rest-client." + restClientName + ".read-timeout", "101"); + clientConfig.put("quarkus.rest-client." + restClientName + ".hostname-verifier", + "io.quarkus.restclient.runtime.RestClientBaseTest$MyHostnameVerifier1"); + clientConfig.put("quarkus.rest-client." + restClientName + ".connection-ttl", "102"); + clientConfig.put("quarkus.rest-client." + restClientName + ".connection-pool-size", "103"); + clientConfig.put("quarkus.rest-client." + restClientName + ".follow-redirects", "true"); + clientConfig.put("quarkus.rest-client." + restClientName + ".providers", + "io.quarkus.restclient.runtime.RestClientBaseTest$MyResponseFilter1"); + clientConfig.put("quarkus.rest-client." + restClientName + ".query-param-style", "comma-separated"); + clientConfig.put("quarkus.rest-client." + restClientName + ".trust-store", truststorePath.toAbsolutePath().toString()); + clientConfig.put("quarkus.rest-client." + restClientName + ".trust-store-password", "truststorePassword"); + clientConfig.put("quarkus.rest-client." + restClientName + ".trust-store-type", "JKS"); + clientConfig.put("quarkus.rest-client." + restClientName + ".key-store", keystorePath.toAbsolutePath().toString()); + clientConfig.put("quarkus.rest-client." + restClientName + ".key-store-password", "keystorePassword"); + clientConfig.put("quarkus.rest-client." + restClientName + ".key-store-type", "JKS"); return clientConfig; } diff --git a/extensions/resteasy-classic/resteasy-common/deployment/src/main/java/io/quarkus/resteasy/common/deployment/RestClientBuildItem.java b/extensions/resteasy-classic/resteasy-common/deployment/src/main/java/io/quarkus/resteasy/common/deployment/RestClientBuildItem.java index cb7834d9beae9..33190120db3f5 100644 --- a/extensions/resteasy-classic/resteasy-common/deployment/src/main/java/io/quarkus/resteasy/common/deployment/RestClientBuildItem.java +++ b/extensions/resteasy-classic/resteasy-common/deployment/src/main/java/io/quarkus/resteasy/common/deployment/RestClientBuildItem.java @@ -1,5 +1,10 @@ package io.quarkus.resteasy.common.deployment; +import java.util.Objects; +import java.util.Optional; + +import org.jboss.jandex.ClassInfo; + import io.quarkus.builder.item.MultiBuildItem; /** @@ -10,12 +15,29 @@ public final class RestClientBuildItem extends MultiBuildItem { private String interfaceName; + private final ClassInfo classInfo; + private final Optional configKey; + private final Optional defaultBaseUri; - public RestClientBuildItem(String interfaceName) { - this.interfaceName = interfaceName; + public RestClientBuildItem(ClassInfo classInfo, Optional configKey, Optional defaultBaseUri) { + this.classInfo = Objects.requireNonNull(classInfo); + this.configKey = Objects.requireNonNull(configKey); + this.defaultBaseUri = Objects.requireNonNull(defaultBaseUri); } public String getInterfaceName() { - return interfaceName; + return classInfo.name().toString(); + } + + public ClassInfo getClassInfo() { + return classInfo; + } + + public Optional getConfigKey() { + return configKey; + } + + public Optional getDefaultBaseUri() { + return defaultBaseUri; } } diff --git a/extensions/resteasy-reactive/rest-client/deployment/src/main/java/io/quarkus/rest/client/reactive/deployment/RestClientReactiveProcessor.java b/extensions/resteasy-reactive/rest-client/deployment/src/main/java/io/quarkus/rest/client/reactive/deployment/RestClientReactiveProcessor.java index 3a144757d81ab..3fb8b64565612 100644 --- a/extensions/resteasy-reactive/rest-client/deployment/src/main/java/io/quarkus/rest/client/reactive/deployment/RestClientReactiveProcessor.java +++ b/extensions/resteasy-reactive/rest-client/deployment/src/main/java/io/quarkus/rest/client/reactive/deployment/RestClientReactiveProcessor.java @@ -87,6 +87,8 @@ import io.quarkus.deployment.builditem.ExtensionSslNativeSupportBuildItem; import io.quarkus.deployment.builditem.FeatureBuildItem; import io.quarkus.deployment.builditem.GeneratedClassBuildItem; +import io.quarkus.deployment.builditem.RunTimeConfigBuilderBuildItem; +import io.quarkus.deployment.builditem.StaticInitConfigBuilderBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem; import io.quarkus.deployment.execannotations.ExecutionModelAnnotationsAllowedBuildItem; @@ -105,6 +107,7 @@ import io.quarkus.rest.client.reactive.runtime.RestClientReactiveConfig; import io.quarkus.rest.client.reactive.runtime.RestClientRecorder; import io.quarkus.rest.client.reactive.spi.RestClientAnnotationsTransformerBuildItem; +import io.quarkus.restclient.config.RegisteredRestClient; import io.quarkus.restclient.config.RestClientsBuildTimeConfig; import io.quarkus.restclient.config.RestClientsConfig; import io.quarkus.restclient.config.deployment.RestClientConfigUtils; @@ -431,7 +434,7 @@ void determineRegisteredRestClients(CombinedIndexBuildItem combinedIndexBuildIte } // now we go through the keys and if any of them correspond to classes that don't have a @RegisterRestClient annotation, we fake that annotation - Set configKeyNames = clientsConfig.configs.keySet(); + Set configKeyNames = clientsConfig.clients().keySet(); for (String configKeyName : configKeyNames) { ClassInfo classInfo = index.getClassByName(configKeyName); if (classInfo == null) { @@ -443,7 +446,7 @@ void determineRegisteredRestClients(CombinedIndexBuildItem combinedIndexBuildIte if (!Modifier.isAbstract(classInfo.flags())) { continue; } - Optional cdiScope = clientsConfig.configs.get(configKeyName).scope; + Optional cdiScope = clientsConfig.clients().get(configKeyName).scope(); if (cdiScope.isEmpty()) { continue; } @@ -451,6 +454,24 @@ void determineRegisteredRestClients(CombinedIndexBuildItem combinedIndexBuildIte } } + @BuildStep + void generateRestClientConfigBuilder( + List restClients, + BuildProducer generatedClass, + BuildProducer staticInitConfigBuilder, + BuildProducer runTimeConfigBuilder) { + + List registeredRestClients = restClients.stream() + .map(rc -> new RegisteredRestClient( + rc.getClassInfo().name().toString(), + rc.getClassInfo().simpleName(), + rc.getConfigKey().orElse(null))) + .toList(); + + RestClientConfigUtils.generateRestClientConfigBuilder(registeredRestClients, generatedClass, staticInitConfigBuilder, + runTimeConfigBuilder); + } + @BuildStep @Record(ExecutionTime.STATIC_INIT) void addRestClientBeans(Capabilities capabilities, diff --git a/extensions/resteasy-reactive/rest-client/deployment/src/main/java/io/quarkus/rest/client/reactive/deployment/devservices/DevServicesRestClientHttpProxyProcessor.java b/extensions/resteasy-reactive/rest-client/deployment/src/main/java/io/quarkus/rest/client/reactive/deployment/devservices/DevServicesRestClientHttpProxyProcessor.java index 60ac73dac04ff..a1a4d5c8f8fb8 100644 --- a/extensions/resteasy-reactive/rest-client/deployment/src/main/java/io/quarkus/rest/client/reactive/deployment/devservices/DevServicesRestClientHttpProxyProcessor.java +++ b/extensions/resteasy-reactive/rest-client/deployment/src/main/java/io/quarkus/rest/client/reactive/deployment/devservices/DevServicesRestClientHttpProxyProcessor.java @@ -29,9 +29,8 @@ import io.quarkus.rest.client.reactive.deployment.RegisteredRestClientBuildItem; import io.quarkus.rest.client.reactive.spi.DevServicesRestClientProxyProvider; import io.quarkus.rest.client.reactive.spi.RestClientHttpProxyBuildItem; -import io.quarkus.restclient.config.RestClientBuildConfig; -import io.quarkus.restclient.config.RestClientConfig; import io.quarkus.restclient.config.RestClientsBuildTimeConfig; +import io.quarkus.restclient.config.RestClientsBuildTimeConfig.RestClientBuildConfig; @BuildSteps(onlyIfNot = IsNormal.class) public class DevServicesRestClientHttpProxyProcessor { @@ -56,15 +55,15 @@ public void determineRequiredProxies(RestClientsBuildTimeConfig restClientsBuild CombinedIndexBuildItem combinedIndexBuildItem, List registeredRestClientBuildItems, BuildProducer producer) { - if (restClientsBuildTimeConfig.configs.isEmpty()) { + if (restClientsBuildTimeConfig.clients().isEmpty()) { return; } IndexView index = combinedIndexBuildItem.getIndex(); - Map configs = restClientsBuildTimeConfig.configs; + Map configs = restClientsBuildTimeConfig.clients(); for (var configEntry : configs.entrySet()) { - if (!configEntry.getValue().enableLocalProxy) { + if (!configEntry.getValue().enableLocalProxy()) { log.trace("Ignoring config key: '" + configEntry.getKey() + "' because enableLocalProxy is false"); break; } @@ -81,8 +80,8 @@ public void determineRequiredProxies(RestClientsBuildTimeConfig restClientsBuild } if (matchingBI != null) { Optional baseUri = oneOf( - RestClientConfig.getConfigValue(configKey, "uri", String.class), - RestClientConfig.getConfigValue(configKey, "url", String.class), + Optional.of(configEntry.getValue().properties().get("url")), + Optional.of(configEntry.getValue().properties().get("url")), matchingBI.getDefaultBaseUri()); if (baseUri.isEmpty()) { @@ -90,7 +89,7 @@ public void determineRequiredProxies(RestClientsBuildTimeConfig restClientsBuild break; } producer.produce(new RestClientHttpProxyBuildItem(matchingBI.getClassInfo().name().toString(), baseUri.get(), - configEntry.getValue().localProxyProvider)); + configEntry.getValue().localProxyProvider())); } else { // now we check if the configKey was actually a class name ClassInfo classInfo = index.getClassByName(configKey); @@ -100,14 +99,14 @@ public void determineRequiredProxies(RestClientsBuildTimeConfig restClientsBuild break; } Optional baseUri = oneOf( - RestClientConfig.getConfigValue(configKey, "uri", String.class), - RestClientConfig.getConfigValue(configKey, "url", String.class)); + Optional.of(configEntry.getValue().properties().get("url")), + Optional.of(configEntry.getValue().properties().get("url"))); if (baseUri.isEmpty()) { log.debug("Unable to determine uri or url for config key '" + configKey + "'"); break; } producer.produce(new RestClientHttpProxyBuildItem(classInfo.name().toString(), baseUri.get(), - configEntry.getValue().localProxyProvider)); + configEntry.getValue().localProxyProvider())); } } } diff --git a/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/ConfigurationTest.java b/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/ConfigurationTest.java index eb6011ebd3ff0..53d7ce2e4a5d7 100644 --- a/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/ConfigurationTest.java +++ b/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/ConfigurationTest.java @@ -2,11 +2,13 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Set; import jakarta.enterprise.inject.spi.Bean; import jakarta.enterprise.inject.spi.BeanManager; +import jakarta.inject.Inject; import jakarta.inject.Singleton; import org.eclipse.microprofile.rest.client.ext.QueryParamStyle; @@ -16,7 +18,7 @@ import io.quarkus.arc.Arc; import io.quarkus.rest.client.reactive.configuration.EchoResource; -import io.quarkus.restclient.config.RestClientConfig; +import io.quarkus.restclient.config.RestClientsConfig; import io.quarkus.test.QuarkusUnitTest; public class ConfigurationTest { @@ -29,9 +31,10 @@ public class ConfigurationTest { @RestClient HelloClientWithBaseUri client; - @RestClient EchoClientWithEmptyPath echoClientWithEmptyPath; + @Inject + RestClientsConfig restClientsConfig; @Test void shouldHaveSingletonScope() { @@ -48,21 +51,21 @@ void clientShouldRespond() { @Test void checkClientSpecificConfigs() { - RestClientConfig clientConfig = RestClientConfig.load(io.quarkus.rest.client.reactive.HelloClientWithBaseUri.class); + RestClientsConfig.RestClientConfig clientConfig = restClientsConfig.getClient(HelloClientWithBaseUri.class); verifyClientConfig(clientConfig, true); - clientConfig = RestClientConfig.load("client-prefix"); + clientConfig = restClientsConfig.getClient("client-prefix"); verifyClientConfig(clientConfig, true); - assertThat(clientConfig.proxyAddress.isPresent()).isTrue(); - assertThat(clientConfig.proxyAddress.get()).isEqualTo("localhost:8080"); - assertThat(clientConfig.headers).containsOnly(entry("user-agent", "MP REST Client"), entry("foo", "bar")); + assertThat(clientConfig.proxyAddress().isPresent()).isTrue(); + assertThat(clientConfig.proxyAddress().get()).isEqualTo("localhost:8080"); + assertThat(clientConfig.headers()).containsOnly(entry("user-agent", "MP REST Client"), entry("foo", "bar")); - clientConfig = RestClientConfig.load("quoted-client-prefix"); - assertThat(clientConfig.url.isPresent()).isTrue(); - assertThat(clientConfig.url.get()).endsWith("/hello"); - assertThat(clientConfig.headers).containsOnly(entry("foo", "bar")); + clientConfig = restClientsConfig.getClient("quoted-client-prefix"); + assertThat(clientConfig.url().isPresent()).isTrue(); + assertThat(clientConfig.url().get()).endsWith("/hello"); + assertThat(clientConfig.headers()).containsOnly(entry("foo", "bar")); - clientConfig = RestClientConfig.load("mp-client-prefix"); + clientConfig = restClientsConfig.getClient("mp-client-prefix"); verifyClientConfig(clientConfig, false); } @@ -71,30 +74,30 @@ void emptyPathAnnotationShouldWork() { assertThat(echoClientWithEmptyPath.echo("hello", "hello world")).isEqualTo("hello world"); } - private void verifyClientConfig(RestClientConfig clientConfig, boolean checkExtraProperties) { - assertThat(clientConfig.url).isPresent(); - assertThat(clientConfig.url.get()).endsWith("/hello"); - assertThat(clientConfig.providers).isPresent(); - assertThat(clientConfig.providers.get()) + private void verifyClientConfig(RestClientsConfig.RestClientConfig clientConfig, boolean checkExtraProperties) { + assertTrue(clientConfig.url().isPresent()); + assertThat(clientConfig.url().get()).endsWith("/hello"); + assertTrue(clientConfig.providers().isPresent()); + assertThat(clientConfig.providers().get()) .isEqualTo("io.quarkus.rest.client.reactive.HelloClientWithBaseUri$MyResponseFilter"); - assertThat(clientConfig.connectTimeout).isPresent(); - assertThat(clientConfig.connectTimeout.get()).isEqualTo(5000); - assertThat(clientConfig.readTimeout).isPresent(); - assertThat(clientConfig.readTimeout.get()).isEqualTo(6000); - assertThat(clientConfig.followRedirects).isPresent(); - assertThat(clientConfig.followRedirects.get()).isEqualTo(true); - assertThat(clientConfig.queryParamStyle).isPresent(); - assertThat(clientConfig.queryParamStyle.get()).isEqualTo(QueryParamStyle.COMMA_SEPARATED); + assertTrue(clientConfig.connectTimeout().isPresent()); + assertThat(clientConfig.connectTimeout().get()).isEqualTo(5000); + assertTrue(clientConfig.readTimeout().isPresent()); + assertThat(clientConfig.readTimeout().get()).isEqualTo(6000); + assertTrue(clientConfig.followRedirects().isPresent()); + assertThat(clientConfig.followRedirects().get()).isEqualTo(true); + assertTrue(clientConfig.queryParamStyle().isPresent()); + assertThat(clientConfig.queryParamStyle().get()).isEqualTo(QueryParamStyle.COMMA_SEPARATED); if (checkExtraProperties) { - assertThat(clientConfig.connectionTTL).isPresent(); - assertThat(clientConfig.connectionTTL.get()).isEqualTo(30000); - assertThat(clientConfig.connectionPoolSize).isPresent(); - assertThat(clientConfig.connectionPoolSize.get()).isEqualTo(10); - assertThat(clientConfig.keepAliveEnabled).isPresent(); - assertThat(clientConfig.keepAliveEnabled.get()).isFalse(); - assertThat(clientConfig.maxRedirects).isPresent(); - assertThat(clientConfig.maxRedirects.get()).isEqualTo(5); + assertTrue(clientConfig.connectionTTL().isPresent()); + assertThat(clientConfig.connectionTTL().get()).isEqualTo(30000); + assertTrue(clientConfig.connectionPoolSize().isPresent()); + assertThat(clientConfig.connectionPoolSize().get()).isEqualTo(10); + assertTrue(clientConfig.keepAliveEnabled().isPresent()); + assertThat(clientConfig.keepAliveEnabled().get()).isFalse(); + assertTrue(clientConfig.maxRedirects().isPresent()); + assertThat(clientConfig.maxRedirects().get()).isEqualTo(5); } } } diff --git a/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/GlobalConfigurationTest.java b/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/GlobalConfigurationTest.java index cf10f86171208..62f0426767e1d 100644 --- a/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/GlobalConfigurationTest.java +++ b/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/GlobalConfigurationTest.java @@ -50,34 +50,33 @@ void shouldRespond() { @Test void checkGlobalConfigValues() { // global properties: - assertThat(configRoot.multipartPostEncoderMode.get()).isEqualTo("HTML5"); - assertThat(configRoot.disableContextualErrorMessages).isTrue(); + assertThat(configRoot.multipartPostEncoderMode().get()).isEqualTo("HTML5"); + assertThat(configRoot.disableContextualErrorMessages()).isTrue(); // global defaults for client specific properties: - assertThat(configRoot.scope.get()).isEqualTo("Singleton"); - assertThat(configRoot.proxyAddress.get()).isEqualTo("host:123"); - assertThat(configRoot.proxyUser.get()).isEqualTo("proxyUser"); - assertThat(configRoot.proxyPassword.get()).isEqualTo("proxyPassword"); - assertThat(configRoot.nonProxyHosts.get()).isEqualTo("nonProxyHosts"); - assertThat(configRoot.connectTimeout).isEqualTo(2000); - assertThat(configRoot.readTimeout).isEqualTo(2001); - assertThat(configRoot.userAgent.get()).isEqualTo("agent"); - assertThat(configRoot.headers).isEqualTo(Collections.singletonMap("foo", "bar")); - assertThat(configRoot.connectionTTL.get()).isEqualTo(20000); // value in ms, will be converted to seconds - assertThat(configRoot.connectionPoolSize.get()).isEqualTo(2); - assertThat(configRoot.keepAliveEnabled.get()).isTrue(); - assertThat(configRoot.maxRedirects.get()).isEqualTo(2); - assertThat(configRoot.followRedirects.get()).isTrue(); - assertThat(configRoot.providers.get()) + assertThat(configRoot.scope().get()).isEqualTo("Singleton"); + assertThat(configRoot.proxyAddress().get()).isEqualTo("host:123"); + assertThat(configRoot.proxyUser().get()).isEqualTo("proxyUser"); + assertThat(configRoot.proxyPassword().get()).isEqualTo("proxyPassword"); + assertThat(configRoot.nonProxyHosts().get()).isEqualTo("nonProxyHosts"); + assertThat(configRoot.connectTimeout()).isEqualTo(2000); + assertThat(configRoot.readTimeout()).isEqualTo(2001); + assertThat(configRoot.userAgent().get()).isEqualTo("agent"); + assertThat(configRoot.headers()).isEqualTo(Collections.singletonMap("foo", "bar")); + assertThat(configRoot.connectionTTL().get()).isEqualTo(20000); // value in ms, will be converted to seconds + assertThat(configRoot.connectionPoolSize().get()).isEqualTo(2); + assertThat(configRoot.keepAliveEnabled().get()).isTrue(); + assertThat(configRoot.maxRedirects().get()).isEqualTo(2); + assertThat(configRoot.followRedirects().get()).isTrue(); + assertThat(configRoot.providers().get()) .isEqualTo("io.quarkus.rest.client.reactive.HelloClientWithBaseUri$MyResponseFilter"); - assertThat(configRoot.queryParamStyle.get()).isEqualTo(QueryParamStyle.MULTI_PAIRS); + assertThat(configRoot.queryParamStyle().get()).isEqualTo(QueryParamStyle.MULTI_PAIRS); - assertThat(configRoot.trustStore.get()).isEqualTo("/path"); - assertThat(configRoot.trustStorePassword.get()).isEqualTo("password"); - assertThat(configRoot.trustStoreType.get()).isEqualTo("JKS"); - assertThat(configRoot.keyStore.get()).isEqualTo("/path"); - assertThat(configRoot.keyStorePassword.get()).isEqualTo("password"); - assertThat(configRoot.keyStoreType.get()).isEqualTo("JKS"); + assertThat(configRoot.trustStore().get()).isEqualTo("/path"); + assertThat(configRoot.trustStorePassword().get()).isEqualTo("password"); + assertThat(configRoot.trustStoreType().get()).isEqualTo("JKS"); + assertThat(configRoot.keyStore().get()).isEqualTo("/path"); + assertThat(configRoot.keyStorePassword().get()).isEqualTo("password"); + assertThat(configRoot.keyStoreType().get()).isEqualTo("JKS"); } - } diff --git a/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/LegacyConfigurationTest.java b/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/LegacyConfigurationTest.java index 3075c625662ef..9a058f424c103 100644 --- a/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/LegacyConfigurationTest.java +++ b/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/LegacyConfigurationTest.java @@ -1,6 +1,7 @@ package io.quarkus.rest.client.reactive; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertTrue; import jakarta.inject.Inject; @@ -8,7 +9,6 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.rest.client.reactive.configuration.EchoResource; -import io.quarkus.restclient.config.RestClientConfig; import io.quarkus.restclient.config.RestClientsConfig; import io.quarkus.test.QuarkusUnitTest; @@ -25,16 +25,15 @@ public class LegacyConfigurationTest { @Test void configurationShouldBeLoaded() { - assertThat(configRoot.multipartPostEncoderMode).isPresent(); - assertThat(configRoot.multipartPostEncoderMode.get()).isEqualTo("RFC3986"); + assertTrue(configRoot.multipartPostEncoderMode().isPresent()); + assertThat(configRoot.multipartPostEncoderMode().get()).isEqualTo("RFC3986"); - RestClientConfig clientConfig = RestClientConfig.load(io.quarkus.rest.client.reactive.HelloClient.class); - assertThat(clientConfig.maxRedirects).isPresent(); - assertThat(clientConfig.maxRedirects.get()).isEqualTo(4); + RestClientsConfig.RestClientConfig clientConfig = configRoot.getClient(HelloClient.class); + assertTrue(clientConfig.maxRedirects().isPresent()); + assertThat(clientConfig.maxRedirects().get()).isEqualTo(4); - clientConfig = RestClientConfig.load("client-prefix"); - assertThat(clientConfig.maxRedirects).isPresent(); - assertThat(clientConfig.maxRedirects.get()).isEqualTo(4); + clientConfig = configRoot.getClient("client-prefix"); + assertTrue(clientConfig.maxRedirects().isPresent()); + assertThat(clientConfig.maxRedirects().get()).isEqualTo(4); } - } diff --git a/extensions/resteasy-reactive/rest-client/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/RestClientBuilderImpl.java b/extensions/resteasy-reactive/rest-client/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/RestClientBuilderImpl.java index 5020e1bdcbf7c..828034de5c9be 100644 --- a/extensions/resteasy-reactive/rest-client/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/RestClientBuilderImpl.java +++ b/extensions/resteasy-reactive/rest-client/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/RestClientBuilderImpl.java @@ -45,10 +45,9 @@ import io.quarkus.arc.ArcContainer; import io.quarkus.arc.InstanceHandle; import io.quarkus.rest.client.reactive.runtime.ProxyAddressUtil.HostAndPort; -import io.quarkus.restclient.config.RestClientConfig; -import io.quarkus.restclient.config.RestClientLoggingConfig; import io.quarkus.restclient.config.RestClientsConfig; import io.quarkus.tls.TlsConfiguration; +import io.smallrye.config.SmallRyeConfig; import io.vertx.core.net.KeyCertOptions; import io.vertx.core.net.SSLOptions; import io.vertx.core.net.TrustOptions; @@ -396,8 +395,11 @@ public T build(Class aClass) throws IllegalStateException, RestClientDefi "The Reactive REST Client needs to be built within the context of a Quarkus application with a valid ArC (CDI) context running."); } + SmallRyeConfig config = ConfigProvider.getConfig().unwrap(SmallRyeConfig.class); + RestClientsConfig restClients = config.getConfigMapping(RestClientsConfig.class); + // support overriding the URI from the override-uri property - Optional maybeOverrideUri = RestClientConfig.getConfigValue(aClass, "override-uri", String.class); + Optional maybeOverrideUri = restClients.getClient(aClass).overrideUri(); if (maybeOverrideUri.isPresent()) { uri = URI.create(maybeOverrideUri.get()); } @@ -427,19 +429,17 @@ public T build(Class aClass) throws IllegalStateException, RestClientDefi clientBuilder.register(new MicroProfileRestClientResponseFilter(exceptionMappers)); clientBuilder.followRedirects(followRedirects); - RestClientsConfig restClientsConfig = arcContainer.instance(RestClientsConfig.class).get(); - - RestClientLoggingConfig logging = restClientsConfig.logging; + RestClientsConfig.RestClientLoggingConfig logging = restClients.logging(); LoggingScope effectiveLoggingScope = loggingScope; // if a scope was specified programmatically, it takes precedence if (effectiveLoggingScope == null) { - effectiveLoggingScope = logging != null ? logging.scope.map(LoggingScope::forName).orElse(LoggingScope.NONE) + effectiveLoggingScope = logging != null ? logging.scope().map(LoggingScope::forName).orElse(LoggingScope.NONE) : LoggingScope.NONE; } Integer effectiveLoggingBodyLimit = loggingBodyLimit; // if a limit was specified programmatically, it takes precedence if (effectiveLoggingBodyLimit == null) { - effectiveLoggingBodyLimit = logging != null ? logging.bodyLimit : 100; + effectiveLoggingBodyLimit = logging != null ? logging.bodyLimit() : 100; } clientBuilder.loggingScope(effectiveLoggingScope); clientBuilder.loggingBodySize(effectiveLoggingBodyLimit); @@ -461,36 +461,36 @@ public T build(Class aClass) throws IllegalStateException, RestClientDefi } clientBuilder.trustAll(effectiveTrustAll); - restClientsConfig.verifyHost.ifPresent(clientBuilder::verifyHost); + restClients.verifyHost().ifPresent(clientBuilder::verifyHost); String effectiveUserAgent = userAgent; if (effectiveUserAgent != null) { clientBuilder.setUserAgent(effectiveUserAgent); - } else if (restClientsConfig.userAgent.isPresent()) { // if config set and client obtained programmatically - clientBuilder.setUserAgent(restClientsConfig.userAgent.get()); + } else if (restClients.userAgent().isPresent()) { // if config set and client obtained programmatically + clientBuilder.setUserAgent(restClients.userAgent().get()); } Integer maxChunkSize = (Integer) getConfiguration().getProperty(QuarkusRestClientProperties.MAX_CHUNK_SIZE); if (maxChunkSize != null) { clientBuilder.maxChunkSize(maxChunkSize); - } else if (restClientsConfig.maxChunkSize.isPresent()) { - clientBuilder.maxChunkSize((int) restClientsConfig.maxChunkSize.get().asLongValue()); - } else if (restClientsConfig.multipart.maxChunkSize.isPresent()) { - clientBuilder.maxChunkSize(restClientsConfig.multipart.maxChunkSize.get()); + } else if (restClients.maxChunkSize().isPresent()) { + clientBuilder.maxChunkSize((int) restClients.maxChunkSize().get().asLongValue()); + } else if (restClients.multipart().maxChunkSize().isPresent()) { + clientBuilder.maxChunkSize(restClients.multipart().maxChunkSize().get()); } else { clientBuilder.maxChunkSize(DEFAULT_MAX_CHUNK_SIZE); } if (getConfiguration().hasProperty(QuarkusRestClientProperties.HTTP2)) { clientBuilder.http2((Boolean) getConfiguration().getProperty(QuarkusRestClientProperties.HTTP2)); - } else if (restClientsConfig.http2) { + } else if (restClients.http2()) { clientBuilder.http2(true); } if (getConfiguration().hasProperty(QuarkusRestClientProperties.ALPN)) { clientBuilder.alpn((Boolean) getConfiguration().getProperty(QuarkusRestClientProperties.ALPN)); - } else if (restClientsConfig.alpn.isPresent()) { - clientBuilder.alpn(restClientsConfig.alpn.get()); + } else if (restClients.alpn().isPresent()) { + clientBuilder.alpn(restClients.alpn().get()); } Boolean enableCompression = ConfigProvider.getConfig() @@ -501,10 +501,10 @@ public T build(Class aClass) throws IllegalStateException, RestClientDefi if (proxyHost != null) { configureProxy(proxyHost, proxyPort, proxyUser, proxyPassword, nonProxyHosts); - } else if (restClientsConfig.proxyAddress.isPresent()) { - HostAndPort globalProxy = ProxyAddressUtil.parseAddress(restClientsConfig.proxyAddress.get()); - configureProxy(globalProxy.host, globalProxy.port, restClientsConfig.proxyUser.orElse(null), - restClientsConfig.proxyPassword.orElse(null), restClientsConfig.nonProxyHosts.orElse(null)); + } else if (restClients.proxyAddress().isPresent()) { + HostAndPort globalProxy = ProxyAddressUtil.parseAddress(restClients.proxyAddress().get()); + configureProxy(globalProxy.host, globalProxy.port, restClients.proxyUser().orElse(null), + restClients.proxyPassword().orElse(null), restClients.nonProxyHosts().orElse(null)); } if (!clientBuilder.getConfiguration().hasProperty(QuarkusRestClientProperties.MULTIPART_ENCODER_MODE)) { @@ -512,9 +512,9 @@ public T build(Class aClass) throws IllegalStateException, RestClientDefi if (this.multipartPostEncoderMode != null) { multipartPostEncoderMode = PausableHttpPostRequestEncoder.EncoderMode .valueOf(this.multipartPostEncoderMode.toUpperCase(Locale.ROOT)); - } else if (restClientsConfig.multipartPostEncoderMode.isPresent()) { + } else if (restClients.multipartPostEncoderMode().isPresent()) { multipartPostEncoderMode = PausableHttpPostRequestEncoder.EncoderMode - .valueOf(restClientsConfig.multipartPostEncoderMode.get().toUpperCase(Locale.ROOT)); + .valueOf(restClients.multipartPostEncoderMode().get().toUpperCase(Locale.ROOT)); } if (multipartPostEncoderMode != null) { clientBuilder.property(QuarkusRestClientProperties.MULTIPART_ENCODER_MODE, multipartPostEncoderMode); diff --git a/extensions/resteasy-reactive/rest-client/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/RestClientCDIDelegateBuilder.java b/extensions/resteasy-reactive/rest-client/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/RestClientCDIDelegateBuilder.java index 186ca14629a67..e1d5eb53db670 100644 --- a/extensions/resteasy-reactive/rest-client/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/RestClientCDIDelegateBuilder.java +++ b/extensions/resteasy-reactive/rest-client/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/RestClientCDIDelegateBuilder.java @@ -22,17 +22,18 @@ import javax.net.ssl.HostnameVerifier; +import org.eclipse.microprofile.config.ConfigProvider; import org.eclipse.microprofile.rest.client.ext.QueryParamStyle; import org.jboss.resteasy.reactive.client.api.QuarkusRestClientProperties; import org.jboss.resteasy.reactive.client.impl.multipart.PausableHttpPostRequestEncoder; import io.quarkus.arc.Arc; import io.quarkus.rest.client.reactive.QuarkusRestClientBuilder; -import io.quarkus.restclient.config.RestClientConfig; import io.quarkus.restclient.config.RestClientsConfig; import io.quarkus.runtime.configuration.MemorySize; import io.quarkus.tls.TlsConfiguration; import io.quarkus.tls.TlsConfigurationRegistry; +import io.smallrye.config.SmallRyeConfig; public class RestClientCDIDelegateBuilder { @@ -50,7 +51,8 @@ public static T createDelegate(Class jaxrsInterface, String baseUriFromAn } private RestClientCDIDelegateBuilder(Class jaxrsInterface, String baseUriFromAnnotation, String configKey) { - this(jaxrsInterface, baseUriFromAnnotation, configKey, RestClientsConfig.getInstance()); + this(jaxrsInterface, baseUriFromAnnotation, configKey, + ConfigProvider.getConfig().unwrap(SmallRyeConfig.class).getConfigMapping(RestClientsConfig.class)); } RestClientCDIDelegateBuilder(Class jaxrsInterface, String baseUriFromAnnotation, String configKey, @@ -80,75 +82,75 @@ void configureBuilder(QuarkusRestClientBuilder builder) { } private void configureCustomProperties(QuarkusRestClientBuilder builder) { - Optional encoder = oneOf(clientConfigByClassName().multipartPostEncoderMode, - clientConfigByConfigKey().multipartPostEncoderMode, configRoot.multipartPostEncoderMode); + Optional encoder = oneOf(clientConfigByClassName().multipartPostEncoderMode(), + clientConfigByConfigKey().multipartPostEncoderMode(), configRoot.multipartPostEncoderMode()); if (encoder != null && encoder.isPresent()) { PausableHttpPostRequestEncoder.EncoderMode mode = PausableHttpPostRequestEncoder.EncoderMode .valueOf(encoder.get().toUpperCase(Locale.ROOT)); builder.property(QuarkusRestClientProperties.MULTIPART_ENCODER_MODE, mode); } - Optional poolSize = oneOf(clientConfigByClassName().connectionPoolSize, - clientConfigByConfigKey().connectionPoolSize, configRoot.connectionPoolSize); + 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); + 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); + Optional keepAliveEnabled = oneOf(clientConfigByClassName().keepAliveEnabled(), + clientConfigByConfigKey().keepAliveEnabled(), configRoot.keepAliveEnabled()); if (keepAliveEnabled.isPresent()) { builder.property(QuarkusRestClientProperties.KEEP_ALIVE_ENABLED, keepAliveEnabled.get()); } - Map headers = clientConfigByClassName().headers; + Map headers = clientConfigByClassName().headers(); if (headers == null || headers.isEmpty()) { - headers = clientConfigByConfigKey().headers; + headers = clientConfigByConfigKey().headers(); } if (headers == null || headers.isEmpty()) { - headers = configRoot.headers; + headers = configRoot.headers(); } if (headers != null && !headers.isEmpty()) { builder.property(QuarkusRestClientProperties.STATIC_HEADERS, headers); } builder.property(QuarkusRestClientProperties.DISABLE_CONTEXTUAL_ERROR_MESSAGES, - configRoot.disableContextualErrorMessages); + configRoot.disableContextualErrorMessages()); - Optional userAgent = oneOf(clientConfigByClassName().userAgent, - clientConfigByConfigKey().userAgent, configRoot.userAgent); + Optional userAgent = oneOf(clientConfigByClassName().userAgent(), + clientConfigByConfigKey().userAgent(), configRoot.userAgent()); if (userAgent.isPresent()) { builder.userAgent(userAgent.get()); } Optional maxChunkSize = oneOf( - clientConfigByClassName().maxChunkSize.map(intChunkSize()), - clientConfigByClassName().multipart.maxChunkSize, - clientConfigByConfigKey().maxChunkSize.map(intChunkSize()), - clientConfigByConfigKey().multipart.maxChunkSize, - configRoot.maxChunkSize.map(intChunkSize()), - configRoot.multipart.maxChunkSize); + clientConfigByClassName().maxChunkSize().map(intChunkSize()), + clientConfigByClassName().multipart().maxChunkSize(), + clientConfigByConfigKey().maxChunkSize().map(intChunkSize()), + clientConfigByConfigKey().multipart().maxChunkSize(), + configRoot.maxChunkSize().map(intChunkSize()), + 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); + Boolean http2 = oneOf(clientConfigByClassName().http2(), + clientConfigByConfigKey().http2()).orElse(configRoot.http2()); builder.property(QuarkusRestClientProperties.HTTP2, http2); - Optional alpn = oneOf(clientConfigByClassName().alpn, - clientConfigByConfigKey().alpn, configRoot.alpn); + 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); + Boolean captureStacktrace = oneOf(clientConfigByClassName().captureStacktrace(), + clientConfigByConfigKey().captureStacktrace()).orElse(configRoot.captureStacktrace()); builder.property(QuarkusRestClientProperties.CAPTURE_STACKTRACE, captureStacktrace); } @@ -157,8 +159,8 @@ private static Function intChunkSize() { } private void configureProxy(QuarkusRestClientBuilder builder) { - Optional maybeProxy = oneOf(clientConfigByClassName().proxyAddress, clientConfigByConfigKey().proxyAddress, - configRoot.proxyAddress); + Optional maybeProxy = oneOf(clientConfigByClassName().proxyAddress(), clientConfigByConfigKey().proxyAddress(), + configRoot.proxyAddress()); if (maybeProxy.isEmpty()) { return; } @@ -170,18 +172,20 @@ private void configureProxy(QuarkusRestClientBuilder builder) { ProxyAddressUtil.HostAndPort hostAndPort = ProxyAddressUtil.parseAddress(proxyAddress); builder.proxyAddress(hostAndPort.host, hostAndPort.port); - oneOf(clientConfigByClassName().proxyUser, clientConfigByConfigKey().proxyUser, configRoot.proxyUser) + oneOf(clientConfigByClassName().proxyUser(), clientConfigByConfigKey().proxyUser(), configRoot.proxyUser()) .ifPresent(builder::proxyUser); - oneOf(clientConfigByClassName().proxyPassword, clientConfigByConfigKey().proxyPassword, configRoot.proxyPassword) + oneOf(clientConfigByClassName().proxyPassword(), clientConfigByConfigKey().proxyPassword(), + configRoot.proxyPassword()) .ifPresent(builder::proxyPassword); - oneOf(clientConfigByClassName().nonProxyHosts, clientConfigByConfigKey().nonProxyHosts, configRoot.nonProxyHosts) + oneOf(clientConfigByClassName().nonProxyHosts(), clientConfigByConfigKey().nonProxyHosts(), + configRoot.nonProxyHosts()) .ifPresent(builder::nonProxyHosts); } } private void configureQueryParamStyle(QuarkusRestClientBuilder builder) { - Optional maybeQueryParamStyle = oneOf(clientConfigByClassName().queryParamStyle, - clientConfigByConfigKey().queryParamStyle, configRoot.queryParamStyle); + Optional maybeQueryParamStyle = oneOf(clientConfigByClassName().queryParamStyle(), + clientConfigByConfigKey().queryParamStyle(), configRoot.queryParamStyle()); if (maybeQueryParamStyle.isPresent()) { QueryParamStyle queryParamStyle = maybeQueryParamStyle.get(); builder.queryParamStyle(queryParamStyle); @@ -189,29 +193,29 @@ private void configureQueryParamStyle(QuarkusRestClientBuilder builder) { } private void configureRedirects(QuarkusRestClientBuilder builder) { - Optional maxRedirects = oneOf(clientConfigByClassName().maxRedirects, - clientConfigByConfigKey().maxRedirects, configRoot.maxRedirects); + 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); + 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); + 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); + Optional name = oneOf(clientConfigByClassName().name(), + clientConfigByConfigKey().name()); if (name.isPresent()) { builder.property(QuarkusRestClientProperties.NAME, name.get()); } @@ -232,9 +236,9 @@ private Optional resolveTlsConfigurationForRegistry() { if (Arc.container() != null) { var registry = Arc.container().select(TlsConfigurationRegistry.class).orNull(); if (registry != null) { - Optional maybeTlsConfigurationName = oneOf(clientConfigByClassName().tlsConfigurationName, - clientConfigByConfigKey().tlsConfigurationName, - configRoot.tlsConfigurationName); + Optional maybeTlsConfigurationName = oneOf(clientConfigByClassName().tlsConfigurationName(), + clientConfigByConfigKey().tlsConfigurationName(), + configRoot.tlsConfigurationName()); return TlsConfiguration.from(registry, maybeTlsConfigurationName); } } @@ -242,25 +246,25 @@ private Optional resolveTlsConfigurationForRegistry() { } private void configureTLSFromProperties(QuarkusRestClientBuilder builder) { - Optional maybeTrustStore = oneOf(clientConfigByClassName().trustStore, clientConfigByConfigKey().trustStore, - configRoot.trustStore); + 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); + 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); + Optional maybeHostnameVerifier = oneOf(clientConfigByClassName().hostnameVerifier(), + clientConfigByConfigKey().hostnameVerifier(), configRoot.hostnameVerifier()); if (maybeHostnameVerifier.isPresent()) { registerHostnameVerifier(maybeHostnameVerifier.get(), builder); } - oneOf(clientConfigByClassName().verifyHost, clientConfigByConfigKey().verifyHost, configRoot.verifyHost) + oneOf(clientConfigByClassName().verifyHost(), clientConfigByConfigKey().verifyHost(), configRoot.verifyHost()) .ifPresent(builder::verifyHost); } @@ -285,10 +289,10 @@ private void registerHostnameVerifier(String verifier, QuarkusRestClientBuilder } 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); + 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")); @@ -311,10 +315,10 @@ private void registerKeyStore(String keyStorePath, QuarkusRestClientBuilder buil } 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); + 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")); @@ -362,8 +366,8 @@ private InputStream locateStream(String path) throws FileNotFoundException { } private void configureProviders(QuarkusRestClientBuilder builder) { - Optional maybeProviders = oneOf(clientConfigByClassName().providers, clientConfigByConfigKey().providers, - configRoot.providers); + Optional maybeProviders = oneOf(clientConfigByClassName().providers(), clientConfigByConfigKey().providers(), + configRoot.providers()); if (maybeProviders.isPresent()) { registerProviders(builder, maybeProviders.get()); } @@ -384,26 +388,26 @@ private Class providerClassForName(String name) { } private void configureTimeouts(QuarkusRestClientBuilder builder) { - Long connectTimeout = oneOf(clientConfigByClassName().connectTimeout, - clientConfigByConfigKey().connectTimeout).orElse(this.configRoot.connectTimeout); + 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); + 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); + Optional propertyOptional = oneOf(clientConfigByClassName().uri(), + clientConfigByConfigKey().uri()); if (propertyOptional.isEmpty()) { - propertyOptional = oneOf(clientConfigByClassName().url, - clientConfigByConfigKey().url); + propertyOptional = oneOf(clientConfigByClassName().url(), + clientConfigByConfigKey().url()); } if (((baseUriFromAnnotation == null) || baseUriFromAnnotation.isEmpty()) && propertyOptional.isEmpty()) { @@ -425,12 +429,12 @@ private void configureBaseUrl(QuarkusRestClientBuilder builder) { } } - private RestClientConfig clientConfigByConfigKey() { - return this.configRoot.getClientConfig(configKey); + private RestClientsConfig.RestClientConfig clientConfigByConfigKey() { + return this.configRoot.getClient(configKey); } - private RestClientConfig clientConfigByClassName() { - return this.configRoot.getClientConfig(jaxrsInterface); + private RestClientsConfig.RestClientConfig clientConfigByClassName() { + return this.configRoot.getClient(jaxrsInterface); } @SafeVarargs diff --git a/extensions/resteasy-reactive/rest-client/runtime/src/test/java/io/quarkus/rest/client/reactive/runtime/RestClientCDIDelegateBuilderTest.java b/extensions/resteasy-reactive/rest-client/runtime/src/test/java/io/quarkus/rest/client/reactive/runtime/RestClientCDIDelegateBuilderTest.java index 9ba83692374bf..95eaf821219d5 100644 --- a/extensions/resteasy-reactive/rest-client/runtime/src/test/java/io/quarkus/rest/client/reactive/runtime/RestClientCDIDelegateBuilderTest.java +++ b/extensions/resteasy-reactive/rest-client/runtime/src/test/java/io/quarkus/rest/client/reactive/runtime/RestClientCDIDelegateBuilderTest.java @@ -1,8 +1,21 @@ package io.quarkus.rest.client.reactive.runtime; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static org.eclipse.microprofile.rest.client.ext.QueryParamStyle.COMMA_SEPARATED; +import static org.eclipse.microprofile.rest.client.ext.QueryParamStyle.MULTI_PAIRS; +import static org.jboss.resteasy.reactive.client.api.QuarkusRestClientProperties.CONNECTION_POOL_SIZE; +import static org.jboss.resteasy.reactive.client.api.QuarkusRestClientProperties.CONNECTION_TTL; +import static org.jboss.resteasy.reactive.client.api.QuarkusRestClientProperties.DISABLE_CONTEXTUAL_ERROR_MESSAGES; +import static org.jboss.resteasy.reactive.client.api.QuarkusRestClientProperties.KEEP_ALIVE_ENABLED; +import static org.jboss.resteasy.reactive.client.api.QuarkusRestClientProperties.MAX_CHUNK_SIZE; +import static org.jboss.resteasy.reactive.client.api.QuarkusRestClientProperties.MAX_REDIRECTS; +import static org.jboss.resteasy.reactive.client.api.QuarkusRestClientProperties.MULTIPART_ENCODER_MODE; +import static org.jboss.resteasy.reactive.client.api.QuarkusRestClientProperties.STATIC_HEADERS; +import static org.jboss.resteasy.reactive.client.impl.multipart.PausableHttpPostRequestEncoder.EncoderMode.HTML5; +import static org.mockito.Mockito.verify; + import java.io.IOException; import java.io.OutputStream; -import java.math.BigInteger; import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; @@ -10,27 +23,22 @@ import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; -import java.util.Collections; -import java.util.Optional; -import java.util.concurrent.TimeUnit; +import java.util.HashMap; +import java.util.Map; import jakarta.ws.rs.client.ClientRequestContext; import jakarta.ws.rs.client.ClientResponseContext; import jakarta.ws.rs.client.ClientResponseFilter; -import org.eclipse.microprofile.rest.client.ext.QueryParamStyle; import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; import org.jboss.resteasy.reactive.client.api.QuarkusRestClientProperties; -import org.jboss.resteasy.reactive.client.impl.multipart.PausableHttpPostRequestEncoder; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.mockito.Mockito; -import io.quarkus.restclient.config.RestClientConfig; -import io.quarkus.restclient.config.RestClientMultipartConfig; import io.quarkus.restclient.config.RestClientsConfig; -import io.quarkus.runtime.configuration.MemorySize; +import io.quarkus.runtime.configuration.ConfigUtils; @SuppressWarnings({ "SameParameterValue" }) public class RestClientCDIDelegateBuilderTest { @@ -84,8 +92,13 @@ public static void afterAll() { public void testClientSpecificConfigs() { // given - RestClientsConfig configRoot = createSampleConfigRoot(); - configRoot.putClientConfig("test-client", createSampleClientConfig()); + RestClientsConfig configRoot = ConfigUtils.emptyConfigBuilder() + .setAddDefaultSources(false) + .withMapping(RestClientsConfig.class) + .withDefaultValues(createSampleConfigRoot()) + .withDefaultValues(createSampleClientConfig("test-client")) + .build() + .getConfigMapping(RestClientsConfig.class); // when @@ -97,39 +110,42 @@ public void testClientSpecificConfigs() { // then - Mockito.verify(restClientBuilderMock).baseUri(URI.create("http://localhost")); - Mockito.verify(restClientBuilderMock).property(QuarkusRestClientProperties.SHARED, true); - Mockito.verify(restClientBuilderMock).property(QuarkusRestClientProperties.NAME, "my-client"); - Mockito.verify(restClientBuilderMock).property(QuarkusRestClientProperties.MULTIPART_ENCODER_MODE, - PausableHttpPostRequestEncoder.EncoderMode.HTML5); - - Mockito.verify(restClientBuilderMock).proxyAddress("host1", 123); - Mockito.verify(restClientBuilderMock).proxyUser("proxyUser1"); - Mockito.verify(restClientBuilderMock).proxyPassword("proxyPassword1"); - Mockito.verify(restClientBuilderMock).nonProxyHosts("nonProxyHosts1"); - Mockito.verify(restClientBuilderMock).connectTimeout(100, TimeUnit.MILLISECONDS); - Mockito.verify(restClientBuilderMock).readTimeout(101, TimeUnit.MILLISECONDS); - Mockito.verify(restClientBuilderMock).userAgent("agent1"); - Mockito.verify(restClientBuilderMock).property(QuarkusRestClientProperties.STATIC_HEADERS, - Collections.singletonMap("header1", "value")); - Mockito.verify(restClientBuilderMock).property(QuarkusRestClientProperties.CONNECTION_TTL, 10); // value converted to seconds - Mockito.verify(restClientBuilderMock).property(QuarkusRestClientProperties.CONNECTION_POOL_SIZE, 103); - Mockito.verify(restClientBuilderMock).property(QuarkusRestClientProperties.KEEP_ALIVE_ENABLED, false); - Mockito.verify(restClientBuilderMock).property(QuarkusRestClientProperties.MAX_REDIRECTS, 104); - Mockito.verify(restClientBuilderMock).property(QuarkusRestClientProperties.MAX_CHUNK_SIZE, 1024); - Mockito.verify(restClientBuilderMock).followRedirects(true); - Mockito.verify(restClientBuilderMock).register(MyResponseFilter1.class); - Mockito.verify(restClientBuilderMock).queryParamStyle(QueryParamStyle.COMMA_SEPARATED); - - Mockito.verify(restClientBuilderMock).trustStore(Mockito.any(), Mockito.anyString()); - Mockito.verify(restClientBuilderMock).keyStore(Mockito.any(), Mockito.anyString()); + verify(restClientBuilderMock).baseUri(URI.create("http://localhost")); + verify(restClientBuilderMock).property(QuarkusRestClientProperties.SHARED, true); + verify(restClientBuilderMock).property(QuarkusRestClientProperties.NAME, "my-client"); + verify(restClientBuilderMock).property(MULTIPART_ENCODER_MODE, HTML5); + + verify(restClientBuilderMock).proxyAddress("host1", 123); + verify(restClientBuilderMock).proxyUser("proxyUser1"); + verify(restClientBuilderMock).proxyPassword("proxyPassword1"); + verify(restClientBuilderMock).nonProxyHosts("nonProxyHosts1"); + verify(restClientBuilderMock).connectTimeout(100, MILLISECONDS); + verify(restClientBuilderMock).readTimeout(101, MILLISECONDS); + verify(restClientBuilderMock).userAgent("agent1"); + verify(restClientBuilderMock).property(STATIC_HEADERS, Map.of("header1", "value")); + verify(restClientBuilderMock).property(CONNECTION_TTL, 10); // value converted to seconds + verify(restClientBuilderMock).property(CONNECTION_POOL_SIZE, 103); + verify(restClientBuilderMock).property(KEEP_ALIVE_ENABLED, false); + verify(restClientBuilderMock).property(MAX_REDIRECTS, 104); + verify(restClientBuilderMock).property(MAX_CHUNK_SIZE, 1024); + verify(restClientBuilderMock).followRedirects(true); + verify(restClientBuilderMock).register(MyResponseFilter1.class); + verify(restClientBuilderMock).queryParamStyle(COMMA_SEPARATED); + + verify(restClientBuilderMock).trustStore(Mockito.any(), Mockito.anyString()); + verify(restClientBuilderMock).keyStore(Mockito.any(), Mockito.anyString()); } @Test public void testGlobalConfigs() { // given - RestClientsConfig configRoot = createSampleConfigRoot(); + RestClientsConfig configRoot = ConfigUtils.emptyConfigBuilder() + .setAddDefaultSources(false) + .withMapping(RestClientsConfig.class) + .withDefaultValues(createSampleConfigRoot()) + .build() + .getConfigMapping(RestClientsConfig.class); // when @@ -141,107 +157,95 @@ public void testGlobalConfigs() { // then - Mockito.verify(restClientBuilderMock).baseUri(URI.create("http://localhost:8080")); - Mockito.verify(restClientBuilderMock) - .property(QuarkusRestClientProperties.MULTIPART_ENCODER_MODE, PausableHttpPostRequestEncoder.EncoderMode.HTML5); - Mockito.verify(restClientBuilderMock).property(QuarkusRestClientProperties.DISABLE_CONTEXTUAL_ERROR_MESSAGES, true); - - Mockito.verify(restClientBuilderMock).proxyAddress("host2", 123); - Mockito.verify(restClientBuilderMock).proxyUser("proxyUser2"); - Mockito.verify(restClientBuilderMock).proxyPassword("proxyPassword2"); - Mockito.verify(restClientBuilderMock).nonProxyHosts("nonProxyHosts2"); - Mockito.verify(restClientBuilderMock).connectTimeout(200, TimeUnit.MILLISECONDS); - Mockito.verify(restClientBuilderMock).readTimeout(201, TimeUnit.MILLISECONDS); - Mockito.verify(restClientBuilderMock).userAgent("agent2"); - Mockito.verify(restClientBuilderMock).property(QuarkusRestClientProperties.STATIC_HEADERS, - Collections.singletonMap("header2", "value")); - Mockito.verify(restClientBuilderMock).property(QuarkusRestClientProperties.CONNECTION_TTL, 20); - Mockito.verify(restClientBuilderMock).property(QuarkusRestClientProperties.CONNECTION_POOL_SIZE, 203); - Mockito.verify(restClientBuilderMock).property(QuarkusRestClientProperties.KEEP_ALIVE_ENABLED, true); - Mockito.verify(restClientBuilderMock).property(QuarkusRestClientProperties.MAX_REDIRECTS, 204); - Mockito.verify(restClientBuilderMock).property(QuarkusRestClientProperties.MAX_CHUNK_SIZE, 1024); - Mockito.verify(restClientBuilderMock).followRedirects(true); - Mockito.verify(restClientBuilderMock).register(MyResponseFilter2.class); - Mockito.verify(restClientBuilderMock).queryParamStyle(QueryParamStyle.MULTI_PAIRS); - - Mockito.verify(restClientBuilderMock).trustStore(Mockito.any(), Mockito.anyString()); - Mockito.verify(restClientBuilderMock).keyStore(Mockito.any(), Mockito.anyString()); + verify(restClientBuilderMock).baseUri(URI.create("http://localhost:8080")); + verify(restClientBuilderMock).property(MULTIPART_ENCODER_MODE, HTML5); + verify(restClientBuilderMock).property(DISABLE_CONTEXTUAL_ERROR_MESSAGES, true); + + verify(restClientBuilderMock).proxyAddress("host2", 123); + verify(restClientBuilderMock).proxyUser("proxyUser2"); + verify(restClientBuilderMock).proxyPassword("proxyPassword2"); + verify(restClientBuilderMock).nonProxyHosts("nonProxyHosts2"); + verify(restClientBuilderMock).connectTimeout(200, MILLISECONDS); + verify(restClientBuilderMock).readTimeout(201, MILLISECONDS); + verify(restClientBuilderMock).userAgent("agent2"); + verify(restClientBuilderMock).property(STATIC_HEADERS, Map.of("header2", "value")); + verify(restClientBuilderMock).property(CONNECTION_TTL, 20); + verify(restClientBuilderMock).property(CONNECTION_POOL_SIZE, 203); + verify(restClientBuilderMock).property(KEEP_ALIVE_ENABLED, true); + verify(restClientBuilderMock).property(MAX_REDIRECTS, 204); + verify(restClientBuilderMock).property(MAX_CHUNK_SIZE, 1024); + verify(restClientBuilderMock).followRedirects(true); + verify(restClientBuilderMock).register(MyResponseFilter2.class); + verify(restClientBuilderMock).queryParamStyle(MULTI_PAIRS); + + verify(restClientBuilderMock).trustStore(Mockito.any(), Mockito.anyString()); + verify(restClientBuilderMock).keyStore(Mockito.any(), Mockito.anyString()); } - private static RestClientsConfig createSampleConfigRoot() { - RestClientsConfig configRoot = new RestClientsConfig(); - + private static Map createSampleConfigRoot() { + Map rootConfig = new HashMap<>(); // global properties: - configRoot.multipartPostEncoderMode = Optional.of("HTML5"); - configRoot.disableContextualErrorMessages = true; - + rootConfig.put("quarkus.rest-client.multipart-post-encoder-mode", "HTML5"); + rootConfig.put("quarkus.rest-client.disable-contextual-error-messages", "true"); // global defaults for client specific properties: - configRoot.proxyAddress = Optional.of("host2:123"); - configRoot.proxyUser = Optional.of("proxyUser2"); - configRoot.proxyPassword = Optional.of("proxyPassword2"); - configRoot.nonProxyHosts = Optional.of("nonProxyHosts2"); - configRoot.connectTimeout = 200L; - configRoot.readTimeout = 201L; - configRoot.userAgent = Optional.of("agent2"); - configRoot.headers = Collections.singletonMap("header2", "value"); - configRoot.connectionTTL = Optional.of(20000); // value in ms, will be converted to seconds - configRoot.connectionPoolSize = Optional.of(203); - configRoot.keepAliveEnabled = Optional.of(true); - configRoot.maxRedirects = Optional.of(204); - configRoot.multipart = new RestClientMultipartConfig(); - configRoot.multipart.maxChunkSize = Optional.of(1024); - configRoot.followRedirects = Optional.of(true); - configRoot.maxChunkSize = Optional.of(new MemorySize(BigInteger.valueOf(1024))); - configRoot.providers = Optional - .of("io.quarkus.rest.client.reactive.runtime.RestClientCDIDelegateBuilderTest$MyResponseFilter2"); - configRoot.queryParamStyle = Optional.of(QueryParamStyle.MULTI_PAIRS); - - configRoot.trustStore = Optional.of(truststorePath.toAbsolutePath().toString()); - configRoot.trustStorePassword = Optional.of("truststorePassword"); - configRoot.trustStoreType = Optional.of("JKS"); - configRoot.keyStore = Optional.of(keystorePath.toAbsolutePath().toString()); - configRoot.keyStorePassword = Optional.of("keystorePassword"); - configRoot.keyStoreType = Optional.of("JKS"); - - return configRoot; + rootConfig.put("quarkus.rest-client.proxy-address", "host2:123"); + rootConfig.put("quarkus.rest-client.proxy-user", "proxyUser2"); + rootConfig.put("quarkus.rest-client.proxy-password", "proxyPassword2"); + rootConfig.put("quarkus.rest-client.non-proxy-hosts", "nonProxyHosts2"); + rootConfig.put("quarkus.rest-client.connect-timeout", "200"); + rootConfig.put("quarkus.rest-client.read-timeout", "201"); + rootConfig.put("quarkus.rest-client.user-agent", "agent2"); + rootConfig.put("quarkus.rest-client.headers.header2", "value"); + rootConfig.put("quarkus.rest-client.connection-ttl", "20000"); + rootConfig.put("quarkus.rest-client.connection-pool-size", "203"); + rootConfig.put("quarkus.rest-client.keep-alive-enabled", "true"); + rootConfig.put("quarkus.rest-client.max-redirects", "204"); + rootConfig.put("quarkus.rest-client.multipart-max-chunk-size", "1024"); + rootConfig.put("quarkus.rest-client.follow-redirects", "true"); + rootConfig.put("quarkus.rest-client.max-chunk-size", "1024"); + rootConfig.put("quarkus.rest-client.providers", + "io.quarkus.rest.client.reactive.runtime.RestClientCDIDelegateBuilderTest$MyResponseFilter2"); + rootConfig.put("quarkus.rest-client.query-param-style", "multi-pairs"); + rootConfig.put("quarkus.rest-client.trust-store", truststorePath.toAbsolutePath().toString()); + rootConfig.put("quarkus.rest-client.trust-store-password", "truststorePassword"); + rootConfig.put("quarkus.rest-client.trust-store-type", "JKS"); + rootConfig.put("quarkus.rest-client.key-store", keystorePath.toAbsolutePath().toString()); + rootConfig.put("quarkus.rest-client.key-store-password", "keystorePassword"); + rootConfig.put("quarkus.rest-client.key-store-type", "JKS"); + return rootConfig; } - private static RestClientConfig createSampleClientConfig() { - RestClientConfig clientConfig = new RestClientConfig(); - + private static Map createSampleClientConfig(final String restClientName) { + Map clientConfig = new HashMap<>(); // properties only configurable via client config - clientConfig.url = Optional.of("http://localhost"); - clientConfig.uri = Optional.empty(); - clientConfig.shared = Optional.of(true); - clientConfig.name = Optional.of("my-client"); - + clientConfig.put("quarkus.rest-client." + restClientName + ".url", "http://localhost"); + clientConfig.put("quarkus.rest-client." + restClientName + ".uri", ""); + clientConfig.put("quarkus.rest-client." + restClientName + ".shared", "true"); + clientConfig.put("quarkus.rest-client." + restClientName + ".name", "my-client"); // properties that override configRoot counterparts - clientConfig.proxyAddress = Optional.of("host1:123"); - clientConfig.proxyUser = Optional.of("proxyUser1"); - clientConfig.proxyPassword = Optional.of("proxyPassword1"); - clientConfig.nonProxyHosts = Optional.of("nonProxyHosts1"); - clientConfig.connectTimeout = Optional.of(100L); - clientConfig.readTimeout = Optional.of(101L); - clientConfig.userAgent = Optional.of("agent1"); - clientConfig.headers = Collections.singletonMap("header1", "value"); - clientConfig.connectionTTL = Optional.of(10000); // value in milliseconds, will be converted to seconds - clientConfig.connectionPoolSize = Optional.of(103); - clientConfig.keepAliveEnabled = Optional.of(false); - clientConfig.maxRedirects = Optional.of(104); - clientConfig.followRedirects = Optional.of(true); - clientConfig.multipart = new RestClientMultipartConfig(); - clientConfig.maxChunkSize = Optional.of(new MemorySize(BigInteger.valueOf(1024))); - clientConfig.providers = Optional - .of("io.quarkus.rest.client.reactive.runtime.RestClientCDIDelegateBuilderTest$MyResponseFilter1"); - clientConfig.queryParamStyle = Optional.of(QueryParamStyle.COMMA_SEPARATED); - - clientConfig.trustStore = Optional.of(truststorePath.toAbsolutePath().toString()); - clientConfig.trustStorePassword = Optional.of("truststorePassword"); - clientConfig.trustStoreType = Optional.of("JKS"); - clientConfig.keyStore = Optional.of(keystorePath.toAbsolutePath().toString()); - clientConfig.keyStorePassword = Optional.of("keystorePassword"); - clientConfig.keyStoreType = Optional.of("JKS"); - + clientConfig.put("quarkus.rest-client." + restClientName + ".proxy-address", "host1:123"); + clientConfig.put("quarkus.rest-client." + restClientName + ".proxy-user", "proxyUser1"); + clientConfig.put("quarkus.rest-client." + restClientName + ".proxy-password", "proxyPassword1"); + clientConfig.put("quarkus.rest-client." + restClientName + ".non-proxy-hosts", "nonProxyHosts1"); + clientConfig.put("quarkus.rest-client." + restClientName + ".connect-timeout", "100"); + clientConfig.put("quarkus.rest-client." + restClientName + ".read-timeout", "101"); + clientConfig.put("quarkus.rest-client." + restClientName + ".user-agent", "agent1"); + clientConfig.put("quarkus.rest-client." + restClientName + ".headers.header1", "value"); + clientConfig.put("quarkus.rest-client." + restClientName + ".connection-ttl", "10000"); + clientConfig.put("quarkus.rest-client." + restClientName + ".connection-pool-size", "103"); + clientConfig.put("quarkus.rest-client." + restClientName + ".keep-alive-enabled", "false"); + clientConfig.put("quarkus.rest-client." + restClientName + ".max-redirects", "104"); + clientConfig.put("quarkus.rest-client." + restClientName + ".follow-redirects", "true"); + clientConfig.put("quarkus.rest-client." + restClientName + ".max-chunk-size", "1024"); + clientConfig.put("quarkus.rest-client." + restClientName + ".providers", + "io.quarkus.rest.client.reactive.runtime.RestClientCDIDelegateBuilderTest$MyResponseFilter1"); + clientConfig.put("quarkus.rest-client." + restClientName + ".query-param-style", "comma-separated"); + clientConfig.put("quarkus.rest-client." + restClientName + ".trust-store", truststorePath.toAbsolutePath().toString()); + clientConfig.put("quarkus.rest-client." + restClientName + ".trust-store-password", "truststorePassword"); + clientConfig.put("quarkus.rest-client." + restClientName + ".trust-store-type", "JKS"); + clientConfig.put("quarkus.rest-client." + restClientName + ".key-store", keystorePath.toAbsolutePath().toString()); + clientConfig.put("quarkus.rest-client." + restClientName + ".key-store-password", "keystorePassword"); + clientConfig.put("quarkus.rest-client." + restClientName + ".key-store-type", "JKS"); return clientConfig; }