This is an automated email from the ASF dual-hosted git repository. reta pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/cxf.git
The following commit(s) were added to refs/heads/master by this push: new bf25a93 CXF-7543: JAX-RS Features not used in proxies or WebClients (#849) bf25a93 is described below commit bf25a931a96a6eb6cac47651bef1f2ff4fb442bc Author: Andriy Redko <drr...@gmail.com> AuthorDate: Thu Sep 16 20:05:21 2021 -0400 CXF-7543: JAX-RS Features not used in proxies or WebClients (#849) --- .../cxf/jaxrs/client/JAXRSClientFactoryBean.java | 41 ++++++++++ .../JAXRSClientFactoryBeanDefinitionParser.java | 59 ++++++++++++++ .../cxf/jaxrs/client/cache/ClientCacheTest.java | 23 ++++++ .../client/spring/JAXRSClientFactoryBeanTest.java | 34 ++++++++ .../client/spring/SpringParameterHandler.java | 58 +++++++++++++ .../org/apache/cxf/jaxrs/client/spring/clients.xml | 6 ++ .../client/MicroProfileClientFactoryBean.java | 14 +++- .../client/CxfTypeSafeClientBuilderTest.java | 42 ++++++++++ .../client/MicroProfileClientFactoryBeanTest.java | 94 ++++++++++++++++++++++ 9 files changed, 368 insertions(+), 3 deletions(-) diff --git a/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/JAXRSClientFactoryBean.java b/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/JAXRSClientFactoryBean.java index 13d3c28..ccf547c 100644 --- a/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/JAXRSClientFactoryBean.java +++ b/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/JAXRSClientFactoryBean.java @@ -24,12 +24,19 @@ import java.lang.reflect.Type; import java.net.URI; import java.security.AccessController; import java.security.PrivilegedAction; +import java.util.Arrays; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.logging.Logger; import javax.ws.rs.CookieParam; import javax.ws.rs.HeaderParam; +import javax.ws.rs.RuntimeType; +import javax.ws.rs.core.Configurable; +import javax.ws.rs.core.Configuration; +import javax.ws.rs.core.Feature; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.ext.ParamConverter; import javax.ws.rs.ext.ParamConverterProvider; @@ -45,6 +52,8 @@ import org.apache.cxf.endpoint.UpfrontConduitSelector; import org.apache.cxf.jaxrs.AbstractJAXRSFactoryBean; import org.apache.cxf.jaxrs.JAXRSServiceFactoryBean; import org.apache.cxf.jaxrs.JAXRSServiceImpl; +import org.apache.cxf.jaxrs.impl.ConfigurableImpl; +import org.apache.cxf.jaxrs.impl.FeatureContextImpl; import org.apache.cxf.jaxrs.impl.MetadataMap; import org.apache.cxf.jaxrs.model.ClassResourceInfo; import org.apache.cxf.jaxrs.utils.AnnotationUtils; @@ -403,6 +412,10 @@ public class JAXRSClientFactoryBean extends AbstractJAXRSFactoryBean { }); } } + + protected <C extends Configurable<C>> Configurable<?> getConfigurableFor(C context) { + return new ConfigurableImpl<>(context, RuntimeType.CLIENT); + } protected void applyFeatures(AbstractClient client) { if (getFeatures() != null) { @@ -410,6 +423,34 @@ public class JAXRSClientFactoryBean extends AbstractJAXRSFactoryBean { feature.initialize(client.getConfiguration(), getBus()); }); } + + // Process JAX-RS features which are passed through as providers + final Set<Object> providers = new HashSet<>(); + for (final Object provider: getProviders()) { + if (provider instanceof Feature) { + final Feature feature = (Feature)provider; + final FeatureContextImpl context = new FeatureContextImpl(); + final Configurable<?> configurable = getConfigurableFor(context); + final Configuration configuration = configurable.getConfiguration(); + final Set<Object> registered = configuration.getInstances(); + + if (!configuration.isRegistered(feature)) { + configurable.register(feature); + + // Disregarding if the feature is enabled or disabled, register only newly added providers, + // excluding pre-existing ones and the feature instance itself. + final Set<Object> added = new HashSet<Object>(configuration.getInstances()); + added.remove(feature); + added.removeAll(registered); + + providers.addAll(added); + } + } + } + + if (!providers.isEmpty()) { + setProviders(Arrays.asList(providers)); + } } /** diff --git a/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/spring/JAXRSClientFactoryBeanDefinitionParser.java b/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/spring/JAXRSClientFactoryBeanDefinitionParser.java index 6543ddc..5c6989d 100644 --- a/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/spring/JAXRSClientFactoryBeanDefinitionParser.java +++ b/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/spring/JAXRSClientFactoryBeanDefinitionParser.java @@ -20,12 +20,14 @@ package org.apache.cxf.jaxrs.client.spring; import java.io.IOException; import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.Map; import javax.ws.rs.Path; +import javax.ws.rs.core.Configurable; import javax.ws.rs.ext.Provider; import javax.xml.namespace.QName; @@ -35,6 +37,7 @@ import org.apache.cxf.bus.spring.BusWiringBeanFactoryPostProcessor; import org.apache.cxf.common.util.ClasspathScanner; import org.apache.cxf.configuration.spring.AbstractFactoryBeanDefinitionParser; import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean; +import org.apache.cxf.jaxrs.impl.ConfigurableImpl; import org.apache.cxf.jaxrs.model.UserResource; import org.apache.cxf.jaxrs.utils.ResourceUtils; import org.springframework.beans.BeansException; @@ -109,11 +112,50 @@ public class JAXRSClientFactoryBeanDefinitionParser extends AbstractFactoryBeanD setFirstChildAsProperty(el, ctx, bean, name); } } + + /** + * Instantiate newly registered provider instances using {@link AutowireCapableBeanFactory} + */ + private static class SpringInstantiator implements ConfigurableImpl.Instantiator { + private final AutowireCapableBeanFactory beanFactory; + + SpringInstantiator(final AutowireCapableBeanFactory beanFactory) { + this.beanFactory = beanFactory; + } + + @Override + public <T> Object create(Class<T> cls) { + return beanFactory.createBean(cls, resolveAutowireMode(cls), true); + } + + @Override + public void release(Object instance) { + beanFactory.destroyBean(instance); + } + + /** + * Return the resolved autowire mode (AUTOWIRE_CONSTRUCTOR or AUTOWIRE_BY_TYPE). + */ + private <T> int resolveAutowireMode(Class<T> cls) { + final Constructor<?>[] constructors = cls.getConstructors(); + + for (Constructor<?> constructor: constructors) { + // If there is a default constructor, we are good to go with + // autowiring by type, otherwise we need construction injection. + if (constructor.getParameterCount() == 0) { + return AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE; + } + } + + return AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR; + } + } public static class JAXRSSpringClientFactoryBean extends JAXRSClientFactoryBean implements ApplicationContextAware { private List<String> basePackages; + private SpringInstantiator instantiator; public JAXRSSpringClientFactoryBean() { super(); @@ -125,6 +167,10 @@ public class JAXRSClientFactoryBeanDefinitionParser extends AbstractFactoryBeanD public void setApplicationContext(ApplicationContext ctx) throws BeansException { try { + if (instantiator == null) { + instantiator = new SpringInstantiator(ctx.getAutowireCapableBeanFactory()); + } + if (basePackages != null) { final Map< Class< ? extends Annotation >, Collection< Class< ? > > > classes = ClasspathScanner.findClasses(basePackages, Path.class, Provider.class); @@ -159,6 +205,17 @@ public class JAXRSClientFactoryBeanDefinitionParser extends AbstractFactoryBeanD setBus(BusWiringBeanFactoryPostProcessor.addDefaultBus(ctx)); } } + + @Override + protected <C extends Configurable<C>> Configurable<?> getConfigurableFor(C context) { + final Configurable<?> configurable = super.getConfigurableFor(context); + + if (instantiator != null) { + configurable.register(instantiator); + } + + return configurable; + } } static Class<?> getServiceClass(Collection<Class<?>> rootClasses) { @@ -184,4 +241,6 @@ public class JAXRSClientFactoryBeanDefinitionParser extends AbstractFactoryBeanD } return providers; } + + } diff --git a/rt/rs/client/src/test/java/org/apache/cxf/jaxrs/client/cache/ClientCacheTest.java b/rt/rs/client/src/test/java/org/apache/cxf/jaxrs/client/cache/ClientCacheTest.java index f784f5d..5fc4be6 100644 --- a/rt/rs/client/src/test/java/org/apache/cxf/jaxrs/client/cache/ClientCacheTest.java +++ b/rt/rs/client/src/test/java/org/apache/cxf/jaxrs/client/cache/ClientCacheTest.java @@ -89,6 +89,29 @@ public class ClientCacheTest { } @Test + public void testGetTimeStringUsingClientFactory() { + CacheControlFeature feature = new CacheControlFeature(); + try { + JAXRSClientFactoryBean bean = new JAXRSClientFactoryBean(); + bean.setAddress(ADDRESS); + bean.setProviders(Collections.singletonList(feature)); + bean.setTransportId(LocalTransportFactory.TRANSPORT_ID); + + final WebClient cached = bean.createWebClient() + .accept("text/plain") + .header(HttpHeaders.CACHE_CONTROL, "public"); + + final Response r = cached.get(); + assertEquals(Response.Status.OK.getStatusCode(), r.getStatus()); + final String r1 = r.readEntity(String.class); + waitABit(); + assertEquals(r1, cached.get().readEntity(String.class)); + } finally { + feature.close(); + } + } + + @Test public void testGetTimeStringAsInputStream() throws Exception { CacheControlFeature feature = new CacheControlFeature(); try { diff --git a/rt/rs/client/src/test/java/org/apache/cxf/jaxrs/client/spring/JAXRSClientFactoryBeanTest.java b/rt/rs/client/src/test/java/org/apache/cxf/jaxrs/client/spring/JAXRSClientFactoryBeanTest.java index ecfff4c..f34d037 100644 --- a/rt/rs/client/src/test/java/org/apache/cxf/jaxrs/client/spring/JAXRSClientFactoryBeanTest.java +++ b/rt/rs/client/src/test/java/org/apache/cxf/jaxrs/client/spring/JAXRSClientFactoryBeanTest.java @@ -18,22 +18,30 @@ */ package org.apache.cxf.jaxrs.client.spring; +import java.util.List; + +import javax.ws.rs.core.Feature; +import javax.ws.rs.core.FeatureContext; import javax.xml.namespace.QName; import org.apache.cxf.BusFactory; import org.apache.cxf.jaxrs.client.Client; import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean; +import org.apache.cxf.jaxrs.client.cache.CacheControlClientReaderInterceptor; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.junit.After; import org.junit.Test; import static org.hamcrest.CoreMatchers.endsWith; +import static org.hamcrest.CoreMatchers.hasItem; +import static org.hamcrest.CoreMatchers.instanceOf; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; + public class JAXRSClientFactoryBeanTest { @After @@ -110,4 +118,30 @@ public class JAXRSClientFactoryBeanTest { endsWith("?list=1&list=2")); } } + + @Test + public void testClientWithFeatures() throws Exception { + try (ClassPathXmlApplicationContext ctx = + new ClassPathXmlApplicationContext(new String[] {"/org/apache/cxf/jaxrs/client/spring/clients.xml"})) { + final Client bean = (Client) ctx.getBean("client4"); + assertNotNull(bean); + + final JAXRSClientFactoryBean cfb = (JAXRSClientFactoryBean)ctx.getBean("client4.proxyFactory"); + assertNotNull(bean); + + assertThat((List<Object>)cfb.getProviders(), + hasItem(instanceOf(CacheControlClientReaderInterceptor.class))); + + assertThat((List<Object>)cfb.getProviders(), + hasItem(instanceOf(SpringParameterHandler.class))); + } + } + + public static class SomeFeature implements Feature { + @Override + public boolean configure(FeatureContext context) { + context.register(SpringParameterHandler.class); + return true; + } + } } \ No newline at end of file diff --git a/rt/rs/client/src/test/java/org/apache/cxf/jaxrs/client/spring/SpringParameterHandler.java b/rt/rs/client/src/test/java/org/apache/cxf/jaxrs/client/spring/SpringParameterHandler.java new file mode 100644 index 0000000..10745a5 --- /dev/null +++ b/rt/rs/client/src/test/java/org/apache/cxf/jaxrs/client/spring/SpringParameterHandler.java @@ -0,0 +1,58 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cxf.jaxrs.client.spring; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.Objects; + +import javax.ws.rs.ext.ParamConverter; +import javax.ws.rs.ext.ParamConverterProvider; + +import org.apache.cxf.jaxrs.client.Customer; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; + +public class SpringParameterHandler implements ParamConverterProvider, ParamConverter<Customer> { + @Autowired + public SpringParameterHandler(ApplicationContext context) { + Objects.requireNonNull(context); + } + + @SuppressWarnings("unchecked") + @Override + public <T> ParamConverter<T> getConverter(Class<T> cls, Type arg1, Annotation[] arg2) { + if (Customer.class == cls) { + return (ParamConverter<T>)this; + } + return null; + } + + public Customer fromString(String s) throws IllegalArgumentException { + Customer c = new Customer(); + c.setName(s); + return c; + } + + @Override + public String toString(Customer arg0) throws IllegalArgumentException { + return null; + } +} diff --git a/rt/rs/client/src/test/java/org/apache/cxf/jaxrs/client/spring/clients.xml b/rt/rs/client/src/test/java/org/apache/cxf/jaxrs/client/spring/clients.xml index 320de2e..7f33450 100644 --- a/rt/rs/client/src/test/java/org/apache/cxf/jaxrs/client/spring/clients.xml +++ b/rt/rs/client/src/test/java/org/apache/cxf/jaxrs/client/spring/clients.xml @@ -59,4 +59,10 @@ <entry key="expand.query.value.as.collection" value="true" /> </jaxrs:properties> </jaxrs:client> + <jaxrs:client id="client4" serviceClass="org.apache.cxf.jaxrs.resources.BookStore" address="http://localhost:9000/foo" threadSafe="true"> + <jaxrs:providers> + <bean class="org.apache.cxf.jaxrs.client.cache.CacheControlFeature"/> + <bean class="org.apache.cxf.jaxrs.client.spring.JAXRSClientFactoryBeanTest.SomeFeature"/> + </jaxrs:providers> + </jaxrs:client> </beans> \ No newline at end of file diff --git a/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/MicroProfileClientFactoryBean.java b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/MicroProfileClientFactoryBean.java index 389c4fa..0f10811 100644 --- a/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/MicroProfileClientFactoryBean.java +++ b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/MicroProfileClientFactoryBean.java @@ -28,6 +28,7 @@ import java.util.concurrent.ExecutorService; import javax.ws.rs.client.ClientRequestFilter; import javax.ws.rs.client.ClientResponseFilter; +import javax.ws.rs.core.Configurable; import javax.ws.rs.core.Configuration; import org.apache.cxf.common.util.ClassHelper; @@ -51,17 +52,19 @@ import org.eclipse.microprofile.rest.client.RestClientBuilder; public class MicroProfileClientFactoryBean extends JAXRSClientFactoryBean { private final Comparator<ProviderInfo<?>> comparator; private final List<Object> registeredProviders; + private final Configurable<RestClientBuilder> configurable; private final Configuration configuration; private ClassLoader proxyLoader; private boolean inheritHeaders; private ExecutorService executorService; private TLSConfiguration secConfig; - public MicroProfileClientFactoryBean(MicroProfileClientConfigurableImpl<RestClientBuilder> configuration, + public MicroProfileClientFactoryBean(MicroProfileClientConfigurableImpl<RestClientBuilder> configurable, String baseUri, Class<?> aClass, ExecutorService executorService, TLSConfiguration secConfig) { super(new MicroProfileServiceFactoryBean()); - this.configuration = configuration.getConfiguration(); + this.configurable = configurable; + this.configuration = configurable.getConfiguration(); this.comparator = MicroProfileClientProviderFactory.createComparator(this); this.executorService = (executorService == null) ? Utils.defaultExecutorService() : executorService; this.secConfig = secConfig; @@ -71,7 +74,7 @@ public class MicroProfileClientFactoryBean extends JAXRSClientFactoryBean { super.setProperties(this.configuration.getProperties()); registeredProviders = new ArrayList<>(); registeredProviders.addAll(processProviders()); - if (!configuration.isDefaultExceptionMapperDisabled()) { + if (!configurable.isDefaultExceptionMapperDisabled()) { registeredProviders.add(new ProviderInfo<>(new DefaultResponseExceptionMapper(), getBus(), false)); } registeredProviders.add(new ProviderInfo<>(new JsrJsonpProvider(), getBus(), false)); @@ -134,6 +137,11 @@ public class MicroProfileClientFactoryBean extends JAXRSClientFactoryBean { inheritHeaders, executorService, configuration, interceptorWrapper, secConfig, varValues); } } + + @Override + protected <C extends Configurable<C>> Configurable<?> getConfigurableFor(C context) { + return configurable; + } Configuration getConfiguration() { return configuration; diff --git a/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/CxfTypeSafeClientBuilderTest.java b/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/CxfTypeSafeClientBuilderTest.java index c9d09a6..8ca5f4b 100644 --- a/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/CxfTypeSafeClientBuilderTest.java +++ b/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/CxfTypeSafeClientBuilderTest.java @@ -24,6 +24,8 @@ import java.net.URL; import javax.ws.rs.PathParam; import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Feature; +import javax.ws.rs.core.FeatureContext; import javax.ws.rs.core.Response; import org.apache.cxf.jaxrs.client.WebClientUtil; @@ -116,6 +118,30 @@ public class CxfTypeSafeClientBuilderTest { // TODO: add a test for writer interceptors - possibly in systests //assertEquals(TestWriterInterceptor.getAndResetValue(), 1); } + + @Test + public void testInvokesPostOperationWithRegisteredFeature() throws Exception { + String inputBody = "input body will be removed"; + String expectedResponseBody = TestMessageBodyReader.REPLACED_BODY; + + InterfaceWithoutProvidersDefined api = new CxfTypeSafeClientBuilder() + .register(SomeFeature.class) + .property("microprofile.rest.client.disable.default.mapper", true) + .baseUrl(new URL("http://localhost/null")) + .build(InterfaceWithoutProvidersDefined.class); + + Response response = api.executePost(inputBody); + + String body = response.readEntity(String.class); + + response.close(); + + assertEquals(expectedResponseBody, body); + + assertEquals(TestClientResponseFilter.getAndResetValue(), 1); + assertEquals(TestClientRequestFilter.getAndResetValue(), 1); + assertEquals(TestReaderInterceptor.getAndResetValue(), 1); + } @Test(expected = NoSuchEntityException.class) public void testResponseExceptionMapper() throws Exception { @@ -223,4 +249,20 @@ public class CxfTypeSafeClientBuilderTest { public void testProxyAddressNullHost() { RestClientBuilder.newBuilder().proxyAddress(null, 8080); } + + public static class SomeFeature implements Feature { + @Override + public boolean configure(FeatureContext context) { + context + .register(TestClientRequestFilter.class) + .register(TestClientResponseFilter.class) + .register(TestMessageBodyReader.class, 4999) + .register(TestMessageBodyWriter.class) + .register(TestParamConverterProvider.class) + .register(TestReaderInterceptor.class) + .register(TestWriterInterceptor.class) + .register(EchoClientReqFilter.class); + return true; + } + } } diff --git a/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/MicroProfileClientFactoryBeanTest.java b/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/MicroProfileClientFactoryBeanTest.java new file mode 100644 index 0000000..9951f82 --- /dev/null +++ b/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/MicroProfileClientFactoryBeanTest.java @@ -0,0 +1,94 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cxf.microprofile.client; + +import java.util.List; + +import javax.ws.rs.core.Feature; +import javax.ws.rs.core.FeatureContext; + +import org.apache.cxf.jaxrs.client.spec.TLSConfiguration; +import org.apache.cxf.microprofile.client.mock.MyClient; +import org.eclipse.microprofile.rest.client.RestClientBuilder; +import org.eclipse.microprofile.rest.client.tck.providers.TestClientRequestFilter; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.hasItem; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +public class MicroProfileClientFactoryBeanTest { + @Test + @SuppressWarnings("unchecked") + public void testCreateClientEnabledFeature() throws Exception { + final MicroProfileClientConfigurableImpl<RestClientBuilder> configurable = + new MicroProfileClientConfigurableImpl<>(RestClientBuilder.newBuilder()); + + final MicroProfileClientFactoryBean bean = new MicroProfileClientFactoryBean(configurable, + "http://bar", MyClient.class, null, new TLSConfiguration()); + + final SomeFeature feature = new SomeFeature(true); + bean.setProvider(feature); + + assertTrue(bean.create() instanceof MyClient); + assertTrue(configurable.getConfiguration().isRegistered(SomeFeature.class)); + assertTrue(configurable.getConfiguration().isRegistered(TestClientRequestFilter.class)); + assertTrue(configurable.getConfiguration().isEnabled(SomeFeature.class)); + + assertThat((List<Object>)bean.getProviders(), hasItem(instanceOf(TestClientRequestFilter.class))); + } + + @Test + @SuppressWarnings("unchecked") + public void testCreateClientDisabledFeature() throws Exception { + final MicroProfileClientConfigurableImpl<RestClientBuilder> configurable = + new MicroProfileClientConfigurableImpl<>(RestClientBuilder.newBuilder()); + + final MicroProfileClientFactoryBean bean = new MicroProfileClientFactoryBean(configurable, + "http://bar", MyClient.class, null, new TLSConfiguration()); + + final SomeFeature feature = new SomeFeature(false); + bean.setProvider(feature); + + assertTrue(bean.create() instanceof MyClient); + assertTrue(configurable.getConfiguration().isRegistered(SomeFeature.class)); + assertTrue(configurable.getConfiguration().isRegistered(TestClientRequestFilter.class)); + assertFalse(configurable.getConfiguration().isEnabled(SomeFeature.class)); + + assertThat((List<Object>)bean.getProviders(), hasItem(instanceOf(TestClientRequestFilter.class))); + } + + public static class SomeFeature implements Feature { + private final boolean enabled; + + public SomeFeature(boolean enabled) { + this.enabled = enabled; + } + + @Override + public boolean configure(FeatureContext context) { + context.register(TestClientRequestFilter.class); + return enabled; + } + } +}