andymc12 closed pull request #482: [CXF-7926] Initial MP Rest Client 1.2 impl URL: https://github.com/apache/cxf/pull/482
This is a PR merged from a forked repository. As GitHub hides the original diff on merge, it is displayed below for the sake of provenance: As this is a foreign pull request (from a fork), the diff is supplied below (as it won't show otherwise due to GitHub magic): diff --git a/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/ClientProperties.java b/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/ClientProperties.java new file mode 100644 index 00000000000..1c6d13be9d1 --- /dev/null +++ b/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/ClientProperties.java @@ -0,0 +1,42 @@ +/** + * 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; + +import org.apache.cxf.common.util.SystemPropertyAction; + +public interface ClientProperties { + String HTTP_CONNECTION_TIMEOUT_PROP = "http.connection.timeout"; + String HTTP_RECEIVE_TIMEOUT_PROP = "http.receive.timeout"; + String HTTP_PROXY_SERVER_PROP = "http.proxy.server.uri"; + String HTTP_PROXY_SERVER_PORT_PROP = "http.proxy.server.port"; + String HTTP_AUTOREDIRECT_PROP = "http.autoredirect"; + String HTTP_MAINTAIN_SESSION_PROP = "http.maintain.session"; + String HTTP_RESPONSE_AUTOCLOSE_PROP = "http.response.stream.auto.close"; + String THREAD_SAFE_CLIENT_PROP = "thread.safe.client"; + String THREAD_SAFE_CLIENT_STATE_CLEANUP_PROP = "thread.safe.client.state.cleanup.period"; + Boolean DEFAULT_THREAD_SAFETY_CLIENT_STATUS = + Boolean.parseBoolean(SystemPropertyAction.getPropertyOrNull(THREAD_SAFE_CLIENT_PROP));; + Integer THREAD_SAFE_CLIENT_STATE_CLEANUP_PERIOD = + getIntValue(SystemPropertyAction.getPropertyOrNull(THREAD_SAFE_CLIENT_STATE_CLEANUP_PROP)); + + static Integer getIntValue(Object o) { + return o instanceof Integer ? (Integer)o : o instanceof String ? Integer.valueOf(o.toString()) : null; + } +} diff --git a/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/spec/ClientBuilderImpl.java b/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/spec/ClientBuilderImpl.java index f82e26a7705..0e989a037f3 100644 --- a/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/spec/ClientBuilderImpl.java +++ b/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/spec/ClientBuilderImpl.java @@ -36,6 +36,9 @@ import org.apache.cxf.jaxrs.client.AbstractClient; +import static org.apache.cxf.jaxrs.client.ClientProperties.HTTP_CONNECTION_TIMEOUT_PROP; +import static org.apache.cxf.jaxrs.client.ClientProperties.HTTP_RECEIVE_TIMEOUT_PROP; + public class ClientBuilderImpl extends ClientBuilder { private ClientConfigurableImpl<ClientBuilder> configImpl; @@ -171,13 +174,13 @@ public ClientBuilder scheduledExecutorService(ScheduledExecutorService scheduled @Override public ClientBuilder connectTimeout(long timeout, TimeUnit timeUnit) { validateTimeout(timeout); - return property(ClientImpl.HTTP_CONNECTION_TIMEOUT_PROP, timeUnit.toMillis(timeout)); + return property(HTTP_CONNECTION_TIMEOUT_PROP, timeUnit.toMillis(timeout)); } @Override public ClientBuilder readTimeout(long timeout, TimeUnit timeUnit) { validateTimeout(timeout); - return property(ClientImpl.HTTP_RECEIVE_TIMEOUT_PROP, timeUnit.toMillis(timeout)); + return property(HTTP_RECEIVE_TIMEOUT_PROP, timeUnit.toMillis(timeout)); } private void validateTimeout(long timeout) { diff --git a/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/spec/ClientImpl.java b/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/spec/ClientImpl.java index 410b8c77e55..de4d2ca4a4f 100644 --- a/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/spec/ClientImpl.java +++ b/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/spec/ClientImpl.java @@ -40,7 +40,6 @@ import javax.ws.rs.core.UriBuilder; import org.apache.cxf.common.util.ClassHelper; -import org.apache.cxf.common.util.SystemPropertyAction; import org.apache.cxf.configuration.jsse.TLSClientParameters; import org.apache.cxf.jaxrs.client.AbstractClient; import org.apache.cxf.jaxrs.client.ClientConfiguration; @@ -51,24 +50,19 @@ import org.apache.cxf.message.Message; import org.apache.cxf.transport.https.SSLUtils; +import static org.apache.cxf.jaxrs.client.ClientProperties.DEFAULT_THREAD_SAFETY_CLIENT_STATUS; +import static org.apache.cxf.jaxrs.client.ClientProperties.HTTP_AUTOREDIRECT_PROP; +import static org.apache.cxf.jaxrs.client.ClientProperties.HTTP_CONNECTION_TIMEOUT_PROP; +import static org.apache.cxf.jaxrs.client.ClientProperties.HTTP_MAINTAIN_SESSION_PROP; +import static org.apache.cxf.jaxrs.client.ClientProperties.HTTP_PROXY_SERVER_PORT_PROP; +import static org.apache.cxf.jaxrs.client.ClientProperties.HTTP_PROXY_SERVER_PROP; +import static org.apache.cxf.jaxrs.client.ClientProperties.HTTP_RECEIVE_TIMEOUT_PROP; +import static org.apache.cxf.jaxrs.client.ClientProperties.HTTP_RESPONSE_AUTOCLOSE_PROP; +import static org.apache.cxf.jaxrs.client.ClientProperties.THREAD_SAFE_CLIENT_PROP; +import static org.apache.cxf.jaxrs.client.ClientProperties.THREAD_SAFE_CLIENT_STATE_CLEANUP_PERIOD; +import static org.apache.cxf.jaxrs.client.ClientProperties.THREAD_SAFE_CLIENT_STATE_CLEANUP_PROP; + public class ClientImpl implements Client { - static final String HTTP_CONNECTION_TIMEOUT_PROP = "http.connection.timeout"; - static final String HTTP_RECEIVE_TIMEOUT_PROP = "http.receive.timeout"; - private static final String HTTP_PROXY_SERVER_PROP = "http.proxy.server.uri"; - private static final String HTTP_PROXY_SERVER_PORT_PROP = "http.proxy.server.port"; - private static final String HTTP_AUTOREDIRECT_PROP = "http.autoredirect"; - private static final String HTTP_MAINTAIN_SESSION_PROP = "http.maintain.session"; - private static final String HTTP_RESPONSE_AUTOCLOSE_PROP = "http.response.stream.auto.close"; - private static final String THREAD_SAFE_CLIENT_PROP = "thread.safe.client"; - private static final String THREAD_SAFE_CLIENT_STATE_CLEANUP_PROP = "thread.safe.client.state.cleanup.period"; - private static final Boolean DEFAULT_THREAD_SAFETY_CLIENT_STATUS; - private static final Integer THREAD_SAFE_CLIENT_STATE_CLEANUP_PERIOD; - static { - DEFAULT_THREAD_SAFETY_CLIENT_STATUS = - Boolean.parseBoolean(SystemPropertyAction.getPropertyOrNull(THREAD_SAFE_CLIENT_PROP)); - THREAD_SAFE_CLIENT_STATE_CLEANUP_PERIOD = - getIntValue(SystemPropertyAction.getPropertyOrNull(THREAD_SAFE_CLIENT_STATE_CLEANUP_PROP)); - } private Configurable<Client> configImpl; private TLSConfiguration secConfig; diff --git a/rt/rs/microprofile-client/pom.xml b/rt/rs/microprofile-client/pom.xml index c072b6f9477..b122d56b5d6 100644 --- a/rt/rs/microprofile-client/pom.xml +++ b/rt/rs/microprofile-client/pom.xml @@ -42,6 +42,7 @@ org.apache.aries.blueprint.NamespaceHandler;osgi.service.blueprint.namespace="http://cxf.apache.org/blueprint/jaxrs-client" </cxf.export.service> <cxf.bundle.activator>org.apache.cxf.jaxrs.client.blueprint.Activator</cxf.bundle.activator> + <mp.rest.client.version>1.2-m2</mp.rest.client.version> </properties> <dependencies> <dependency> @@ -104,7 +105,7 @@ <dependency> <groupId>org.eclipse.microprofile.rest.client</groupId> <artifactId>microprofile-rest-client-api</artifactId> - <version>1.1</version> + <version>${mp.rest.client.version}</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> @@ -114,12 +115,12 @@ <dependency> <groupId>javax.json</groupId> <artifactId>javax.json-api</artifactId> - <version>1.0</version> + <version>1.1</version> </dependency> <dependency> <groupId>org.eclipse.microprofile.rest.client</groupId> <artifactId>microprofile-rest-client-tck</artifactId> - <version>1.1</version> + <version>${mp.rest.client.version}</version> <exclusions> <exclusion> <groupId>org.testng</groupId> diff --git a/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/CxfTypeSafeClientBuilder.java b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/CxfTypeSafeClientBuilder.java index ed7715233b4..5b1ff3d4fab 100644 --- a/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/CxfTypeSafeClientBuilder.java +++ b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/CxfTypeSafeClientBuilder.java @@ -20,22 +20,59 @@ import java.net.URI; import java.net.URL; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Collection; import java.util.Map; import java.util.Objects; +import java.util.ServiceLoader; +import java.util.WeakHashMap; import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; import javax.ws.rs.core.Configurable; import javax.ws.rs.core.Configuration; +import org.apache.cxf.common.logging.LogUtils; +import org.apache.cxf.microprofile.client.config.ConfigFacade; import org.eclipse.microprofile.rest.client.RestClientBuilder; import org.eclipse.microprofile.rest.client.annotation.RegisterProvider; +import org.eclipse.microprofile.rest.client.spi.RestClientListener; + +import static org.apache.cxf.jaxrs.client.ClientProperties.HTTP_CONNECTION_TIMEOUT_PROP; +import static org.apache.cxf.jaxrs.client.ClientProperties.HTTP_RECEIVE_TIMEOUT_PROP; public class CxfTypeSafeClientBuilder implements RestClientBuilder, Configurable<RestClientBuilder> { + private static final Logger LOG = LogUtils.getL7dLogger(CxfTypeSafeClientBuilder.class); + private static final String REST_CONN_TIMEOUT_FORMAT = "%s/mp-rest/connectTimeout"; + private static final String REST_READ_TIMEOUT_FORMAT = "%s/mp-rest/readTimeout"; + private static final Map<ClassLoader, Collection<RestClientListener>> REST_CLIENT_LISTENERS = + new WeakHashMap<>(); + private String baseUri; private ExecutorService executorService; private final MicroProfileClientConfigurableImpl<RestClientBuilder> configImpl = new MicroProfileClientConfigurableImpl<>(this); + private static Collection<RestClientListener> listeners() { + ClassLoader threadContextClassLoader; + if (System.getSecurityManager() == null) { + threadContextClassLoader = Thread.currentThread().getContextClassLoader(); + } else { + threadContextClassLoader = AccessController.doPrivileged( + (PrivilegedAction<ClassLoader>)() -> Thread.currentThread().getContextClassLoader()); + } + synchronized (REST_CLIENT_LISTENERS) { + return REST_CLIENT_LISTENERS.computeIfAbsent(threadContextClassLoader, key -> + StreamSupport.stream(ServiceLoader.load(RestClientListener.class).spliterator(), false) + .collect(Collectors.toList())); + } + } + @Override public RestClientBuilder baseUrl(URL url) { this.baseUri = Objects.requireNonNull(url).toExternalForm(); @@ -57,6 +94,28 @@ public RestClientBuilder executorService(ExecutorService executor) { return this; } + @Override + public RestClientBuilder connectTimeout(long timeout, TimeUnit unit) { + if (null == unit) { + throw new IllegalArgumentException("time unit must not be null"); + } + if (timeout < 0) { + throw new IllegalArgumentException("timeout must be non-negative"); + } + return property(HTTP_CONNECTION_TIMEOUT_PROP, unit.toMillis(timeout)); + } + + @Override + public RestClientBuilder readTimeout(long timeout, TimeUnit unit) { + if (null == unit) { + throw new IllegalArgumentException("time unit must not be null"); + } + if (timeout < 0) { + throw new IllegalArgumentException("timeout must be non-negative"); + } + return property(HTTP_RECEIVE_TIMEOUT_PROP, unit.toMillis(timeout)); + } + @Override public <T> T build(Class<T> aClass) { if (baseUri == null) { @@ -76,8 +135,29 @@ public RestClientBuilder executorService(ExecutorService executor) { } } } + + final String interfaceName = aClass.getName(); + + ConfigFacade.getOptionalLong(String.format(REST_CONN_TIMEOUT_FORMAT, interfaceName)).ifPresent( + timeoutValue -> { + connectTimeout(timeoutValue, TimeUnit.MILLISECONDS); + if (LOG.isLoggable(Level.FINEST)) { + LOG.finest("readTimeout set by MP Config: " + timeoutValue); + } + }); + + ConfigFacade.getOptionalLong(String.format(REST_READ_TIMEOUT_FORMAT, interfaceName)).ifPresent( + timeoutValue -> { + readTimeout(timeoutValue, TimeUnit.MILLISECONDS); + if (LOG.isLoggable(Level.FINEST)) { + LOG.finest("readTimeout set by MP Config: " + timeoutValue); + } + }); + + listeners().forEach(l -> l.onNewClient(aClass, this)); + MicroProfileClientFactoryBean bean = new MicroProfileClientFactoryBean(configImpl, - baseUri, aClass, executorService); + baseUri, aClass, executorService); return bean.create(aClass); } diff --git a/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/Messages.properties b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/Messages.properties index f4c7633d409..1af5bb65caa 100644 --- a/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/Messages.properties +++ b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/Messages.properties @@ -26,4 +26,7 @@ VALIDATION_EXTRA_PATH_PARAMS=The client interface, {0} has one or more extra pat VALIDATION_ASYNC_METHOD_RETURNS_UNEXPECTED_TYPE=The client interface, {0} has an asynchronous method, {1} that returns an unexpected type, {2}. If the method has an InvocationCallback parameter, then it must be return void. ASYNC_INTERCEPTOR_EXCEPTION_PREPARE_CONTEXT=The AsyncInvocationInterceptor, {0} has thrown an exception during the invocation of the prepareContext method. This interceptor's applyContext method will not be invoked. -ASYNC_INTERCEPTOR_EXCEPTION_APPLY_CONTEXT=The AsyncInvocationInterceptor, {0} has thrown an exception during the invocation of the applyContext method. \ No newline at end of file +ASYNC_INTERCEPTOR_EXCEPTION_APPLY_CONTEXT=The AsyncInvocationInterceptor, {0} has thrown an exception during the invocation of the applyContext method. +ASYNC_INTERCEPTOR_EXCEPTION_REMOVE_CONTEXT=The AsyncInvocationInterceptor, {0} has thrown an exception during the invocation of the removeContext method. + +INVALID_TIMEOUT_PROPERTY=The timeout property, {0} is specified as {1}, and is invalid. It must be a non-negative integer. \ No newline at end of file diff --git a/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/MicroProfileClientConfigurableImpl.java b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/MicroProfileClientConfigurableImpl.java index 4cf27631d0e..1a64e3b8378 100644 --- a/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/MicroProfileClientConfigurableImpl.java +++ b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/MicroProfileClientConfigurableImpl.java @@ -28,6 +28,7 @@ import javax.ws.rs.ext.ReaderInterceptor; import javax.ws.rs.ext.WriterInterceptor; +import org.apache.cxf.common.util.PropertyUtils; import org.apache.cxf.jaxrs.impl.ConfigurableImpl; import org.apache.cxf.jaxrs.impl.ConfigurationImpl; import org.apache.cxf.microprofile.client.config.ConfigFacade; @@ -52,8 +53,8 @@ public MicroProfileClientConfigurableImpl(C configurable, Configuration config) boolean isDefaultExceptionMapperDisabled() { Object prop = getConfiguration().getProperty(CONFIG_KEY_DISABLE_MAPPER); - if (prop instanceof Boolean) { - return (Boolean)prop; + if (prop != null) { + return PropertyUtils.isTrue(prop); } return ConfigFacade.getOptionalValue(CONFIG_KEY_DISABLE_MAPPER, Boolean.class).orElse(false); 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 479cabb8a19..6d59031adc9 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 @@ -40,6 +40,7 @@ import org.apache.cxf.jaxrs.model.FilterProviderInfo; import org.apache.cxf.jaxrs.model.ProviderInfo; import org.apache.cxf.jaxrs.provider.jsrjsonp.JsrJsonpProvider; +import org.apache.cxf.microprofile.client.cdi.CDIInterceptorWrapper; import org.apache.cxf.microprofile.client.proxy.MicroProfileClientProxyImpl; import org.eclipse.microprofile.rest.client.RestClientBuilder; @@ -97,12 +98,13 @@ protected void initClient(AbstractClient client, Endpoint ep, boolean addHeaders @Override protected ClientProxyImpl createClientProxy(ClassResourceInfo cri, boolean isRoot, ClientState actualState, Object[] varValues) { + CDIInterceptorWrapper interceptorWrapper = CDIInterceptorWrapper.createWrapper(getServiceClass()); if (actualState == null) { return new MicroProfileClientProxyImpl(URI.create(getAddress()), proxyLoader, cri, isRoot, - inheritHeaders, executorService, configuration, varValues); + inheritHeaders, executorService, configuration, interceptorWrapper, varValues); } else { return new MicroProfileClientProxyImpl(actualState, proxyLoader, cri, isRoot, - inheritHeaders, executorService, configuration, varValues); + inheritHeaders, executorService, configuration, interceptorWrapper, varValues); } } diff --git a/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/cdi/CDIInterceptorWrapper.java b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/cdi/CDIInterceptorWrapper.java new file mode 100644 index 00000000000..298317b5dd2 --- /dev/null +++ b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/cdi/CDIInterceptorWrapper.java @@ -0,0 +1,69 @@ +/** + * 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.cdi; + +import java.lang.reflect.Method; +import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.concurrent.Callable; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.apache.cxf.common.logging.LogUtils; + + +public interface CDIInterceptorWrapper { + Logger LOG = LogUtils.getL7dLogger(CDIInterceptorWrapper.class); + + class BasicCDIInterceptorWrapper implements CDIInterceptorWrapper { + BasicCDIInterceptorWrapper() { + } + + @Override + public Object invoke(Object restClient, Method m, Object[] params, Callable callable) throws Exception { + return callable.call(); + } + } + + static CDIInterceptorWrapper createWrapper(Class<?> restClient) { + try { + return AccessController.doPrivileged((PrivilegedExceptionAction<CDIInterceptorWrapper>) () -> { + Class<?> cdiClass = Class.forName("javax.enterprise.inject.spi.CDI", false, + restClient.getClassLoader()); + Method currentMethod = cdiClass.getMethod("current"); + Object cdiCurrent = currentMethod.invoke(null); + + Method getBeanMgrMethod = cdiClass.getMethod("getBeanManager"); + + return new CDIInterceptorWrapperImpl(restClient, getBeanMgrMethod.invoke(cdiCurrent)); + }); + } catch (PrivilegedActionException pae) { + // expected for environments where CDI is not supported + if (LOG.isLoggable(Level.FINEST)) { + LOG.log(Level.FINEST, "Unable to load CDI SPI classes, assuming no CDI is available", pae); + } + return new BasicCDIInterceptorWrapper(); + } + + } + + Object invoke(Object restClient, Method m, Object[] params, Callable<Object> callable) throws Exception; +} diff --git a/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/cdi/CDIInterceptorWrapperImpl.java b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/cdi/CDIInterceptorWrapperImpl.java new file mode 100644 index 00000000000..3e68de4029a --- /dev/null +++ b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/cdi/CDIInterceptorWrapperImpl.java @@ -0,0 +1,138 @@ +/** + * 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.cdi; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.spi.AnnotatedMethod; +import javax.enterprise.inject.spi.AnnotatedType; +import javax.enterprise.inject.spi.BeanManager; +import javax.enterprise.inject.spi.InterceptionType; +import javax.enterprise.inject.spi.Interceptor; + +import org.apache.cxf.common.logging.LogUtils; + + +class CDIInterceptorWrapperImpl implements CDIInterceptorWrapper { + private static final Logger LOG = LogUtils.getL7dLogger(CDIInterceptorWrapperImpl.class); + + private final CreationalContext<?> creationalContext; + private final Map<Method, List<InterceptorInvoker>> interceptorInvokers; + + CDIInterceptorWrapperImpl(Class<?> restClient, Object beanManagerObject) { + BeanManager beanManager = (BeanManager) beanManagerObject; + creationalContext = beanManager != null ? beanManager.createCreationalContext(null) : null; + interceptorInvokers = initInterceptorInvokers(beanManager, creationalContext, restClient); + } + + private static Map<Method, List<InterceptorInvoker>> initInterceptorInvokers(BeanManager beanManager, + CreationalContext<?> creationalContext, + Class<?> restClient) { + Map<Method, List<InterceptorInvoker>> invokers = new HashMap<>(); + // Interceptor as a key in a map is not entirely correct (custom interceptors) but should work in most cases + Map<Interceptor<?>, Object> interceptorInstances = new HashMap<>(); + + AnnotatedType<?> restClientType = beanManager.createAnnotatedType(restClient); + + List<Annotation> classBindings = getBindings(restClientType.getAnnotations(), beanManager); + + for (AnnotatedMethod<?> method : restClientType.getMethods()) { + Method javaMethod = method.getJavaMember(); + if (javaMethod.isDefault() || method.isStatic()) { + continue; + } + List<Annotation> methodBindings = getBindings(method.getAnnotations(), beanManager); + + if (!classBindings.isEmpty() || !methodBindings.isEmpty()) { + Annotation[] interceptorBindings = merge(methodBindings, classBindings); + + List<Interceptor<?>> interceptors = + new ArrayList<>(beanManager.resolveInterceptors(InterceptionType.AROUND_INVOKE, + interceptorBindings)); + if (LOG.isLoggable(Level.FINEST)) { + LOG.finest("Resolved interceptors from beanManager, " + beanManager + ":" + interceptors); + } + + if (!interceptors.isEmpty()) { + List<InterceptorInvoker> chain = new ArrayList<>(); + for (Interceptor<?> interceptor : interceptors) { + chain.add( + new InterceptorInvoker(interceptor, + interceptorInstances.computeIfAbsent(interceptor, + i -> beanManager.getReference(i, + i.getBeanClass(), + creationalContext)))); + } + invokers.put(javaMethod, chain); + } + } + } + return invokers.isEmpty() ? Collections.emptyMap() : invokers; + } + + private static Annotation[] merge(List<Annotation> methodBindings, List<Annotation> classBindings) { + Set<Class<? extends Annotation>> types = methodBindings.stream() + .map(a -> a.annotationType()) + .collect(Collectors.toSet()); + List<Annotation> merged = new ArrayList<>(methodBindings); + for (Annotation annotation : classBindings) { + if (!types.contains(annotation.annotationType())) { + merged.add(annotation); + } + } + return merged.toArray(new Annotation[] {}); + } + + private static List<Annotation> getBindings(Set<Annotation> annotations, BeanManager beanManager) { + if (annotations == null || annotations.size() == 0) { + return Collections.emptyList(); + } + List<Annotation> bindings = new ArrayList<>(); + for (Annotation annotation : annotations) { + if (beanManager.isInterceptorBinding(annotation.annotationType())) { + bindings.add(annotation); + } + } + return bindings; + } + + @Override + public Object invoke(Object restClient, Method method, Object[] params, Callable<Object> callable) + throws Exception { + + List<InterceptorInvoker> invokers = interceptorInvokers.get(method); + if (invokers == null || invokers.isEmpty()) { + return callable.call(); + } + return new MPRestClientInvocationContextImpl(restClient, method, params, invokers, callable).proceed(); + } +} diff --git a/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/cdi/InterceptorInvoker.java b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/cdi/InterceptorInvoker.java new file mode 100644 index 00000000000..6ef755c23c2 --- /dev/null +++ b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/cdi/InterceptorInvoker.java @@ -0,0 +1,43 @@ +/** + * 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.cdi; + +import javax.enterprise.inject.spi.InterceptionType; +import javax.enterprise.inject.spi.Interceptor; +import javax.interceptor.InvocationContext; + +public class InterceptorInvoker { + + @SuppressWarnings("rawtypes") + private final Interceptor interceptor; + + private final Object interceptorInstance; + + public InterceptorInvoker(Interceptor<?> interceptor, Object interceptorInstance) { + this.interceptor = interceptor; + this.interceptorInstance = interceptorInstance; + } + + @SuppressWarnings("unchecked") + Object invoke(InvocationContext ctx) throws Exception { + return interceptor.intercept(InterceptionType.AROUND_INVOKE, interceptorInstance, ctx); + } + +} diff --git a/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/cdi/MPRestClientInvocationContextImpl.java b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/cdi/MPRestClientInvocationContextImpl.java new file mode 100644 index 00000000000..1c3a67244a1 --- /dev/null +++ b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/cdi/MPRestClientInvocationContextImpl.java @@ -0,0 +1,129 @@ +/** + * 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.cdi; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; + +import javax.interceptor.InvocationContext; + + +class MPRestClientInvocationContextImpl implements InvocationContext { + + private final Object target; + + private final Method method; + + private Object[] args; + + private int index; + + private final List<InterceptorInvoker> interceptorInvokers; + + private final Map<String, Object> contextData = new HashMap<>(); + + private final Callable<Object> callable; + /** + * @param target + * @param method + * @param args + * @param interceptorInvokers + */ + MPRestClientInvocationContextImpl(Object target, Method method, Object[] args, + List<InterceptorInvoker> interceptorInvokers, + Callable<Object> callable) { + this.target = target; + this.method = method; + this.args = args == null ? new Object[] {} : args; + this.interceptorInvokers = interceptorInvokers; + this.callable = callable; + } + + boolean hasNextInterceptor() { + return index < interceptorInvokers.size(); + } + + protected Object invokeNextInterceptor() throws Exception { + int oldIndex = index; + try { + // Note that some FaultTolerance interceptors can cause + // some interesting behaviors if they are invoked before + // other interceptors. The CDIInterceptorWrapperImpl + // intentionally orders the FT interceptor last to + // avoid these side effects. + return interceptorInvokers.get(index++).invoke(this); + } finally { + index = oldIndex; + } + } + + protected Object interceptorChainCompleted() throws Exception { + return callable.call(); + } + + @Override + public Object proceed() throws Exception { + if (hasNextInterceptor()) { + return invokeNextInterceptor(); + } else { + return interceptorChainCompleted(); + } + } + + @Override + public Object getTarget() { + return target; + } + + @Override + public Method getMethod() { + return method; + } + + @Override + public Constructor<?> getConstructor() { + return null; + } + + @Override + public Object[] getParameters() throws IllegalStateException { + return args; + } + + @Override + public void setParameters(Object[] params) throws IllegalStateException, IllegalArgumentException { + this.args = params; + } + + @Override + public Map<String, Object> getContextData() { + return contextData; + } + + @Override + public Object getTimer() { + return null; + } + +} diff --git a/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/cdi/RestClientBean.java b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/cdi/RestClientBean.java index 04a8e70cbd0..89d2b7ddc9a 100644 --- a/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/cdi/RestClientBean.java +++ b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/cdi/RestClientBean.java @@ -28,7 +28,6 @@ import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.NoSuchElementException; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; @@ -48,6 +47,7 @@ import org.apache.cxf.common.logging.LogUtils; import org.apache.cxf.microprofile.client.CxfTypeSafeClientBuilder; import org.apache.cxf.microprofile.client.config.ConfigFacade; +import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; import org.eclipse.microprofile.rest.client.inject.RestClient; public class RestClientBean implements Bean<Object>, PassivationCapable { @@ -138,21 +138,26 @@ public boolean isAlternative() { private String getBaseUri() { String interfaceName = clientInterface.getName(); - String property = String.format(REST_URI_FORMAT, interfaceName); - String baseURI = null; - try { - baseURI = ConfigFacade.getValue(property, String.class); - } catch (NoSuchElementException ex) { - // no-op - will revert to baseURL config value (as opposed to baseURI) - } + String baseURI = ConfigFacade + .getOptionalValue(String.format(REST_URI_FORMAT, interfaceName), String.class) + .orElseGet(() -> ConfigFacade.getOptionalValue(String.format(REST_URL_FORMAT, interfaceName), + String.class).orElse(null)); + if (baseURI == null) { - // revert to baseUrl - property = String.format(REST_URL_FORMAT, interfaceName); - baseURI = ConfigFacade.getValue(property, String.class); - if (baseURI == null) { - throw new IllegalStateException("Unable to determine base URI from configuration"); + // last, if baseUrl/Uri is not specified via MP Config, check the @RegisterRestClient annotation + RegisterRestClient anno = clientInterface.getAnnotation(RegisterRestClient.class); + if (anno != null) { + String annoUri = anno.baseUri(); + if (annoUri != null && !"".equals(anno.baseUri())) { + baseURI = annoUri; + } } } + + if (baseURI == null) { + throw new IllegalStateException("Unable to determine base URI from configuration for " + + interfaceName); + } return baseURI; } diff --git a/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/config/ConfigFacade.java b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/config/ConfigFacade.java index e543d7c0cfb..9bb688e6ce1 100644 --- a/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/config/ConfigFacade.java +++ b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/config/ConfigFacade.java @@ -20,6 +20,7 @@ package org.apache.cxf.microprofile.client.config; import java.util.Optional; +import java.util.OptionalLong; import org.eclipse.microprofile.config.Config; import org.eclipse.microprofile.config.ConfigProvider; @@ -28,7 +29,7 @@ private ConfigFacade() { } - + private static Optional<Config> config() { Config c; try { @@ -39,15 +40,22 @@ private ConfigFacade() { } return Optional.ofNullable(c); } - + public static <T> Optional<T> getOptionalValue(String propertyName, Class<T> clazz) { Optional<Config> c = config(); return c.isPresent() ? c.get().getOptionalValue(propertyName, clazz) : Optional.empty(); } - + public static <T> T getValue(String propertyName, Class<T> clazz) { Optional<Config> c = config(); return c.isPresent() ? c.get().getValue(propertyName, clazz) : null; } - + + public static OptionalLong getOptionalLong(String propName) { + Optional<Config> c = config(); + Optional<Long> opt = + c.isPresent() ? c.get().getOptionalValue(propName, Long.class) : Optional.empty(); + return opt.isPresent() ? OptionalLong.of(opt.get()) : OptionalLong.empty(); + + } } diff --git a/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/proxy/MPAsyncInvocationInterceptorImpl.java b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/proxy/MPAsyncInvocationInterceptorImpl.java index 13ecf18b346..5871c8a6fb4 100644 --- a/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/proxy/MPAsyncInvocationInterceptorImpl.java +++ b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/proxy/MPAsyncInvocationInterceptorImpl.java @@ -41,8 +41,18 @@ private final List<AsyncInvocationInterceptor> interceptors = new ArrayList<>(); - MPAsyncInvocationInterceptorImpl() { + MPAsyncInvocationInterceptorImpl(Message message) { super(Phase.POST_MARSHAL); + + MicroProfileClientProviderFactory factory = MicroProfileClientProviderFactory.getInstance(message); + List<ProviderInfo<Object>> aiiProviderList = + factory.getAsyncInvocationInterceptorFactories(); + + for (ProviderInfo<Object> providerInfo: aiiProviderList) { + AsyncInvocationInterceptor aiInterceptor = + ((AsyncInvocationInterceptorFactory) providerInfo.getProvider()).newInterceptor(); + interceptors.add(0, aiInterceptor); // sort in reverse order + } } List<AsyncInvocationInterceptor> getInterceptors() { @@ -52,23 +62,14 @@ /** {@inheritDoc}*/ @Override public void handleMessage(Message message) throws Fault { - MicroProfileClientProviderFactory factory = MicroProfileClientProviderFactory.getInstance(message); - List<ProviderInfo<Object>> aiiProviderList = - factory.getAsyncInvocationInterceptorFactories(); - - for (ProviderInfo<Object> providerInfo: aiiProviderList) { - AsyncInvocationInterceptor aiInterceptor = - ((AsyncInvocationInterceptorFactory) providerInfo.getProvider()).newInterceptor(); + for (int i = interceptors.size() - 1; i >= 0; i--) { try { - aiInterceptor.prepareContext(); - interceptors.add(0, aiInterceptor); // sort in reverse order + interceptors.get(i).prepareContext(); } catch (Throwable t) { LOG.log(Level.WARNING, "ASYNC_INTERCEPTOR_EXCEPTION_PREPARE_CONTEXT", - new Object[]{aiInterceptor.getClass().getName(), t}); + new Object[]{interceptors.get(i).getClass().getName(), t}); } } - - //message.getInterceptorChain().add(new MPAsyncInvocationInterceptorPostAsyncImpl(interceptors)); } } diff --git a/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/proxy/MPAsyncInvocationInterceptorRemoveContextImpl.java b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/proxy/MPAsyncInvocationInterceptorRemoveContextImpl.java new file mode 100644 index 00000000000..8813ee4e74f --- /dev/null +++ b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/proxy/MPAsyncInvocationInterceptorRemoveContextImpl.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.microprofile.client.proxy; + +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.apache.cxf.common.logging.LogUtils; +import org.apache.cxf.interceptor.Fault; +import org.apache.cxf.message.Message; +import org.apache.cxf.phase.AbstractPhaseInterceptor; +import org.apache.cxf.phase.Phase; +import org.eclipse.microprofile.rest.client.ext.AsyncInvocationInterceptor; + +/** + * + */ +public class MPAsyncInvocationInterceptorRemoveContextImpl extends AbstractPhaseInterceptor<Message> { + private static final Logger LOG = LogUtils.getL7dLogger(MPAsyncInvocationInterceptorRemoveContextImpl.class); + + private List<AsyncInvocationInterceptor> interceptors; + + public MPAsyncInvocationInterceptorRemoveContextImpl(List<AsyncInvocationInterceptor> interceptors) { + super(Phase.POST_INVOKE); + this.interceptors = interceptors; + } + + /** {@inheritDoc}*/ + @Override + public void handleMessage(Message message) throws Fault { + for (AsyncInvocationInterceptor interceptor : interceptors) { + try { + interceptor.removeContext(); + } catch (Throwable t) { + LOG.log(Level.WARNING, "ASYNC_INTERCEPTOR_EXCEPTION_REMOVE_CONTEXT", + new Object[]{interceptor.getClass().getName(), t}); + } + } + } +} \ No newline at end of file diff --git a/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/proxy/MicroProfileClientProxyImpl.java b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/proxy/MicroProfileClientProxyImpl.java index 1296cdc69af..ea7db941bda 100644 --- a/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/proxy/MicroProfileClientProxyImpl.java +++ b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/proxy/MicroProfileClientProxyImpl.java @@ -24,14 +24,19 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.Callable; import java.util.concurrent.CompletionStage; import java.util.concurrent.ExecutorService; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.ws.rs.client.InvocationCallback; import javax.ws.rs.core.Configuration; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; +import org.apache.cxf.common.logging.LogUtils; +import org.apache.cxf.interceptor.Interceptor; import org.apache.cxf.jaxrs.client.ClientProxyImpl; import org.apache.cxf.jaxrs.client.ClientState; import org.apache.cxf.jaxrs.client.JaxrsClientCallback; @@ -43,9 +48,14 @@ import org.apache.cxf.message.Message; import org.apache.cxf.microprofile.client.MPRestClientCallback; import org.apache.cxf.microprofile.client.MicroProfileClientProviderFactory; +import org.apache.cxf.microprofile.client.cdi.CDIInterceptorWrapper; import org.eclipse.microprofile.rest.client.ext.ResponseExceptionMapper; +import static org.apache.cxf.jaxrs.client.ClientProperties.HTTP_CONNECTION_TIMEOUT_PROP; +import static org.apache.cxf.jaxrs.client.ClientProperties.HTTP_RECEIVE_TIMEOUT_PROP; + public class MicroProfileClientProxyImpl extends ClientProxyImpl { + private static final Logger LOG = LogUtils.getL7dLogger(MicroProfileClientProxyImpl.class); private static final InvocationCallback<Object> NO_OP_CALLBACK = new InvocationCallback<Object>() { @Override @@ -55,23 +65,27 @@ public void failed(Throwable t) { } public void completed(Object o) { } }; - private final MPAsyncInvocationInterceptorImpl aiiImpl = new MPAsyncInvocationInterceptorImpl(); - + private final CDIInterceptorWrapper interceptorWrapper; + //CHECKSTYLE:OFF public MicroProfileClientProxyImpl(URI baseURI, ClassLoader loader, ClassResourceInfo cri, boolean isRoot, boolean inheritHeaders, ExecutorService executorService, - Configuration configuration, Object... varValues) { + Configuration configuration, CDIInterceptorWrapper interceptorWrapper, + Object... varValues) { super(new LocalClientState(baseURI), loader, cri, isRoot, inheritHeaders, varValues); cfg.getRequestContext().put(EXECUTOR_SERVICE_PROPERTY, executorService); cfg.getRequestContext().putAll(configuration.getProperties()); + this.interceptorWrapper = interceptorWrapper; } public MicroProfileClientProxyImpl(ClientState initialState, ClassLoader loader, ClassResourceInfo cri, boolean isRoot, boolean inheritHeaders, ExecutorService executorService, - Configuration configuration, Object... varValues) { + Configuration configuration, CDIInterceptorWrapper interceptorWrapper, + Object... varValues) { super(initialState, loader, cri, isRoot, inheritHeaders, varValues); cfg.getRequestContext().put(EXECUTOR_SERVICE_PROPERTY, executorService); cfg.getRequestContext().putAll(configuration.getProperties()); + this.interceptorWrapper = interceptorWrapper; } //CHECKSTYLE:ON @@ -99,15 +113,25 @@ protected boolean checkAsyncReturnType(OperationResourceInfo ori, @Override protected Object doInvokeAsync(OperationResourceInfo ori, Message outMessage, InvocationCallback<Object> asyncCallback) { + MPAsyncInvocationInterceptorImpl aiiImpl = new MPAsyncInvocationInterceptorImpl(outMessage); outMessage.getInterceptorChain().add(aiiImpl); - cfg.getInInterceptors().add(new MPAsyncInvocationInterceptorPostAsyncImpl(aiiImpl.getInterceptors())); + List<Interceptor<? extends Message>>inboundChain = cfg.getInInterceptors(); + inboundChain.add(new MPAsyncInvocationInterceptorPostAsyncImpl(aiiImpl.getInterceptors())); + inboundChain.add(new MPAsyncInvocationInterceptorRemoveContextImpl(aiiImpl.getInterceptors())); + setTimeouts(cfg.getRequestContext()); super.doInvokeAsync(ori, outMessage, asyncCallback); JaxrsClientCallback<?> cb = outMessage.getExchange().get(JaxrsClientCallback.class); return cb.createFuture(); } + @Override + protected void doRunInterceptorChain(Message message) { + setTimeouts(cfg.getRequestContext()); + super.doRunInterceptorChain(message); + } + @Override protected JaxrsClientCallback<?> newJaxrsClientCallback(InvocationCallback<Object> asyncCallback, Class<?> responseClass, @@ -171,4 +195,80 @@ protected Message createMessage(Object body, filterProps.put("org.eclipse.microprofile.rest.client.invokedMethod", m); return msg; } + + protected void setTimeouts(Map<String, Object> props) { + try { + Long connectTimeout = getIntFromProps(props, HTTP_CONNECTION_TIMEOUT_PROP); + Long readTimeout = getIntFromProps(props, HTTP_RECEIVE_TIMEOUT_PROP); + if (connectTimeout > -1) { + cfg.getHttpConduit().getClient().setConnectionTimeout(connectTimeout); + } + if (readTimeout > -1) { + cfg.getHttpConduit().getClient().setReceiveTimeout(readTimeout); + } + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + private Long getIntFromProps(Map<String, Object> props, String key) { + Object o = props.get(key); + if (o == null) { + return -1L; // not declared + } + Long l; + if (o instanceof Long) { + l = (Long) o; + } else if (o instanceof String) { + try { + l = Long.parseLong((String)o); + } catch (NumberFormatException ex) { + LOG.log(Level.WARNING, "INVALID_TIMEOUT_PROPERTY", new Object[]{key, o}); + return -1L; // + } + } else { + LOG.log(Level.WARNING, "INVALID_TIMEOUT_PROPERTY", new Object[]{key, o}); + return -1L; + } + if (l < 0) { + LOG.log(Level.WARNING, "INVALID_TIMEOUT_PROPERTY", new Object[]{key, o}); + return -1L; + } + return l; + } + + @Override + public Object invoke(Object o, Method m, Object[] params) throws Throwable { + return interceptorWrapper.invoke(o, m, params, new Invoker(o, m, params, this)); + } + + private Object invokeActual(Object o, Method m, Object[] params) throws Throwable { + return super.invoke(o, m, params); + } + + private static class Invoker implements Callable<Object> { + private final Object targetObject; + private final Method method; + private final Object[] params; + private final MicroProfileClientProxyImpl proxy; + + Invoker(Object o, Method m, Object[] params, MicroProfileClientProxyImpl proxy) { + this.targetObject = o; + this.method = m; + this.params = params; + this.proxy = proxy; + } + + @Override + public Object call() throws Exception { + try { + return proxy.invokeActual(targetObject, method, params); + } catch (Throwable t) { + if (t instanceof Exception) { + throw (Exception) t; + } + throw new RuntimeException(t); + } + } + } } diff --git a/systests/microprofile/client/async/pom.xml b/systests/microprofile/client/async/pom.xml index 80db328436c..043b095a7aa 100644 --- a/systests/microprofile/client/async/pom.xml +++ b/systests/microprofile/client/async/pom.xml @@ -33,7 +33,7 @@ <properties> <cxf.module.name>org.apache.cxf.systests.microprofile.async</cxf.module.name> <cxf.geronimo.config.version>1.0</cxf.geronimo.config.version> - <cxf.microprofile.rest.client.version>1.1</cxf.microprofile.rest.client.version> + <cxf.microprofile.rest.client.version>1.2-m2</cxf.microprofile.rest.client.version> <cxf.wiremock.params>--port=8765</cxf.wiremock.params> <cxf.weld.se.version>2.4.5.Final</cxf.weld.se.version> <cxf.arquillian.weld.container.version>2.0.0.Final</cxf.arquillian.weld.container.version> diff --git a/systests/microprofile/client/async/src/test/java/org/apache/cxf/systest/microprofile/rest/client/mock/AsyncInvocationInterceptorFactoryTestImpl.java b/systests/microprofile/client/async/src/test/java/org/apache/cxf/systest/microprofile/rest/client/mock/AsyncInvocationInterceptorFactoryTestImpl.java index 242b161972c..75b863498c4 100644 --- a/systests/microprofile/client/async/src/test/java/org/apache/cxf/systest/microprofile/rest/client/mock/AsyncInvocationInterceptorFactoryTestImpl.java +++ b/systests/microprofile/client/async/src/test/java/org/apache/cxf/systest/microprofile/rest/client/mock/AsyncInvocationInterceptorFactoryTestImpl.java @@ -51,6 +51,14 @@ public void applyContext() { list.add(Thread.currentThread().getName()); list.add(AsyncInvocationInterceptorFactoryTestImpl.class.getSimpleName()); } + + /** {@inheritDoc}*/ + @Override + public void removeContext() { + List<String> list = INBOUND.get(); + list.add("REMOVE-" + Thread.currentThread().getName()); + list.add("REMOVE-" + AsyncInvocationInterceptorFactoryTestImpl.class.getSimpleName()); + } } /** {@inheritDoc}*/ diff --git a/systests/microprofile/client/async/src/test/java/org/apache/cxf/systest/microprofile/rest/client/mock/AsyncInvocationInterceptorFactoryTestImpl2.java b/systests/microprofile/client/async/src/test/java/org/apache/cxf/systest/microprofile/rest/client/mock/AsyncInvocationInterceptorFactoryTestImpl2.java index e4ceb6e6704..2576cb4c6b0 100644 --- a/systests/microprofile/client/async/src/test/java/org/apache/cxf/systest/microprofile/rest/client/mock/AsyncInvocationInterceptorFactoryTestImpl2.java +++ b/systests/microprofile/client/async/src/test/java/org/apache/cxf/systest/microprofile/rest/client/mock/AsyncInvocationInterceptorFactoryTestImpl2.java @@ -46,6 +46,14 @@ public void applyContext() { list.add(Thread.currentThread().getName()); list.add(AsyncInvocationInterceptorFactoryTestImpl2.class.getSimpleName()); } + + /** {@inheritDoc}*/ + @Override + public void removeContext() { + List<String> list = AsyncInvocationInterceptorFactoryTestImpl.INBOUND.get(); + list.add("REMOVE-" + Thread.currentThread().getName()); + list.add("REMOVE-" + AsyncInvocationInterceptorFactoryTestImpl.class.getSimpleName()); + } } /** {@inheritDoc}*/ diff --git a/systests/microprofile/client/weld/testng.xml b/systests/microprofile/client/weld/testng.xml index cae845056c4..f2cea4b5514 100644 --- a/systests/microprofile/client/weld/testng.xml +++ b/systests/microprofile/client/weld/testng.xml @@ -1,27 +1,11 @@ <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" > -<suite name="MPRestClientTCK1.1"> - <test name="All tests except Executor Service Test"> +<suite name="MPRestClientTCK1.2"> + <test name="All TCK Tests"> <packages> <package name="org.eclipse.microprofile.rest.client.tck" /> + <package name="org.eclipse.microprofile.rest.client.tck.asynctests" /> <package name="org.eclipse.microprofile.rest.client.tck.cditests" /> </packages> - <classes> - <class name="org.eclipse.microprofile.rest.client.tck.asynctests.CDIInvokeSimpleGetOperationTest" /> - <class name="org.eclipse.microprofile.rest.client.tck.asynctests.AsyncMethodTest"> - <methods> - <exclude name="testExecutorService" /> - </methods> - </class> - </classes> - </test> - <test name="Solo Executor Service Test"> - <classes> - <class name="org.eclipse.microprofile.rest.client.tck.asynctests.AsyncMethodTest"> - <methods> - <include name="testExecutorService" /> - </methods> - </class> - </classes> </test> </suite> diff --git a/systests/microprofile/pom.xml b/systests/microprofile/pom.xml index 18100adc643..f92b748ed9b 100644 --- a/systests/microprofile/pom.xml +++ b/systests/microprofile/pom.xml @@ -33,7 +33,7 @@ <url>http://cxf.apache.org</url> <properties> <cxf.geronimo.config.version>1.0</cxf.geronimo.config.version> - <cxf.microprofile.rest.client.version>1.1</cxf.microprofile.rest.client.version> + <cxf.microprofile.rest.client.version>1.2-m2</cxf.microprofile.rest.client.version> <cxf.wiremock.params>--port=8765</cxf.wiremock.params> <cxf.weld.se.version>2.4.5.Final</cxf.weld.se.version> <cxf.arquillian.weld.container.version>2.0.0.Final</cxf.arquillian.weld.container.version> ---------------------------------------------------------------- This is an automated message from the Apache Git Service. To respond to the message, please log on GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: us...@infra.apache.org With regards, Apache Git Services