You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@cxf.apache.org by "ASF GitHub Bot (JIRA)" <ji...@apache.org> on 2018/08/01 22:04:01 UTC

[jira] [Commented] (CXF-7673) Implement MicroProfile Rest Client 1.1

    [ https://issues.apache.org/jira/browse/CXF-7673?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16566053#comment-16566053 ] 

ASF GitHub Bot commented on CXF-7673:
-------------------------------------

andymc12 closed pull request #396: [CXF-7673] MP Rest Client 1.1 Impl
URL: https://github.com/apache/cxf/pull/396
 
 
   

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/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/ResponseImpl.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/ResponseImpl.java
index 617e8eec118..0dfeec030c9 100644
--- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/ResponseImpl.java
+++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/ResponseImpl.java
@@ -238,6 +238,9 @@ public int getLength() {
 
     public URI getLocation() {
         Object header = metadata.getFirst(HttpHeaders.LOCATION);
+        if (header == null) {
+            header = outMessage.get(Message.REQUEST_URI);
+        }
         return header == null || header instanceof URI ? (URI)header
             : URI.create(header.toString());
     }
diff --git a/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java b/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java
index 8ac886609b2..7993aae588e 100644
--- a/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java
+++ b/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java
@@ -245,7 +245,7 @@ private void addNonEmptyPath(UriBuilder builder, String pathValue) {
         }
     }
 
-    private static MultivaluedMap<ParameterType, Parameter> getParametersInfo(Method m,
+    private MultivaluedMap<ParameterType, Parameter> getParametersInfo(Method m,
         Object[] params, OperationResourceInfo ori) {
         MultivaluedMap<ParameterType, Parameter> map =
             new MetadataMap<ParameterType, Parameter>();
@@ -280,7 +280,7 @@ private void addNonEmptyPath(UriBuilder builder, String pathValue) {
         return map;
     }
 
-    private static boolean isIgnorableParameter(Method m, Parameter p) {
+    protected boolean isIgnorableParameter(Method m, Parameter p) {
         if (p.getType() == ParameterType.CONTEXT) {
             return true;
         }
@@ -592,7 +592,7 @@ private boolean getValuesFromBeanParamField(Object bean,
         }
         return jaxrsParamAnnAvailable;
     }
-    
+
     private void handleMatrixes(Method m,
                                 Object[] params,
                                 MultivaluedMap<ParameterType, Parameter> map,
@@ -772,10 +772,9 @@ private Object doChainedInvocation(URI uri,
             reqContext.put(PROXY_METHOD_PARAM_BODY_INDEX, bodyIndex);
 
             // execute chain
-            InvocationCallback<Object> asyncCallback = checkAsyncCallback(ori, reqContext);
+            InvocationCallback<Object> asyncCallback = checkAsyncCallback(ori, reqContext, outMessage);
             if (asyncCallback != null) {
-                doInvokeAsync(ori, outMessage, asyncCallback);
-                return null;
+                return doInvokeAsync(ori, outMessage, asyncCallback);
             }
             doRunInterceptorChain(outMessage);
 
@@ -801,8 +800,9 @@ private Object doChainedInvocation(URI uri,
 
     }
 
-    private InvocationCallback<Object> checkAsyncCallback(OperationResourceInfo ori,
-                                                          Map<String, Object> reqContext) {
+    protected InvocationCallback<Object> checkAsyncCallback(OperationResourceInfo ori,
+                                                            Map<String, Object> reqContext,
+                                                            Message outMessage) {
         Object callbackProp = reqContext.get(InvocationCallback.class.getName());
         if (callbackProp != null) {
             if (callbackProp instanceof Collection) {
@@ -820,7 +820,6 @@ private Object doChainedInvocation(URI uri,
             }
         }
         return null;
-
     }
 
     private InvocationCallback<Object> doCheckAsyncCallback(OperationResourceInfo ori,
@@ -837,16 +836,23 @@ private Object doChainedInvocation(URI uri,
         return null;
     }
 
-    protected void doInvokeAsync(OperationResourceInfo ori, Message outMessage,
-                                 InvocationCallback<Object> asyncCallback) {
+    protected Object doInvokeAsync(OperationResourceInfo ori, 
+                                   Message outMessage,
+                                   InvocationCallback<Object> asyncCallback) {
         outMessage.getExchange().setSynchronous(false);
         setAsyncMessageObserverIfNeeded(outMessage.getExchange());
-        JaxrsClientCallback<?> cb = new JaxrsClientCallback<Object>(asyncCallback,
+        JaxrsClientCallback<?> cb = newJaxrsClientCallback(asyncCallback,
             ori.getMethodToInvoke().getReturnType(), ori.getMethodToInvoke().getGenericReturnType());
         outMessage.getExchange().put(JaxrsClientCallback.class, cb);
         doRunInterceptorChain(outMessage);
 
+        return null;
+    }
 
+    protected JaxrsClientCallback<?> newJaxrsClientCallback(InvocationCallback<Object> asyncCallback,
+                                                            Class<?> responseClass,
+                                                            Type outGenericType) {
+        return new JaxrsClientCallback<Object>(asyncCallback, responseClass, outGenericType);
     }
 
     @Override
@@ -885,7 +891,7 @@ protected Object handleResponse(Message outMessage, Class<?> serviceCls)
                 r.bufferEntity();
             }
 
-            Class<?> returnType = method.getReturnType();
+            Class<?> returnType = getReturnType(method, outMessage);
             Type genericType =
                 InjectionUtils.processGenericTypeIfNeeded(serviceCls,
                                                           returnType,
@@ -901,6 +907,10 @@ protected Object handleResponse(Message outMessage, Class<?> serviceCls)
         }
     }
 
+    protected Class<?> getReturnType(Method method, Message outMessage) {
+        return method.getReturnType();
+    }
+
     public Object getInvocationHandler() {
         return this;
     }
diff --git a/rt/rs/microprofile-client/pom.xml b/rt/rs/microprofile-client/pom.xml
index 1e9f6270875..3062c952f3f 100644
--- a/rt/rs/microprofile-client/pom.xml
+++ b/rt/rs/microprofile-client/pom.xml
@@ -69,6 +69,11 @@
             <artifactId>cxf-rt-rs-client</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.apache.cxf</groupId>
+            <artifactId>cxf-rt-frontend-jaxrs</artifactId>
+            <version>${project.version}</version>
+        </dependency>
         <dependency>
             <groupId>org.apache.cxf</groupId>
             <artifactId>cxf-rt-transports-local</artifactId>
@@ -99,6 +104,7 @@
         <dependency>
             <groupId>org.eclipse.microprofile.rest.client</groupId>
             <artifactId>microprofile-rest-client-api</artifactId>
+            <version>1.1</version>
         </dependency>
         <dependency>
             <groupId>org.apache.cxf</groupId>
@@ -113,6 +119,7 @@
         <dependency>
             <groupId>org.eclipse.microprofile.rest.client</groupId>
             <artifactId>microprofile-rest-client-tck</artifactId>
+            <version>1.1</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 5f62a56ee49..ed7715233b4 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
@@ -18,9 +18,11 @@
  */
 package org.apache.cxf.microprofile.client;
 
+import java.net.URI;
 import java.net.URL;
 import java.util.Map;
 import java.util.Objects;
+import java.util.concurrent.ExecutorService;
 
 import javax.ws.rs.core.Configurable;
 import javax.ws.rs.core.Configuration;
@@ -30,6 +32,7 @@
 
 public class CxfTypeSafeClientBuilder implements RestClientBuilder, Configurable<RestClientBuilder> {
     private String baseUri;
+    private ExecutorService executorService;
     private final MicroProfileClientConfigurableImpl<RestClientBuilder> configImpl =
             new MicroProfileClientConfigurableImpl<>(this);
 
@@ -39,6 +42,21 @@ public RestClientBuilder baseUrl(URL url) {
         return this;
     }
 
+    @Override
+    public RestClientBuilder baseUri(URI uri) {
+        this.baseUri = Objects.requireNonNull(uri).toString();
+        return this;
+    }
+
+    @Override
+    public RestClientBuilder executorService(ExecutorService executor) {
+        if (null == executor) {
+            throw new IllegalArgumentException("executor must not be null");
+        }
+        this.executorService = executor;
+        return this;
+    }
+
     @Override
     public <T> T build(Class<T> aClass) {
         if (baseUri == null) {
@@ -59,7 +77,7 @@ public RestClientBuilder baseUrl(URL url) {
             }
         }
         MicroProfileClientFactoryBean bean = new MicroProfileClientFactoryBean(configImpl,
-                baseUri, aClass);
+                baseUri, aClass, executorService);
         return bean.create(aClass);
     }
 
diff --git a/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/MPRestClientCallback.java b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/MPRestClientCallback.java
new file mode 100644
index 00000000000..a6863a37b14
--- /dev/null
+++ b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/MPRestClientCallback.java
@@ -0,0 +1,100 @@
+/**
+ * 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.lang.reflect.Type;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import javax.ws.rs.client.InvocationCallback;
+
+import org.apache.cxf.jaxrs.client.JaxrsClientCallback;
+
+public class MPRestClientCallback<T> extends JaxrsClientCallback {
+
+    public MPRestClientCallback(InvocationCallback<T> handler,
+                                Class<?> responseClass,
+                                Type outGenericType) {
+        super(handler, responseClass, outGenericType);
+    }
+
+    @Override
+    public Future<T> createFuture() {
+        return new MPRestClientResponseFuture<T>(this);
+    }
+
+    static class MPRestClientResponseFuture<T> extends CompletableFuture<T> implements Future<T> {
+        MPRestClientCallback<T> callback;
+        MPRestClientResponseFuture(MPRestClientCallback<T> cb) {
+            callback = cb;
+        }
+
+        public Map<String, Object> getContext() {
+            try {
+                return callback.getResponseContext();
+            } catch (Exception ex) {
+                return null;
+            }
+        }
+        public boolean cancel(boolean mayInterruptIfRunning) {
+            return callback.cancel(mayInterruptIfRunning);
+        }
+
+        public T get() throws InterruptedException, ExecutionException {
+            try {
+                return getObject(callback.get()[0]);
+            } catch (InterruptedException ex) {
+                InvocationCallback<T> handler = callback.getHandler();
+                if (handler != null) {
+                    handler.failed(ex);
+                }
+                throw ex;
+            }
+        }
+        public T get(long timeout, TimeUnit unit) throws InterruptedException,
+            ExecutionException, TimeoutException {
+            try {
+                return getObject(callback.get(timeout, unit)[0]);
+            } catch (InterruptedException ex) {
+                InvocationCallback<T> handler = callback.getHandler();
+                if (handler != null) {
+                    handler.failed(ex);
+                }
+                throw ex;
+            }
+        }
+
+        @SuppressWarnings("unchecked")
+        private T getObject(Object object) {
+            return (T)object;
+        }
+
+        public boolean isCancelled() {
+            return callback.isCancelled();
+        }
+        public boolean isDone() {
+            return callback.isDone();
+        }
+    }
+}
\ No newline at end of file
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 2280582b3f2..f4c7633d409 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
@@ -22,4 +22,8 @@
 VALIDATION_NOT_AN_INTERFACE=The type, {0},  passed in to the build method is not an interface.
 VALIDATION_METHOD_WITH_MULTIPLE_VERBS=The client interface contains a method, {0} annotated with more than one HTTP method: {1}
 VALIDATION_UNRESOLVED_PATH_PARAMS=The client interface, {0} has one or more methods with unresolved path template variables: {1}
-VALIDATION_EXTRA_PATH_PARAMS=The client interface, {0} has one or more extra path segments not in the template: {1}
\ No newline at end of file
+VALIDATION_EXTRA_PATH_PARAMS=The client interface, {0} has one or more extra path segments not in the template: {1}
+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
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 a94af367279..9abee1a418f 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
@@ -24,6 +24,7 @@
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.ExecutorService;
 
 import javax.ws.rs.client.ClientRequestFilter;
 import javax.ws.rs.client.ClientResponseFilter;
@@ -48,12 +49,14 @@
     private Configuration configuration;
     private ClassLoader proxyLoader;
     private boolean inheritHeaders;
+    private ExecutorService executorService;
 
     public MicroProfileClientFactoryBean(MicroProfileClientConfigurableImpl<RestClientBuilder> configuration,
-                                         String baseUri, Class<?> aClass) {
+                                         String baseUri, Class<?> aClass, ExecutorService executorService) {
         super();
         this.configuration = configuration.getConfiguration();
         this.comparator = MicroProfileClientProviderFactory.createComparator(this);
+        this.executorService = executorService;
         super.setAddress(baseUri);
         super.setServiceClass(aClass);
         super.setProviderComparator(comparator);
@@ -78,6 +81,10 @@ public void setInheritHeaders(boolean inheritHeaders) {
         this.inheritHeaders = inheritHeaders;
     }
 
+    public ExecutorService getExecutorService() {
+        return executorService;
+    }
+
     @Override
     protected void initClient(AbstractClient client, Endpoint ep, boolean addHeaders) {
         super.initClient(client, ep, addHeaders);
@@ -92,10 +99,10 @@ protected ClientProxyImpl createClientProxy(ClassResourceInfo cri, boolean isRoo
                                                 ClientState actualState, Object[] varValues) {
         if (actualState == null) {
             return new MicroProfileClientProxyImpl(URI.create(getAddress()), proxyLoader, cri, isRoot,
-                    inheritHeaders, varValues);
+                    inheritHeaders, executorService, varValues);
         } else {
             return new MicroProfileClientProxyImpl(actualState, proxyLoader, cri, isRoot,
-                    inheritHeaders, varValues);
+                    inheritHeaders, executorService, varValues);
         }
     }
 
diff --git a/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/MicroProfileClientProviderFactory.java b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/MicroProfileClientProviderFactory.java
index 01d16fea370..f02739e7c5d 100644
--- a/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/MicroProfileClientProviderFactory.java
+++ b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/MicroProfileClientProviderFactory.java
@@ -28,6 +28,7 @@
 
 import org.apache.cxf.Bus;
 import org.apache.cxf.BusFactory;
+import org.apache.cxf.common.classloader.ClassLoaderUtils;
 import org.apache.cxf.common.util.ClassHelper;
 import org.apache.cxf.endpoint.Endpoint;
 import org.apache.cxf.jaxrs.model.ProviderInfo;
@@ -37,8 +38,8 @@
 
 public final class MicroProfileClientProviderFactory extends ProviderFactory {
     static final String CLIENT_FACTORY_NAME = MicroProfileClientProviderFactory.class.getName();
-    private List<ProviderInfo<ResponseExceptionMapper<?>>> responseExceptionMappers =
-            new ArrayList<ProviderInfo<ResponseExceptionMapper<?>>>(1);
+    private List<ProviderInfo<ResponseExceptionMapper<?>>> responseExceptionMappers = new ArrayList<>(1);
+    private List<ProviderInfo<Object>> asyncInvocationInterceptorFactories = new ArrayList<>();
     private final Comparator<ProviderInfo<?>> comparator;
 
     private MicroProfileClientProviderFactory(Bus bus, Comparator<ProviderInfo<?>> comparator) {
@@ -82,11 +83,24 @@ protected void setProviders(boolean custom, boolean busGlobal, Object... provide
             if (ResponseExceptionMapper.class.isAssignableFrom(providerCls)) {
                 addProviderToList(responseExceptionMappers, provider);
             }
+            String className = "org.eclipse.microprofile.rest.client.ext.AsyncInvocationInterceptorFactory";
+            try {
+                
+                Class<?> asyncIIFactoryClass = ClassLoaderUtils.loadClass(className,
+                                                                          MicroProfileClientProviderFactory.class);
+                if (asyncIIFactoryClass.isAssignableFrom(providerCls)) {
+                    addProviderToList(asyncInvocationInterceptorFactories, provider);
+                }
+            } catch (ClassNotFoundException ex) {
+                // expected if using the MP Rest Client 1.0 APIs
+            }
 
         }
         responseExceptionMappers.sort(comparator);
+        asyncInvocationInterceptorFactories.sort(comparator);
 
         injectContextProxies(responseExceptionMappers);
+        injectContextProxies(asyncInvocationInterceptorFactories);
     }
 
     public List<ResponseExceptionMapper<?>> createResponseExceptionMapper(Message m, Class<?> paramType) {
@@ -101,10 +115,15 @@ protected void setProviders(boolean custom, boolean busGlobal, Object... provide
                                             .collect(Collectors.toList()));
     }
 
+    public List<ProviderInfo<Object>> getAsyncInvocationInterceptorFactories() {
+        return asyncInvocationInterceptorFactories;
+    }
+
     @Override
     public void clearProviders() {
         super.clearProviders();
         responseExceptionMappers.clear();
+        asyncInvocationInterceptorFactories.clear();
     }
 
     @Override
diff --git a/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/Validator.java b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/Validator.java
index bcabd292bf8..31551640350 100644
--- a/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/Validator.java
+++ b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/Validator.java
@@ -45,7 +45,7 @@
     private Validator() {
     }
 
-
+    
     public static void checkValid(Class<?> userType) throws RestClientDefinitionException {
         if (!userType.isInterface()) {
             throwException("VALIDATION_NOT_AN_INTERFACE", userType);
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 23868f6704c..d9f47e95a80 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
@@ -20,13 +20,13 @@
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
-import java.net.MalformedURLException;
-import java.net.URL;
+import java.net.URI;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
+import java.util.NoSuchElementException;
 import java.util.Set;
 
 import javax.enterprise.context.Dependent;
@@ -45,6 +45,7 @@
 
 public class RestClientBean implements Bean<Object>, PassivationCapable {
     public static final String REST_URL_FORMAT = "%s/mp-rest/url";
+    public static final String REST_URI_FORMAT = "%s/mp-rest/uri";
     public static final String REST_SCOPE_FORMAT = "%s/mp-rest/scope";
     private static final Default DEFAULT_LITERAL = new DefaultLiteral();
     private final Class<?> clientInterface;
@@ -79,12 +80,8 @@ public boolean isNullable() {
     @Override
     public Object create(CreationalContext<Object> creationalContext) {
         CxfTypeSafeClientBuilder builder = new CxfTypeSafeClientBuilder();
-        String baseUrl = getBaseUrl();
-        try {
-            return builder.baseUrl(new URL(baseUrl)).build(clientInterface);
-        } catch (MalformedURLException e) {
-            throw new IllegalArgumentException("The value of URL was invalid " + baseUrl);
-        }
+        String baseUri = getBaseUri();
+        return builder.baseUri(URI.create(baseUri)).build(clientInterface);
     }
 
     @Override
@@ -122,13 +119,24 @@ public boolean isAlternative() {
         return false;
     }
 
-    private String getBaseUrl() {
-        String property = String.format(REST_URL_FORMAT, clientInterface.getName());
-        String baseURL = ConfigFacade.getValue(property, String.class);
-        if (baseURL == null) {
-            throw new IllegalStateException("Unable to determine base URL from configuration");
+    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)
+        }
+        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");
+            }
         }
-        return baseURL;
+        return baseURI;
     }
 
     private Class<? extends Annotation> readScope() {
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
new file mode 100644
index 00000000000..13ecf18b346
--- /dev/null
+++ b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/proxy/MPAsyncInvocationInterceptorImpl.java
@@ -0,0 +1,74 @@
+/**
+ * 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.ArrayList;
+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.jaxrs.model.ProviderInfo;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.microprofile.client.MicroProfileClientProviderFactory;
+import org.apache.cxf.phase.AbstractPhaseInterceptor;
+import org.apache.cxf.phase.Phase;
+import org.eclipse.microprofile.rest.client.ext.AsyncInvocationInterceptor;
+import org.eclipse.microprofile.rest.client.ext.AsyncInvocationInterceptorFactory;
+
+
+public class MPAsyncInvocationInterceptorImpl extends AbstractPhaseInterceptor<Message> {
+    private static final Logger LOG = LogUtils.getL7dLogger(MPAsyncInvocationInterceptorImpl.class);
+
+    private final List<AsyncInvocationInterceptor> interceptors = new ArrayList<>();
+
+    
+    MPAsyncInvocationInterceptorImpl() {
+        super(Phase.POST_MARSHAL);
+    }
+
+    List<AsyncInvocationInterceptor> getInterceptors() {
+        return interceptors;
+    }
+
+    /** {@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();
+            try {
+                aiInterceptor.prepareContext();
+                interceptors.add(0, aiInterceptor); // sort in reverse order
+            } catch (Throwable t) {
+                LOG.log(Level.WARNING, "ASYNC_INTERCEPTOR_EXCEPTION_PREPARE_CONTEXT", 
+                        new Object[]{aiInterceptor.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/MPAsyncInvocationInterceptorPostAsyncImpl.java b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/proxy/MPAsyncInvocationInterceptorPostAsyncImpl.java
new file mode 100644
index 00000000000..d5c653bca73
--- /dev/null
+++ b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/proxy/MPAsyncInvocationInterceptorPostAsyncImpl.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 MPAsyncInvocationInterceptorPostAsyncImpl extends AbstractPhaseInterceptor<Message> {
+    private static final Logger LOG = LogUtils.getL7dLogger(MPAsyncInvocationInterceptorPostAsyncImpl.class);
+
+    private List<AsyncInvocationInterceptor> interceptors;
+
+    public MPAsyncInvocationInterceptorPostAsyncImpl(List<AsyncInvocationInterceptor> interceptors) {
+        super(Phase.PRE_PROTOCOL);
+        this.interceptors = interceptors;
+    }
+
+    /** {@inheritDoc}*/
+    @Override
+    public void handleMessage(Message message) throws Fault {
+        for (AsyncInvocationInterceptor interceptor : interceptors) {
+            try {
+                interceptor.applyContext();
+            } catch (Throwable t) {
+                LOG.log(Level.WARNING, "ASYNC_INTERCEPTOR_EXCEPTION_APPLY_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 617efec2ad6..9038f2bc013 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
@@ -19,27 +19,92 @@
 package org.apache.cxf.microprofile.client.proxy;
 
 import java.lang.reflect.Method;
+import java.lang.reflect.Type;
 import java.net.URI;
 import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.ExecutorService;
 
+import javax.ws.rs.client.InvocationCallback;
 import javax.ws.rs.core.Response;
 
 import org.apache.cxf.jaxrs.client.ClientProxyImpl;
 import org.apache.cxf.jaxrs.client.ClientState;
+import org.apache.cxf.jaxrs.client.JaxrsClientCallback;
+import org.apache.cxf.jaxrs.client.LocalClientState;
 import org.apache.cxf.jaxrs.model.ClassResourceInfo;
+import org.apache.cxf.jaxrs.model.OperationResourceInfo;
+import org.apache.cxf.jaxrs.utils.InjectionUtils;
 import org.apache.cxf.message.Message;
+import org.apache.cxf.microprofile.client.MPRestClientCallback;
 import org.apache.cxf.microprofile.client.MicroProfileClientProviderFactory;
 import org.eclipse.microprofile.rest.client.ext.ResponseExceptionMapper;
 
 public class MicroProfileClientProxyImpl extends ClientProxyImpl {
+
+    private static final InvocationCallback<Object> NO_OP_CALLBACK = new InvocationCallback<Object>() {
+        @Override
+        public void failed(Throwable t) { }
+
+        @Override
+        public void completed(Object o) { }
+    };
+
+    private final MPAsyncInvocationInterceptorImpl aiiImpl = new MPAsyncInvocationInterceptorImpl();
+
     public MicroProfileClientProxyImpl(URI baseURI, ClassLoader loader, ClassResourceInfo cri,
-                                       boolean isRoot, boolean inheritHeaders, Object... varValues) {
-        super(baseURI, loader, cri, isRoot, inheritHeaders, varValues);
+                                       boolean isRoot, boolean inheritHeaders, ExecutorService executorService,
+                                       Object... varValues) {
+        super(new LocalClientState(baseURI), loader, cri, isRoot, inheritHeaders, varValues);
+        cfg.getRequestContext().put(EXECUTOR_SERVICE_PROPERTY, executorService);
     }
 
     public MicroProfileClientProxyImpl(ClientState initialState, ClassLoader loader, ClassResourceInfo cri,
-                                       boolean isRoot, boolean inheritHeaders, Object... varValues) {
+                                       boolean isRoot, boolean inheritHeaders, ExecutorService executorService,
+                                       Object... varValues) {
         super(initialState, loader, cri, isRoot, inheritHeaders, varValues);
+        cfg.getRequestContext().put(EXECUTOR_SERVICE_PROPERTY, executorService);
+    }
+
+
+
+    @SuppressWarnings("unchecked")
+    @Override
+    protected InvocationCallback<Object> checkAsyncCallback(OperationResourceInfo ori,
+                                                            Map<String, Object> reqContext,
+                                                            Message outMessage) {
+        InvocationCallback<Object> callback = outMessage.getContent(InvocationCallback.class);
+        if (callback == null && CompletionStage.class.equals(ori.getMethodToInvoke().getReturnType())) {
+            callback = NO_OP_CALLBACK;
+            outMessage.setContent(InvocationCallback.class, callback);
+        }
+        return callback;
+    }
+
+    protected boolean checkAsyncReturnType(OperationResourceInfo ori,
+                                           Map<String, Object> reqContext,
+                                           Message outMessage) {
+        return CompletionStage.class.equals(ori.getMethodToInvoke().getReturnType());
+    }
+
+    @Override
+    protected Object doInvokeAsync(OperationResourceInfo ori, Message outMessage,
+                                   InvocationCallback<Object> asyncCallback) {
+        outMessage.getInterceptorChain().add(aiiImpl);
+        cfg.getInInterceptors().add(new MPAsyncInvocationInterceptorPostAsyncImpl(aiiImpl.getInterceptors()));
+
+        super.doInvokeAsync(ori, outMessage, asyncCallback);
+
+        JaxrsClientCallback<?> cb = outMessage.getExchange().get(JaxrsClientCallback.class);
+        return cb.createFuture();
+    }
+
+    @Override
+    protected JaxrsClientCallback<?> newJaxrsClientCallback(InvocationCallback<Object> asyncCallback,
+                                                            Class<?> responseClass,
+                                                            Type outGenericType) {
+        return new MPRestClientCallback<Object>(asyncCallback, responseClass, outGenericType);
     }
 
     @Override
@@ -64,4 +129,14 @@ protected void checkResponse(Method m, Response r, Message inMessage) throws Thr
             }
         }
     }
+
+    @Override
+    protected Class<?> getReturnType(Method method, Message outMessage) {
+        Class<?> returnType = super.getReturnType(method, outMessage);
+        if (CompletionStage.class.isAssignableFrom(returnType)) {
+            Type t = method.getGenericReturnType();
+            returnType = InjectionUtils.getActualType(t);
+        }
+        return returnType;
+    }
 }
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 16fb8a9b9b8..5aa7c619880 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
@@ -119,7 +119,7 @@ public void testResponseExceptionMapper() throws Exception {
         Response r = client.getEntity();
         fail(r, "Did not throw expected mapped exception: NoSuchEntityException");
     }
-    
+
     @Test(expected = WebApplicationException.class)
     public void testDefaultResponseExceptionMapper() throws Exception {
         ExceptionMappingClient client = new CxfTypeSafeClientBuilder()
@@ -130,7 +130,7 @@ public void testDefaultResponseExceptionMapper() throws Exception {
         Response r = client.getEntity();
         fail(r, "Did not throw expected mapped exception: WebApplicationException");
     }
-    
+
     private void fail(Response r, String failureMessage) {
         System.out.println(r.getStatus());
         fail(failureMessage);
diff --git a/systests/microprofile/client/async/pom.xml b/systests/microprofile/client/async/pom.xml
new file mode 100644
index 00000000000..043301810dd
--- /dev/null
+++ b/systests/microprofile/client/async/pom.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0"?>
+<!--
+  ~  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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <parent>
+        <artifactId>cxf-microprofile-tck</artifactId>
+        <groupId>org.apache.cxf.systests</groupId>
+        <version>3.3.0-SNAPSHOT</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.apache.cxf.systests</groupId>
+    <artifactId>cxf-microprofile-async</artifactId>
+    <name>Apache CXF MicroProfile Async Sys Tests</name>
+    <description>Apache CXF System Tests - MicroProfile Rest Client Async Tests</description>
+    <url>http://cxf.apache.org</url>
+    <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.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>
+        <cxf.johnzon.tck.version>1.1.3</cxf.johnzon.tck.version>
+        <cxf.json.api.1.1.version>1.1</cxf.json.api.1.1.version>
+        <cxf.arquillian.version>1.1.14.Final</cxf.arquillian.version>
+        <cxf.slf4j.version>1.7.25</cxf.slf4j.version>
+        <cxf.commons.logging.version>1.2</cxf.commons.logging.version>
+        <cxf.javax.ws.rs.version>2.1</cxf.javax.ws.rs.version>
+        <cxf.wiremock.version>2.10.1</cxf.wiremock.version>
+    </properties>
+        <dependencies>
+            <dependency>
+                <groupId>org.hamcrest</groupId>
+                <artifactId>hamcrest-all</artifactId>
+                <version>1.3</version>
+                <scope>test</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.cxf</groupId>
+                <artifactId>cxf-rt-rs-mp-client</artifactId>
+                <scope>test</scope>
+            </dependency>
+            <dependency>
+                <groupId>commons-logging</groupId>
+                <artifactId>commons-logging</artifactId>
+                <version>${cxf.commons.logging.version}</version>
+                <scope>test</scope>
+            </dependency>
+            <dependency>
+                <groupId>javax.ws.rs</groupId>
+                <artifactId>javax.ws.rs-api</artifactId>
+                <version>${cxf.javax.ws.rs.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.jboss.weld.se</groupId>
+                <artifactId>weld-se-core</artifactId>
+                <version>${cxf.weld.se.version}</version>
+                <scope>test</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.jboss.arquillian.container</groupId>
+                <artifactId>arquillian-weld-embedded</artifactId>
+                <version>${cxf.arquillian.weld.container.version}</version>
+                <scope>test</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.eclipse.microprofile.rest.client</groupId>
+                <artifactId>microprofile-rest-client-api</artifactId>
+                <version>${cxf.microprofile.rest.client.version}</version>
+                <scope>test</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.johnzon</groupId>
+                <artifactId>johnzon-core</artifactId>
+                <version>${cxf.johnzon.tck.version}</version>
+                <scope>test</scope>
+            </dependency>
+            <dependency>
+                <groupId>javax.json</groupId>
+                <artifactId>javax.json-api</artifactId>
+                <version>${cxf.json.api.1.1.version}</version>
+                <scope>test</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.slf4j</groupId>
+                <artifactId>slf4j-jdk14</artifactId>
+                <version>${cxf.slf4j.version}</version>
+                <scope>test</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.geronimo.config</groupId>
+                <artifactId>geronimo-config-impl</artifactId>
+                <version>${cxf.geronimo.config.version}</version>
+                <scope>test</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.cxf</groupId>
+                <artifactId>cxf-rt-rs-mp-client</artifactId>
+                <version>${project.version}</version>
+                <scope>test</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.johnzon</groupId>
+                <artifactId>johnzon-jsonb</artifactId>
+                <version>${cxf.johnzon.tck.version}</version>
+                <scope>test</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.eclipse.microprofile.rest.client</groupId>
+                <artifactId>microprofile-rest-client-tck</artifactId>
+                <version>${cxf.microprofile.rest.client.version}</version>
+                <scope>test</scope>
+            </dependency>
+            <dependency>
+                <groupId>com.github.tomakehurst</groupId>
+                <artifactId>wiremock</artifactId>
+                <version>${cxf.wiremock.version}</version>
+                <scope>test</scope>
+            </dependency>
+        </dependencies>
+</project>
diff --git a/systests/microprofile/client/async/src/test/java/org/apache/cxf/systest/microprofile/rest/client/AsyncMethodTest.java b/systests/microprofile/client/async/src/test/java/org/apache/cxf/systest/microprofile/rest/client/AsyncMethodTest.java
new file mode 100644
index 00000000000..19efc4661ed
--- /dev/null
+++ b/systests/microprofile/client/async/src/test/java/org/apache/cxf/systest/microprofile/rest/client/AsyncMethodTest.java
@@ -0,0 +1,153 @@
+/**
+ * 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.systest.microprofile.rest.client;
+
+import java.net.URI;
+import java.util.List;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+
+import javax.ws.rs.core.Response;
+
+import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
+import com.github.tomakehurst.wiremock.junit.WireMockRule;
+
+import org.apache.cxf.systest.microprofile.rest.client.mock.AsyncClientWithCompletionStage;
+import org.apache.cxf.systest.microprofile.rest.client.mock.AsyncInvocationInterceptorFactoryTestImpl;
+import org.apache.cxf.systest.microprofile.rest.client.mock.AsyncInvocationInterceptorFactoryTestImpl2;
+import org.apache.cxf.systest.microprofile.rest.client.mock.ThreadLocalClientFilter;
+import org.eclipse.microprofile.rest.client.RestClientBuilder;
+import org.eclipse.microprofile.rest.client.tck.providers.TestClientRequestFilter;
+import org.eclipse.microprofile.rest.client.tck.providers.TestClientResponseFilter;
+import org.eclipse.microprofile.rest.client.tck.providers.TestMessageBodyReader;
+import org.eclipse.microprofile.rest.client.tck.providers.TestMessageBodyWriter;
+import org.eclipse.microprofile.rest.client.tck.providers.TestParamConverterProvider;
+import org.eclipse.microprofile.rest.client.tck.providers.TestReaderInterceptor;
+import org.eclipse.microprofile.rest.client.tck.providers.TestWriterInterceptor;
+
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+
+//CHECKSTYLE:OFF
+import static com.github.tomakehurst.wiremock.client.WireMock.*;
+//CHECKSTYLE:ON
+
+public class AsyncMethodTest extends Assert {
+
+    @Rule
+    public WireMockRule wireMockRule = new WireMockRule(WireMockConfiguration.options().dynamicPort());
+
+
+    @Test
+    public void testInvokesPostOperationWithRegisteredProvidersAsyncCompletionStage() throws Exception {
+        wireMockRule.stubFor(put(urlEqualTo("/echo/test"))
+                                .willReturn(aResponse()
+                                .withBody("this is the replaced writer input body will be removed")));
+        String inputBody = "input body will be removed";
+        String expectedResponseBody = TestMessageBodyReader.REPLACED_BODY;
+
+        AsyncClientWithCompletionStage api = RestClientBuilder.newBuilder()
+                .register(TestClientRequestFilter.class)
+                .register(TestClientResponseFilter.class)
+                .register(TestMessageBodyReader.class, 3)
+                .register(TestMessageBodyWriter.class)
+                .register(TestParamConverterProvider.class)
+                .register(TestReaderInterceptor.class)
+                .register(TestWriterInterceptor.class)
+                .baseUri(getBaseUri())
+                .build(AsyncClientWithCompletionStage.class);
+
+        CompletionStage<Response> cs = api.put(inputBody);
+
+        // should need <1 second, but 20s timeout in case something goes wrong
+        Response response = cs.toCompletableFuture().get(20, TimeUnit.SECONDS);
+        String actualResponseBody = response.readEntity(String.class);
+
+        assertEquals(expectedResponseBody, actualResponseBody);
+
+        assertEquals(TestClientResponseFilter.getAndResetValue(), 1);
+        assertEquals(TestClientRequestFilter.getAndResetValue(), 1);
+        assertEquals(TestReaderInterceptor.getAndResetValue(), 1);
+    }
+
+    @Test
+    public void testInvokesPostOperationWithRegisteredProvidersAsyncCompletionStageWithExecutor() throws Exception {
+        final String inputBody = "input body will be ignored";
+        wireMockRule.stubFor(put(urlEqualTo("/echo/test"))
+                                .willReturn(aResponse()
+                                .withBody(inputBody)));
+        AsyncInvocationInterceptorFactoryTestImpl.INBOUND.remove();
+        AsyncInvocationInterceptorFactoryTestImpl.OUTBOUND.remove();
+        try {
+            final String asyncThreadName = "CXF-MPRestClientThread-2";
+
+            AsyncClientWithCompletionStage api = RestClientBuilder.newBuilder()
+                .register(AsyncInvocationInterceptorFactoryTestImpl.class)
+                .register(AsyncInvocationInterceptorFactoryTestImpl2.class)
+                .register(ThreadLocalClientFilter.class)
+                .baseUri(getBaseUri())
+                .executorService(Executors.newSingleThreadExecutor(new ThreadFactory() {
+
+                    @Override
+                    public Thread newThread(Runnable r) {
+                        return new Thread(r, asyncThreadName);
+                    }
+                }))
+                .build(AsyncClientWithCompletionStage.class);
+
+            CompletionStage<Response> cs = api.put(inputBody);
+            List<String> outboundList = AsyncInvocationInterceptorFactoryTestImpl.OUTBOUND.get();
+            assertEquals(4, outboundList.size());
+
+            // ensure filters and asyncInvocationInterceptors are executed in the correct order and the correct thread
+            // outbound:
+            assertEquals(ThreadLocalClientFilter.class.getSimpleName(), outboundList.get(0));
+            assertEquals(AsyncInvocationInterceptorFactoryTestImpl.class.getSimpleName(), outboundList.get(1));
+            assertEquals(AsyncInvocationInterceptorFactoryTestImpl2.class.getSimpleName(), outboundList.get(2));
+            assertEquals(Thread.currentThread().getName(), outboundList.get(3));
+
+            // inbound:
+            // should need <1 second, but 20s timeout in case something goes wrong
+            Response response = cs.toCompletableFuture().get(20, TimeUnit.SECONDS);
+
+            List<String> responseList = response.getStringHeaders().get("CXFTestResponse");
+            assertEquals(4, responseList.size());
+
+            assertEquals(asyncThreadName, responseList.get(0));
+            assertEquals(AsyncInvocationInterceptorFactoryTestImpl2.class.getSimpleName(), responseList.get(1));
+            assertEquals(AsyncInvocationInterceptorFactoryTestImpl.class.getSimpleName(), responseList.get(2));
+            assertEquals(ThreadLocalClientFilter.class.getSimpleName(), responseList.get(3));
+        } finally {
+            AsyncInvocationInterceptorFactoryTestImpl.INBOUND.remove();
+            AsyncInvocationInterceptorFactoryTestImpl.OUTBOUND.remove();
+        }
+    }
+
+    private URI getBaseUri() {
+        return URI.create("http://localhost:" + wireMockRule.port() + "/echo");
+    }
+
+    private void fail(Response r, String failureMessage) {
+        System.out.println(r.getStatus());
+        fail(failureMessage);
+    }
+}
diff --git a/systests/microprofile/client/async/src/test/java/org/apache/cxf/systest/microprofile/rest/client/mock/AsyncClientWithCompletionStage.java b/systests/microprofile/client/async/src/test/java/org/apache/cxf/systest/microprofile/rest/client/mock/AsyncClientWithCompletionStage.java
new file mode 100644
index 00000000000..d30a2d055c3
--- /dev/null
+++ b/systests/microprofile/client/async/src/test/java/org/apache/cxf/systest/microprofile/rest/client/mock/AsyncClientWithCompletionStage.java
@@ -0,0 +1,33 @@
+/**
+ * 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.systest.microprofile.rest.client.mock;
+
+import java.util.concurrent.CompletionStage;
+
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Response;
+
+public interface AsyncClientWithCompletionStage {
+
+    @PUT
+    @Path("/test")
+    CompletionStage<Response> put(String text);
+}
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
new file mode 100644
index 00000000000..242b161972c
--- /dev/null
+++ b/systests/microprofile/client/async/src/test/java/org/apache/cxf/systest/microprofile/rest/client/mock/AsyncInvocationInterceptorFactoryTestImpl.java
@@ -0,0 +1,62 @@
+/**
+ * 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.systest.microprofile.rest.client.mock;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Priority;
+
+import org.eclipse.microprofile.rest.client.ext.AsyncInvocationInterceptor;
+import org.eclipse.microprofile.rest.client.ext.AsyncInvocationInterceptorFactory;
+
+@Priority(3500)
+public class AsyncInvocationInterceptorFactoryTestImpl implements AsyncInvocationInterceptorFactory {
+
+    //CHECKSTYLE:OFF
+    public static ThreadLocal<List<String>> OUTBOUND = ThreadLocal.withInitial(() -> {return new ArrayList<>();});
+    public static ThreadLocal<List<String>> INBOUND = ThreadLocal.withInitial(() -> {return new ArrayList<>();});
+    //CHECKSTYLE:ON
+
+    static class AsyncInvocationInterceptorTestImpl implements AsyncInvocationInterceptor {
+
+        /** {@inheritDoc}*/
+        @Override
+        public void prepareContext() {
+            List<String> list = OUTBOUND.get();
+            list.add(AsyncInvocationInterceptorFactoryTestImpl.class.getSimpleName());
+        }
+
+        /** {@inheritDoc}*/
+        @Override
+        public void applyContext() {
+            List<String> list = INBOUND.get();
+            list.add(Thread.currentThread().getName());
+            list.add(AsyncInvocationInterceptorFactoryTestImpl.class.getSimpleName());
+        }
+    }
+
+    /** {@inheritDoc}*/
+    @Override
+    public AsyncInvocationInterceptor newInterceptor() {
+        return new AsyncInvocationInterceptorTestImpl();
+    }
+
+}
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
new file mode 100644
index 00000000000..e4ceb6e6704
--- /dev/null
+++ b/systests/microprofile/client/async/src/test/java/org/apache/cxf/systest/microprofile/rest/client/mock/AsyncInvocationInterceptorFactoryTestImpl2.java
@@ -0,0 +1,57 @@
+/**
+ * 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.systest.microprofile.rest.client.mock;
+
+import java.util.List;
+
+import javax.annotation.Priority;
+
+import org.eclipse.microprofile.rest.client.ext.AsyncInvocationInterceptor;
+import org.eclipse.microprofile.rest.client.ext.AsyncInvocationInterceptorFactory;
+
+@Priority(3700)
+public class AsyncInvocationInterceptorFactoryTestImpl2 implements AsyncInvocationInterceptorFactory {
+
+    static class AsyncInvocationInterceptorTestImpl2 implements AsyncInvocationInterceptor {
+
+        /** {@inheritDoc}*/
+        @Override
+        public void prepareContext() {
+            List<String> list = AsyncInvocationInterceptorFactoryTestImpl.OUTBOUND.get();
+            list.add(AsyncInvocationInterceptorFactoryTestImpl2.class.getSimpleName());
+            list.add(Thread.currentThread().getName());
+        }
+
+        /** {@inheritDoc}*/
+        @Override
+        public void applyContext() {
+            List<String> list = AsyncInvocationInterceptorFactoryTestImpl.INBOUND.get();
+            list.add(Thread.currentThread().getName());
+            list.add(AsyncInvocationInterceptorFactoryTestImpl2.class.getSimpleName());
+        }
+    }
+
+    /** {@inheritDoc}*/
+    @Override
+    public AsyncInvocationInterceptor newInterceptor() {
+        return new AsyncInvocationInterceptorTestImpl2();
+    }
+
+}
diff --git a/systests/microprofile/client/async/src/test/java/org/apache/cxf/systest/microprofile/rest/client/mock/ThreadLocalClientFilter.java b/systests/microprofile/client/async/src/test/java/org/apache/cxf/systest/microprofile/rest/client/mock/ThreadLocalClientFilter.java
new file mode 100644
index 00000000000..bd83d640f40
--- /dev/null
+++ b/systests/microprofile/client/async/src/test/java/org/apache/cxf/systest/microprofile/rest/client/mock/ThreadLocalClientFilter.java
@@ -0,0 +1,48 @@
+/**
+ * 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.systest.microprofile.rest.client.mock;
+
+import java.io.IOException;
+import java.util.List;
+
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientRequestFilter;
+import javax.ws.rs.client.ClientResponseContext;
+import javax.ws.rs.client.ClientResponseFilter;
+
+public class ThreadLocalClientFilter implements ClientRequestFilter, ClientResponseFilter {
+
+    /** {@inheritDoc}*/
+    @Override
+    public void filter(ClientRequestContext requestContext) throws IOException {
+        List<String> list = AsyncInvocationInterceptorFactoryTestImpl.OUTBOUND.get();
+        list.add(ThreadLocalClientFilter.class.getSimpleName());
+    }
+
+    /** {@inheritDoc}*/
+    @Override
+    public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext)
+        throws IOException {
+        List<String> list = AsyncInvocationInterceptorFactoryTestImpl.INBOUND.get();
+        list.add(ThreadLocalClientFilter.class.getSimpleName());
+        responseContext.getHeaders().put("CXFTestResponse", list);
+    }
+
+}
diff --git a/systests/microprofile/client/weld/pom.xml b/systests/microprofile/client/weld/pom.xml
index 5fa726fe681..57e85204944 100644
--- a/systests/microprofile/client/weld/pom.xml
+++ b/systests/microprofile/client/weld/pom.xml
@@ -114,6 +114,9 @@
                     <dependenciesToScan>
                         <dependency>org.eclipse.microprofile.rest.client:microprofile-rest-client-tck</dependency>
                     </dependenciesToScan>
+                    <suiteXmlFiles>
+                        <suiteXmlFile>testng.xml</suiteXmlFile>
+                    </suiteXmlFiles>
                 </configuration>
             </plugin>
         </plugins>
diff --git a/systests/microprofile/client/weld/testng.xml b/systests/microprofile/client/weld/testng.xml
new file mode 100644
index 00000000000..cae845056c4
--- /dev/null
+++ b/systests/microprofile/client/weld/testng.xml
@@ -0,0 +1,27 @@
+<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
+ 
+<suite name="MPRestClientTCK1.1">
+    <test name="All tests except Executor Service Test">
+        <packages>
+            <package name="org.eclipse.microprofile.rest.client.tck" />
+            <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 df68370f4b6..da9e88ced9f 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.0</cxf.microprofile.rest.client.version>
+        <cxf.microprofile.rest.client.version>1.1</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>
@@ -46,6 +46,12 @@
     </properties>
     <dependencyManagement>
         <dependencies>
+            <dependency>
+                <groupId>org.testng</groupId>
+                <artifactId>testng</artifactId>
+                <version>6.14.2</version>
+                <scope>test</scope>
+            </dependency>
             <dependency>
                 <groupId>org.jboss.arquillian.testng</groupId>
                 <artifactId>arquillian-testng-container</artifactId>
@@ -126,6 +132,7 @@
         </dependencies>
     </dependencyManagement>
     <modules>
+        <module>client/async</module>
         <module>client/weld</module>
     </modules>
 </project>


 

----------------------------------------------------------------
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:
users@infra.apache.org


> Implement MicroProfile Rest Client 1.1
> --------------------------------------
>
>                 Key: CXF-7673
>                 URL: https://issues.apache.org/jira/browse/CXF-7673
>             Project: CXF
>          Issue Type: New Feature
>            Reporter: John D. Ament
>            Assignee: Andy McCright
>            Priority: Major
>
> Implement MP Rest Client 1.1
>  * Async support
>  * baseUri vs baseUrl
>  * {{ProviderRegistrar}} support
>  
> FYI [~andymc]



--
This message was sent by Atlassian JIRA
(v7.6.3#76005)