You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by am...@apache.org on 2019/01/17 00:00:41 UTC

[cxf] branch master updated: [CXF-7926] Initial MP Rest Client 1.2 impl - part 2

This is an automated email from the ASF dual-hosted git repository.

amccright pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cxf.git


The following commit(s) were added to refs/heads/master by this push:
     new f29e60b  [CXF-7926] Initial MP Rest Client 1.2 impl - part 2
     new 0669933  Merge pull request #499 from andymc12/mpRest12initial-part2
f29e60b is described below

commit f29e60b167440662b1406693cda4c44c06df78e7
Author: Andy McCright <j....@gmail.com>
AuthorDate: Tue Dec 11 22:16:03 2018 -0600

    [CXF-7926] Initial MP Rest Client 1.2 impl - part 2
    
    * This includes generation and propagation of HTTPHeaders via
    @ClientHeaderParam annotations and ClientHeadersFactory.
    * Ensuring that timeout props only have effect when using CDI.
    * Related unit and system tests.
---
 rt/rs/microprofile-client/pom.xml                  |   2 +-
 .../client/CxfTypeSafeClientBuilder.java           |  25 ---
 .../cxf/microprofile/client/Messages.properties    |   9 +-
 .../client/MicroProfileClientFactoryBean.java      |   4 +-
 .../apache/cxf/microprofile/client/Validator.java  |  92 ++++++++++
 .../microprofile/client/cdi/RestClientBean.java    |  24 +++
 .../client/proxy/MicroProfileClientProxyImpl.java  | 198 ++++++++++++++++++++-
 .../cxf/microprofile/client/ClientHeadersTest.java |  92 ++++++++++
 .../cxf/microprofile/client/ValidatorTest.java     |  90 ++++++++++
 .../mock/HeaderCaptureClientRequestFilter.java     |  46 +++++
 .../microprofile/client/mock/HeaderGenerator.java  |  33 ++++
 .../client/mock/HeadersFactoryClient.java          |  40 +++++
 .../client/mock/HeadersOnInterfaceClient.java      |  40 +++++
 .../client/mock/HeadersOnMethodClient.java         |  41 +++++
 .../client/mock/MockConfigProviderResolver.java    |   1 -
 .../client/mock/MyClientHeadersFactory.java        |  61 +++++++
 systests/microprofile/client/async/pom.xml         |   4 +-
 systests/microprofile/client/jaxrs/pom.xml         | 190 ++++++++++++++++++++
 .../rest/client/JaxrsHeaderPropagationTest.java    | 118 ++++++++++++
 .../microprofile/rest/client/JaxrsResource.java    |  49 +++++
 .../rest/client}/MockConfigProviderResolver.java   |   3 +-
 .../microprofile/rest/client/RestClient.java       |  32 ++++
 .../client/ReturnAllOutboundHeadersFilter.java     |  40 +++++
 systests/microprofile/client/weld/testng.xml       |   1 +
 systests/microprofile/pom.xml                      |   3 +-
 25 files changed, 1199 insertions(+), 39 deletions(-)

diff --git a/rt/rs/microprofile-client/pom.xml b/rt/rs/microprofile-client/pom.xml
index b122d56..8235b21 100644
--- a/rt/rs/microprofile-client/pom.xml
+++ b/rt/rs/microprofile-client/pom.xml
@@ -42,7 +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>
+        <mp.rest.client.version>1.2-RC1</mp.rest.client.version>
     </properties>
     <dependencies>
         <dependency>
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 5b1ff3d..d1177af 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
@@ -29,16 +29,12 @@ 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;
@@ -47,9 +43,6 @@ import static org.apache.cxf.jaxrs.client.ClientProperties.HTTP_CONNECTION_TIMEO
 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<>();
 
@@ -136,24 +129,6 @@ public class CxfTypeSafeClientBuilder implements RestClientBuilder, Configurable
             }
         }
 
-        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,
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 1af5bb6..dfe9ac0 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
@@ -29,4 +29,11 @@ ASYNC_INTERCEPTOR_EXCEPTION_PREPARE_CONTEXT=The AsyncInvocationInterceptor, {0}
 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
+INVALID_TIMEOUT_PROPERTY=The timeout property, {0} is specified as {1}, and is invalid. It must be a non-negative integer.
+
+CLIENT_HEADER_NO_NAME=A @ClientHeaderParam annotation specified in the {0} interface is invalid because it has a null or empty name.
+CLIENT_HEADER_NO_VALUE=A @ClientHeaderParam annotation specified in the {0} interface is invalid because it does not define a valid value attribute.
+CLIENT_HEADER_INVALID_COMPUTE_METHOD=A @ClientHeaderParam annotation specified in the {0} interface is invalid because it's value attribute specifies a method, {1}, that does not exist, is not accessible, or contains an incorrect signature. The signature must return a String or String[] and must contain either no arguments or contain exactly one String argument.
+CLIENT_HEADER_MULTI_METHOD=A @ClientHeaderParam annotation specified in the {0} interface is invalid because it's value attribute specifies multiple values and at least one of those values is a compute method. If the value attribute specifies a compute method, that must be the only value.
+CLIENT_HEADER_COMPUTE_CLASS_NOT_FOUND=A @ClientHeaderParam annotation specified in the {0} interface is invalid because it specifies a compute method on a class that cannot be found.
+CLIENT_HEADER_MULTIPLE_SAME_HEADER_NAMES=A @ClientHeaderParam annotation specified in the {0} interface is invalid because it specifies a header name that is already specified on the same target. A header name may only be specified in one @ClientHeaderParam per target.
\ 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 6d59031..6007e3b 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
@@ -115,10 +115,10 @@ public class MicroProfileClientFactoryBean extends JAXRSClientFactoryBean {
     private Set<Object> processProviders() {
         Set<Object> providers = new LinkedHashSet<>();
         for (Object provider : configuration.getInstances()) {
-            Class<?> providerCls = ClassHelper.getRealClass(bus, provider);
+            Class<?> providerCls = ClassHelper.getRealClass(getBus(), provider);
             if (provider instanceof ClientRequestFilter || provider instanceof ClientResponseFilter) {
                 FilterProviderInfo<Object> filter = new FilterProviderInfo<>(providerCls, providerCls,
-                        provider, bus, configuration.getContracts(providerCls));
+                        provider, getBus(), configuration.getContracts(providerCls));
                 providers.add(filter);
             } else {
                 providers.add(provider);
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 3155164..1ae4807 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
@@ -20,8 +20,10 @@ package org.apache.cxf.microprofile.client;
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
 import java.lang.reflect.Parameter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -29,17 +31,24 @@ import java.util.List;
 import java.util.Map;
 import java.util.ResourceBundle;
 import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 import javax.ws.rs.HttpMethod;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 
+import org.apache.cxf.common.classloader.ClassLoaderUtils;
 import org.apache.cxf.common.i18n.BundleUtils;
 import org.apache.cxf.common.i18n.Message;
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.util.StringUtils;
 import org.apache.cxf.jaxrs.model.URITemplate;
 import org.eclipse.microprofile.rest.client.RestClientDefinitionException;
+import org.eclipse.microprofile.rest.client.annotation.ClientHeaderParam;
 
 final class Validator {
+    private static final Logger LOG = LogUtils.getL7dLogger(Validator.class);
     private static final ResourceBundle BUNDLE = BundleUtils.getBundle(Validator.class);
 
     private Validator() {
@@ -53,6 +62,7 @@ final class Validator {
         Method[] methods = userType.getMethods();
         checkMethodsForMultipleHTTPMethodAnnotations(methods);
         checkMethodsForInvalidURITemplates(userType, methods);
+        checkForInvalidClientHeaderParams(userType, methods);
     }
 
     private static void checkMethodsForMultipleHTTPMethodAnnotations(Method[] clientMethods)
@@ -129,6 +139,88 @@ final class Validator {
         }
     }
 
+    private static void checkForInvalidClientHeaderParams(Class<?> userType, Method[] methods) {
+        ClientHeaderParam[] interfaceAnnotations = userType.getAnnotationsByType(ClientHeaderParam.class);
+        checkClientHeaderParamAnnotation(interfaceAnnotations, userType, methods);
+        for (Method method : methods) {
+            ClientHeaderParam[] methodAnnotations = method.getAnnotationsByType(ClientHeaderParam.class);
+            checkClientHeaderParamAnnotation(methodAnnotations, userType, methods);
+        }
+    }
+
+    private static void checkClientHeaderParamAnnotation(ClientHeaderParam[] annos, Class<?> userType,
+                                                         Method[] methods) {
+        Set<String> headerNames = new HashSet<>();
+        for (ClientHeaderParam anno : annos) {
+            String name = anno.name();
+            if (headerNames.contains(name)) {
+                throwException("CLIENT_HEADER_MULTIPLE_SAME_HEADER_NAMES", userType.getName());
+            }
+            headerNames.add(name);
+
+            if (name == null || "".equals(name)) {
+                throwException("CLIENT_HEADER_NO_NAME", userType.getName());
+            }
+
+            String[] values = anno.value();
+
+            for (String value : values) {
+                if (StringUtils.isEmpty(value)) {
+                    throwException("CLIENT_HEADER_NO_VALUE", userType.getName());
+                }
+
+                if (value.startsWith("{") && value.endsWith("}")) {
+                    if (values.length > 1) {
+                        throwException("CLIENT_HEADER_MULTI_METHOD", userType.getName());
+                    }
+                    String computeValue = value.substring(1, value.length() - 1);
+                    boolean usingOtherClass = false;
+                    if (computeValue.contains(".")) {
+                        usingOtherClass = true;
+                        String className = computeValue.substring(0, computeValue.lastIndexOf('.'));
+                        computeValue = computeValue.substring(computeValue.lastIndexOf('.') + 1);
+                        try {
+                            Class<?> computeClass = ClassLoaderUtils.loadClass(className, userType);
+                            methods = Arrays.stream(computeClass.getDeclaredMethods())
+                                                                .filter(m -> {
+                                                                    int i = m.getModifiers();
+                                                                    return Modifier.isPublic(i) 
+                                                                        && Modifier.isStatic(i);
+                                                                })
+                                                                .toArray(Method[]::new);
+                        } catch (ClassNotFoundException ex) {
+                            if (LOG.isLoggable(Level.FINEST)) {
+                                LOG.log(Level.FINEST, "Unable to load specified compute method class", ex);
+                            }
+                            throwException("CLIENT_HEADER_COMPUTE_CLASS_NOT_FOUND", userType.getName(), ex);
+                        }
+                       
+                    }
+                    boolean foundMatchingMethod = false;
+                    for (Method method : methods) {
+                        Class<?> returnType = method.getReturnType();
+                        if ((usingOtherClass || method.isDefault())
+                            && (String.class.equals(returnType) || String[].class.equals(returnType))
+                            && computeValue.equals(method.getName())) {
+
+                            Class<?>[] args = method.getParameterTypes();
+                            if (args.length == 0 || (args.length == 1 && String.class.equals(args[0]))) {
+                                foundMatchingMethod = true;
+                                break;
+                            }
+
+                        }
+                    }
+                    if (!foundMatchingMethod) {
+                        throwException("CLIENT_HEADER_INVALID_COMPUTE_METHOD",
+                                       userType.getName(),
+                                       computeValue);
+                    }
+                }
+            }
+        }
+    }
+
     private static void throwException(String msgKey, Object...msgParams) throws RestClientDefinitionException {
         Message msg = new Message(msgKey, BUNDLE, msgParams);
         throw new RestClientDefinitionException(msg.toString());
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 89d2b7d..fa6a84a 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
@@ -29,6 +29,7 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -55,6 +56,8 @@ public class RestClientBean implements Bean<Object>, PassivationCapable {
     public static final String REST_URI_FORMAT = "%s/mp-rest/uri";
     public static final String REST_SCOPE_FORMAT = "%s/mp-rest/scope";
     public static final String REST_PROVIDERS_FORMAT = "%s/mp-rest/providers";
+    public static final String REST_CONN_TIMEOUT_FORMAT = "%s/mp-rest/connectTimeout";
+    public static final String REST_READ_TIMEOUT_FORMAT = "%s/mp-rest/readTimeout";
     public static final String REST_PROVIDERS_PRIORITY_FORMAT = "%s/mp-rest/providers/%s/priority";
     private static final Logger LOG = LogUtils.getL7dLogger(RestClientBean.class);
     private static final Default DEFAULT_LITERAL = new DefaultLiteral();
@@ -98,6 +101,7 @@ public class RestClientBean implements Bean<Object>, PassivationCapable {
             builder = (CxfTypeSafeClientBuilder) builder.register(providerClass, 
                                        providerPriorities.getOrDefault(providerClass, Priorities.USER));
         }
+        setTimeouts(builder);
         return builder.build(clientInterface);
     }
 
@@ -229,4 +233,24 @@ public class RestClientBean implements Bean<Object>, PassivationCapable {
         private static final long serialVersionUID = 1L;
 
     }
+
+    private void setTimeouts(CxfTypeSafeClientBuilder builder) {
+        final String interfaceName = clientInterface.getName();
+
+        ConfigFacade.getOptionalLong(String.format(REST_CONN_TIMEOUT_FORMAT, interfaceName)).ifPresent(
+            timeoutValue -> {
+                builder.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 -> {
+                builder.readTimeout(timeoutValue, TimeUnit.MILLISECONDS);
+                if (LOG.isLoggable(Level.FINEST)) {
+                    LOG.finest("readTimeout set by MP Config: " + timeoutValue);
+                }
+            });
+    }
 }
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 ea7db94..14ab55a 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
@@ -18,9 +18,11 @@
  */
 package org.apache.cxf.microprofile.client.proxy;
 
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Type;
 import java.net.URI;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -32,10 +34,13 @@ import java.util.logging.Logger;
 
 import javax.ws.rs.client.InvocationCallback;
 import javax.ws.rs.core.Configuration;
+import javax.ws.rs.core.MultivaluedHashMap;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
 
+import org.apache.cxf.common.classloader.ClassLoaderUtils;
 import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.helpers.CastUtils;
 import org.apache.cxf.interceptor.Interceptor;
 import org.apache.cxf.jaxrs.client.ClientProxyImpl;
 import org.apache.cxf.jaxrs.client.ClientState;
@@ -43,12 +48,18 @@ 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.model.Parameter;
+import org.apache.cxf.jaxrs.model.ParameterType;
+import org.apache.cxf.jaxrs.utils.HttpUtils;
 import org.apache.cxf.jaxrs.utils.InjectionUtils;
 import org.apache.cxf.message.Exchange;
 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.annotation.ClientHeaderParam;
+import org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders;
+import org.eclipse.microprofile.rest.client.ext.ClientHeadersFactory;
 import org.eclipse.microprofile.rest.client.ext.ResponseExceptionMapper;
 
 import static org.apache.cxf.jaxrs.client.ClientProperties.HTTP_CONNECTION_TIMEOUT_PROP;
@@ -65,8 +76,25 @@ public class MicroProfileClientProxyImpl extends ClientProxyImpl {
         public void completed(Object o) { }
     };
 
-    private final CDIInterceptorWrapper interceptorWrapper;
+    private static final Method JAXRS_UTILS_GET_CURRENT_MESSAGE_METHOD;
     
+    static {
+        Method m;
+        try {
+            Class<?> jaxrsUtilsClass = Class.forName("org.apache.cxf.jaxrs.utils.JAXRSUtils");
+            m = jaxrsUtilsClass.getDeclaredMethod("getCurrentMessage");
+        } catch (Throwable t) {
+            // expected in non-JAX-RS server environments
+            if (LOG.isLoggable(Level.FINEST)) {
+                LOG.log(Level.FINEST, "Caught exception getting JAXRUtils class", t);
+            }
+            m = null;
+        }
+        JAXRS_UTILS_GET_CURRENT_MESSAGE_METHOD = m;
+    }
+    private final CDIInterceptorWrapper interceptorWrapper;
+    private Object objectInstance;
+
     //CHECKSTYLE:OFF
     public MicroProfileClientProxyImpl(URI baseURI, ClassLoader loader, ClassResourceInfo cri,
                                        boolean isRoot, boolean inheritHeaders, ExecutorService executorService,
@@ -89,8 +117,6 @@ public class MicroProfileClientProxyImpl extends ClientProxyImpl {
     }
     //CHECKSTYLE:ON
 
-
-
     @SuppressWarnings("unchecked")
     @Override
     protected InvocationCallback<Object> checkAsyncCallback(OperationResourceInfo ori,
@@ -207,7 +233,9 @@ public class MicroProfileClientProxyImpl extends ClientProxyImpl {
                 cfg.getHttpConduit().getClient().setReceiveTimeout(readTimeout);
             }
         } catch (Exception ex) {
-            ex.printStackTrace();
+            if (LOG.isLoggable(Level.FINEST)) {
+                LOG.log(Level.FINEST, "Caught exception setting timeouts", ex);
+            }
         }
     }
 
@@ -237,8 +265,144 @@ public class MicroProfileClientProxyImpl extends ClientProxyImpl {
         return l;
     }
 
+    private String invokeBestFitComputeMethod(Class<?> clientIntf, ClientHeaderParam anno) throws Throwable {
+        String methodName = anno.value()[0];
+        methodName = methodName.substring(1, methodName.length() - 1);
+        Class<?> computeClass = clientIntf;
+        if (methodName.contains(".")) {
+            String className = methodName.substring(0, methodName.lastIndexOf('.'));
+            methodName = methodName.substring(methodName.lastIndexOf('.') + 1);
+            try {
+                computeClass = ClassLoaderUtils.loadClass(className, clientIntf);
+            } catch (ClassNotFoundException ex) {
+                LOG.warning("Cannot find specified computeValue class, " + className);
+                return null;
+            }
+        }
+        Method m = null;
+        boolean includeHeaderName = false;
+        try {
+            m = computeClass.getMethod(methodName, String.class);
+            includeHeaderName = true;
+        } catch (NoSuchMethodException expected) {
+            try {
+                m = computeClass.getMethod(methodName);
+            } catch (NoSuchMethodException expected2) { }
+        }
+
+        String value;
+        if (m == null) {
+            value = null;
+            LOG.warning("Cannot find specified computeValue method, "
+                + methodName + ", on client interface, " + clientIntf.getName());
+        } else {
+            try {
+                Object valueFromComputeMethod;
+                if (includeHeaderName) {
+                    valueFromComputeMethod = m.invoke(computeClass == clientIntf ? objectInstance : null,
+                                                      anno.name());
+                } else {
+                    valueFromComputeMethod = m.invoke(computeClass == clientIntf ? objectInstance : null);
+                }
+                if (valueFromComputeMethod instanceof String[]) {
+                    value = HttpUtils.getHeaderString(Arrays.asList((String[]) valueFromComputeMethod));
+                } else {
+                    value = (String) valueFromComputeMethod;
+                }
+            } catch (Throwable t) {
+                if (LOG.isLoggable(Level.FINEST)) {
+                    LOG.log(Level.FINEST, "Caught exception invoking compute method", t);
+                }
+                if (t instanceof InvocationTargetException) {
+                    t = t.getCause();
+                }
+                throw t;
+            }
+        }
+        return value;
+    }
+
+    private Parameter createClientHeaderParameter(ClientHeaderParam anno, Class<?> clientIntf) {
+        Parameter p = new Parameter(ParameterType.HEADER, anno.name());
+        String[] values = anno.value();
+        String headerValue;
+        if (values[0] != null && values[0].length() > 2 && values[0].startsWith("{") && values[0].endsWith("}")) {
+            try {
+                headerValue = invokeBestFitComputeMethod(clientIntf, anno);
+            } catch (Throwable t) {
+                if (anno.required()) {
+                    throwException(t);
+                }
+                return null;
+            }
+        } else {
+            headerValue = HttpUtils.getHeaderString(Arrays.asList(values));
+        }
+
+        p.setDefaultValue(headerValue);
+        return p;
+    }
+
+    @Override
+    protected void handleHeaders(Method m,
+                                 Object[] params,
+                                 MultivaluedMap<String, String> headers,
+                                 List<Parameter> beanParams,
+                                 MultivaluedMap<ParameterType, Parameter> map) {
+        super.handleHeaders(m, params, headers, beanParams, map);
+
+        try {
+            Class<?> declaringClass = m.getDeclaringClass();
+            ClientHeaderParam[] clientHeaderAnnosOnInterface = declaringClass
+                .getAnnotationsByType(ClientHeaderParam.class);
+            ClientHeaderParam[] clientHeaderAnnosOnMethod = m.getAnnotationsByType(ClientHeaderParam.class);
+            RegisterClientHeaders headersFactoryAnno = declaringClass.getAnnotation(RegisterClientHeaders.class);
+            if (clientHeaderAnnosOnInterface.length < 1 && clientHeaderAnnosOnMethod.length < 1
+                && headersFactoryAnno == null) {
+                return;
+            }
+            
+            for (ClientHeaderParam methodAnno : clientHeaderAnnosOnMethod) {
+                String headerName = methodAnno.name();
+                if (!headers.containsKey(headerName)) {
+                    Parameter p = createClientHeaderParameter(methodAnno, declaringClass);
+                    if (p != null) {
+                        headers.putSingle(p.getName(), p.getDefaultValue());
+                    }
+                }
+            }
+            for (ClientHeaderParam intfAnno : clientHeaderAnnosOnInterface) {
+                String headerName = intfAnno.name();
+                if (!headers.containsKey(headerName)) {
+                    Parameter p = createClientHeaderParameter(intfAnno, declaringClass);
+                    if (p != null) {
+                        headers.putSingle(p.getName(), p.getDefaultValue());
+                    }
+                }
+            }
+
+            ClientHeadersFactory headersFactory = null;
+            
+            if (headersFactoryAnno != null) {
+                Class<?> headersFactoryClass = headersFactoryAnno.value();
+                headersFactory = (ClientHeadersFactory) headersFactoryClass.newInstance();
+                mergeHeaders(headersFactory, headers);
+            }
+        } catch (Throwable t) {
+            throwException(t);
+        }
+    }
+
+    private void mergeHeaders(ClientHeadersFactory factory,
+                              MultivaluedMap<String, String> existingHeaders) {
+
+        MultivaluedMap<String, String> jaxrsHeaders = getJaxrsHeaders();
+        MultivaluedMap<String, String> updatedHeaders = factory.update(jaxrsHeaders, existingHeaders);
+        existingHeaders.putAll(updatedHeaders);
+    }
     @Override
     public Object invoke(Object o, Method m, Object[] params) throws Throwable {
+        objectInstance = o;
         return interceptorWrapper.invoke(o, m, params, new Invoker(o, m, params, this));
     }
 
@@ -271,4 +435,30 @@ public class MicroProfileClientProxyImpl extends ClientProxyImpl {
             }
         }
     }
+    
+    private void throwException(Throwable t) {
+        if (t instanceof Error) {
+            throw (Error) t;
+        }
+        if (t instanceof RuntimeException) {
+            throw (RuntimeException) t;
+        }
+        throw new RuntimeException(t);
+    }
+
+    private static MultivaluedMap<String, String> getJaxrsHeaders() {
+        MultivaluedMap<String, String> headers = new MultivaluedHashMap<>();
+        try {
+            if (JAXRS_UTILS_GET_CURRENT_MESSAGE_METHOD != null) {
+                Message m = (Message) JAXRS_UTILS_GET_CURRENT_MESSAGE_METHOD.invoke(null);
+                headers.putAll(CastUtils.cast((Map<?, ?>)m.get(Message.PROTOCOL_HEADERS)));
+            }
+        } catch (Throwable t) {
+            // expected if not running in a JAX-RS server environment.
+            if (LOG.isLoggable(Level.FINEST)) {
+                LOG.log(Level.FINEST, "Caught exception getting JAX-RS incoming headers", t);
+            }
+        }
+        return headers;
+    }
 }
diff --git a/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/ClientHeadersTest.java b/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/ClientHeadersTest.java
new file mode 100644
index 0000000..3a3f3f2
--- /dev/null
+++ b/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/ClientHeadersTest.java
@@ -0,0 +1,92 @@
+/**
+ * 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.net.URI;
+
+import org.apache.cxf.microprofile.client.mock.HeaderCaptureClientRequestFilter;
+import org.apache.cxf.microprofile.client.mock.HeadersFactoryClient;
+import org.apache.cxf.microprofile.client.mock.HeadersOnInterfaceClient;
+import org.apache.cxf.microprofile.client.mock.HeadersOnMethodClient;
+import org.apache.cxf.microprofile.client.mock.MyClientHeadersFactory;
+import org.eclipse.microprofile.rest.client.RestClientBuilder;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.apache.cxf.microprofile.client.mock.HeaderCaptureClientRequestFilter.getOutboundHeaders;
+import static org.apache.cxf.microprofile.client.mock.MyClientHeadersFactory.getInitialHeaders;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+public class ClientHeadersTest {
+
+    @Before
+    public void clearHeaders() {
+        HeaderCaptureClientRequestFilter.setOutboundHeaders(null);
+        MyClientHeadersFactory.setInitialHeaders(null);
+    }
+
+    @Test
+    public void testClientHeaderParamsOnInterface() {
+        HeadersOnInterfaceClient client = RestClientBuilder.newBuilder()
+                                                           .baseUri(URI.create("http://localhost/notUsed"))
+                                                           .register(HeaderCaptureClientRequestFilter.class)
+                                                           .build(HeadersOnInterfaceClient.class);
+        assertEquals("SUCCESS", client.put("ignored"));
+        assertNotNull(getOutboundHeaders());
+        assertEquals("value1", getOutboundHeaders().getFirst("IntfHeader1"));
+        assertEquals("value2,value3", getOutboundHeaders().getFirst("IntfHeader2"));
+        assertEquals("HeadersOnInterfaceClientValueForIntfHeader3", getOutboundHeaders().getFirst("IntfHeader3"));
+        assertEquals("valueForIntfHeader4", getOutboundHeaders().getFirst("IntfHeader4"));
+    }
+
+    @Test
+    public void testClientHeaderParamsOnMethod() {
+        HeadersOnMethodClient client = RestClientBuilder.newBuilder()
+                                                        .baseUri(URI.create("http://localhost/notUsed"))
+                                                        .register(HeaderCaptureClientRequestFilter.class)
+                                                        .build(HeadersOnMethodClient.class);
+        assertEquals("SUCCESS", client.delete("ignored"));
+        assertNotNull(getOutboundHeaders());
+        assertEquals("valueA", getOutboundHeaders().getFirst("MethodHeader1"));
+        assertEquals("valueB,valueC", getOutboundHeaders().getFirst("MethodHeader2"));
+        assertEquals("HeadersOnMethodClientValueForMethodHeader3", getOutboundHeaders().getFirst("MethodHeader3"));
+        assertEquals("valueForMethodHeader4", getOutboundHeaders().getFirst("MethodHeader4"));
+    }
+
+    @Test
+    public void testClientHeadersFactory() {
+        HeadersFactoryClient client = RestClientBuilder.newBuilder()
+                                                       .baseUri(URI.create("http://localhost/notUsed"))
+                                                       .register(HeaderCaptureClientRequestFilter.class)
+                                                       .build(HeadersFactoryClient.class);
+        assertEquals("SUCCESS", client.get("headerParamValue1"));
+        assertNotNull(getInitialHeaders());
+        assertEquals("headerParamValue1", getInitialHeaders().getFirst("HeaderParam1"));
+        assertEquals("abc", getInitialHeaders().getFirst("IntfHeader1"));
+        assertEquals("def", getInitialHeaders().getFirst("MethodHeader1"));
+
+        assertNotNull(getOutboundHeaders());
+        assertEquals("1eulaVmaraPredaeh", getOutboundHeaders().getFirst("HeaderParam1"));
+        assertEquals("cba", getOutboundHeaders().getFirst("IntfHeader1"));
+        assertEquals("fed", getOutboundHeaders().getFirst("MethodHeader1"));
+        
+    }
+}
diff --git a/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/ValidatorTest.java b/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/ValidatorTest.java
index 9432399..f79437e 100644
--- a/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/ValidatorTest.java
+++ b/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/ValidatorTest.java
@@ -27,10 +27,12 @@ import javax.ws.rs.POST;
 import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
+import javax.ws.rs.client.ClientRequestContext;
 import javax.ws.rs.core.Response;
 
 import org.eclipse.microprofile.rest.client.RestClientBuilder;
 import org.eclipse.microprofile.rest.client.RestClientDefinitionException;
+import org.eclipse.microprofile.rest.client.annotation.ClientHeaderParam;
 
 import org.junit.Test;
 
@@ -94,6 +96,62 @@ public class ValidatorTest {
         Response get(@PathParam("any") String any);
     }
 
+    public interface ClientHeaderParamNoName {
+        @ClientHeaderParam(name = "", value = "something")
+        @GET
+        Response get();
+    }
+
+    public interface ClientHeaderParamNoComputeMethod {
+        @ClientHeaderParam(name = "SomeHeader", value = "{missingComputeMethod}")
+        @GET
+        Response get();
+    }
+
+    public interface ClientHeaderParamNonDefaultComputeMethod {
+        @ClientHeaderParam(name = "SomeHeader", value = "{nonDefaultComputeMethod}")
+        @GET
+        Response get();
+
+        String nonDefaultComputeMethod();
+    }
+
+    public interface ClientHeaderParamComputeMethodDoesNotExist {
+        @ClientHeaderParam(name = "SomeHeader", value = "{nonExistentComputeMethod}")
+        @GET
+        Response get();
+    }
+
+    public interface ClientHeaderParamInaccessibleComputeMethod {
+        @ClientHeaderParam(name = "SomeHeader", 
+            value = "{org.apache.cxf.microprofile.client.mock.HeaderGenerator.generateHeaderPrivate}")
+        @GET
+        Response get();
+    }
+
+    public interface ClientHeaderParamNoValidComputeMethodSignatures {
+        @ClientHeaderParam(name = "SomeHeader", value = "{computeMethod}")
+        @GET
+        Response get();
+
+        default String computeMethod(String x, String y) {
+            return "must only contain one String argument";
+        }
+        default String computeMethod(ClientRequestContext x, ClientRequestContext y) {
+            return "must only contain one ClientRequestContext argument";
+        }
+        default Integer computeMethod() {
+            return 5; // must return a String
+        }
+        default void computeMethod(String headerName) { } // must return a String
+        default String computeMethod(java.util.Date date) {
+            return "unexpected argument";
+        }
+        default String computeMethod(String headerName, ClientRequestContext context, int extra) {
+            return "too many arguments";
+        }
+    }
+
     private static RestClientBuilder newBuilder() {
         RestClientBuilder builder = RestClientBuilder.newBuilder();
         try {
@@ -132,6 +190,38 @@ public class ValidatorTest {
         test(ExtraParamTemplate.class, "extra path segments", "ExtraParamTemplate");
     }
 
+    @Test
+    public void testClientHeaderParamNoName() {
+        test(ClientHeaderParamNoName.class, ClientHeaderParamNoName.class.getName(), "null or empty name");
+    }
+
+    @Test
+    public void testClientHeaderParamNoComputeMethod() {
+        test(ClientHeaderParamNoComputeMethod.class, ClientHeaderParamNoComputeMethod.class.getName(), 
+             "value attribute specifies a method", "that does not exist");
+    }
+
+    @Test
+    public void testClientHeaderParamNonDefaultComputeMethod() {
+        test(ClientHeaderParamNonDefaultComputeMethod.class,
+             ClientHeaderParamNonDefaultComputeMethod.class.getName(),
+             " is not accessible");
+    }
+
+    @Test
+    public void testClientHeaderParamComputeMethodDoesNotExist() {
+        test(ClientHeaderParamNonDefaultComputeMethod.class,
+             ClientHeaderParamNonDefaultComputeMethod.class.getName(),
+             " does not exist");
+    }
+
+    @Test
+    public void testClientHeaderParamNoValidComputeMethodSignatures() {
+        test(ClientHeaderParamNoValidComputeMethodSignatures.class,
+             ClientHeaderParamNoValidComputeMethodSignatures.class.getName(),
+             " contains an incorrect signature");
+    }
+
     private void test(Class<?> clientInterface, String...expectedMessageTexts) {
         try {
             newBuilder().build(clientInterface);
diff --git a/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/mock/HeaderCaptureClientRequestFilter.java b/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/mock/HeaderCaptureClientRequestFilter.java
new file mode 100644
index 0000000..57c6fc5
--- /dev/null
+++ b/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/mock/HeaderCaptureClientRequestFilter.java
@@ -0,0 +1,46 @@
+/**
+ * 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.mock;
+
+import java.io.IOException;
+
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientRequestFilter;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+
+public class HeaderCaptureClientRequestFilter implements ClientRequestFilter {
+
+    private static MultivaluedMap<String, String> outboundHeaders;
+
+    public static MultivaluedMap<String, String> getOutboundHeaders() {
+        return outboundHeaders;
+    }
+
+    public static void setOutboundHeaders(MultivaluedMap<String, String> newHeaders) {
+        outboundHeaders = newHeaders;
+    }
+
+    @Override
+    public void filter(ClientRequestContext ctx) throws IOException {
+        setOutboundHeaders(ctx.getStringHeaders());
+        ctx.abortWith(Response.ok("SUCCESS").build());
+    }
+
+}
diff --git a/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/mock/HeaderGenerator.java b/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/mock/HeaderGenerator.java
new file mode 100644
index 0000000..d710994
--- /dev/null
+++ b/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/mock/HeaderGenerator.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.microprofile.client.mock;
+
+public final class HeaderGenerator {
+
+    private HeaderGenerator() {
+    }
+
+    public static String generateHeader(String headerName) {
+        return generateHeaderPrivate(headerName);
+    }
+
+    private static String generateHeaderPrivate(String headerName) {
+        return "valueFor" + headerName;
+    }
+}
diff --git a/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/mock/HeadersFactoryClient.java b/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/mock/HeadersFactoryClient.java
new file mode 100644
index 0000000..dc6d125
--- /dev/null
+++ b/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/mock/HeadersFactoryClient.java
@@ -0,0 +1,40 @@
+/**
+ * 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.mock;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.Path;
+
+import org.eclipse.microprofile.rest.client.annotation.ClientHeaderParam;
+import org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders;
+
+@RegisterClientHeaders(MyClientHeadersFactory.class)
+@ClientHeaderParam(name = "IntfHeader1", value = "abc")
+public interface HeadersFactoryClient {
+
+    default String computeHeader(String headerName) {
+        return "HeadersOnMethodClientValueFor" + headerName;
+    }
+
+    @ClientHeaderParam(name = "MethodHeader1", value = "def")
+    @GET
+    @Path("/")
+    String get(@HeaderParam("HeaderParam1") String someValue);
+}
diff --git a/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/mock/HeadersOnInterfaceClient.java b/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/mock/HeadersOnInterfaceClient.java
new file mode 100644
index 0000000..dd9d5a6
--- /dev/null
+++ b/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/mock/HeadersOnInterfaceClient.java
@@ -0,0 +1,40 @@
+/**
+ * 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.mock;
+
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+
+import org.eclipse.microprofile.rest.client.annotation.ClientHeaderParam;
+
+@ClientHeaderParam(name = "IntfHeader1", value = "value1")
+@ClientHeaderParam(name = "IntfHeader2", value = {"value2", "value3"})
+@ClientHeaderParam(name = "IntfHeader3", value = "{computeHeader}")
+@ClientHeaderParam(name = "IntfHeader4",
+    value = "{org.apache.cxf.microprofile.client.mock.HeaderGenerator.generateHeader}")
+public interface HeadersOnInterfaceClient {
+
+    default String computeHeader(String headerName) {
+        return "HeadersOnInterfaceClientValueFor" + headerName;
+    }
+
+    @PUT
+    @Path("/")
+    String put(String someValue);
+}
diff --git a/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/mock/HeadersOnMethodClient.java b/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/mock/HeadersOnMethodClient.java
new file mode 100644
index 0000000..799620b
--- /dev/null
+++ b/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/mock/HeadersOnMethodClient.java
@@ -0,0 +1,41 @@
+/**
+ * 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.mock;
+
+import javax.ws.rs.DELETE;
+import javax.ws.rs.Path;
+
+import org.eclipse.microprofile.rest.client.annotation.ClientHeaderParam;
+
+
+public interface HeadersOnMethodClient {
+
+    default String computeHeader(String headerName) {
+        return "HeadersOnMethodClientValueFor" + headerName;
+    }
+
+    @ClientHeaderParam(name = "MethodHeader1", value = "valueA")
+    @ClientHeaderParam(name = "MethodHeader2", value = {"valueB", "valueC"})
+    @ClientHeaderParam(name = "MethodHeader3", value = "{computeHeader}")
+    @ClientHeaderParam(name = "MethodHeader4",
+        value = "{org.apache.cxf.microprofile.client.mock.HeaderGenerator.generateHeader}")
+    @DELETE
+    @Path("/")
+    String delete(String someValue);
+}
diff --git a/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/mock/MockConfigProviderResolver.java b/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/mock/MockConfigProviderResolver.java
index 05267a1..962d969 100644
--- a/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/mock/MockConfigProviderResolver.java
+++ b/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/mock/MockConfigProviderResolver.java
@@ -77,7 +77,6 @@ public class MockConfigProviderResolver extends ConfigProviderResolver {
     }
 
     public MockConfigProviderResolver(Map<String, String> configValues) {
-        System.out.println("MockConfigProviderResolver ctor " + configValues);
         this.configValues = configValues;
     }
 
diff --git a/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/mock/MyClientHeadersFactory.java b/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/mock/MyClientHeadersFactory.java
new file mode 100644
index 0000000..bc039bc
--- /dev/null
+++ b/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/mock/MyClientHeadersFactory.java
@@ -0,0 +1,61 @@
+/**
+ * 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.mock;
+
+import javax.ws.rs.core.MultivaluedHashMap;
+import javax.ws.rs.core.MultivaluedMap;
+
+import org.eclipse.microprofile.rest.client.ext.ClientHeadersFactory;
+
+public class MyClientHeadersFactory implements ClientHeadersFactory {
+
+    private static MultivaluedMap<String, String> initialHeaders;
+
+    public static MultivaluedMap<String, String> getInitialHeaders() {
+        return initialHeaders;
+    }
+
+    public static void setInitialHeaders(MultivaluedMap<String, String> newHeaders) {
+        initialHeaders = newHeaders;
+    }
+
+    private static String reverse(String s) {
+        StringBuilder sb = new StringBuilder();
+        char[] ch = s.toCharArray();
+        //CHECKSTYLE:OFF
+        for (int i = ch.length-1; i >= 0; i--) {
+            sb.append(ch[i]);
+        }
+        //CHECKSTYLE:ON
+        return sb.toString();
+    }
+
+    @Override
+    public MultivaluedMap<String, String> update(MultivaluedMap<String, String> incomingHeaders,
+                                                 MultivaluedMap<String, String> clientOutgoingHeaders) {
+
+        initialHeaders = new MultivaluedHashMap<>();
+        initialHeaders.putAll(clientOutgoingHeaders);
+        MultivaluedMap<String, String> updatedMap = new MultivaluedHashMap<>();
+        clientOutgoingHeaders.forEach((k, v) -> {
+            updatedMap.putSingle(k, reverse(v.get(0))); });
+        return updatedMap;
+    }
+}
diff --git a/systests/microprofile/client/async/pom.xml b/systests/microprofile/client/async/pom.xml
index 043b095..a85ad88 100644
--- a/systests/microprofile/client/async/pom.xml
+++ b/systests/microprofile/client/async/pom.xml
@@ -26,14 +26,14 @@
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <groupId>org.apache.cxf.systests</groupId>
-    <artifactId>cxf-microprofile-async</artifactId>
+    <artifactId>cxf-systests-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.2-m2</cxf.microprofile.rest.client.version>
+        <cxf.microprofile.rest.client.version>1.2-RC1</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/jaxrs/pom.xml b/systests/microprofile/client/jaxrs/pom.xml
new file mode 100644
index 0000000..c53a686
--- /dev/null
+++ b/systests/microprofile/client/jaxrs/pom.xml
@@ -0,0 +1,190 @@
+<?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-systests-microprofile-jaxrs</artifactId>
+    <name>Apache CXF MicroProfile JAX-RS integration Sys Tests</name>
+    <description>Apache CXF System Tests - verifying MicroProfile Rest Client integration with JAX-RS</description>
+    <url>http://cxf.apache.org</url>
+    <properties>
+        <cxf.module.name>org.apache.cxf.systests.microprofile.jaxrs</cxf.module.name>
+        <cxf.geronimo.config.version>1.0</cxf.geronimo.config.version>
+        <cxf.microprofile.rest.client.version>1.2-RC1</cxf.microprofile.rest.client.version>
+    </properties>
+        <dependencies>
+            <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.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.cxf</groupId>
+            <artifactId>cxf-rt-rs-service-description-swagger</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.cxf</groupId>
+            <artifactId>cxf-rt-rs-service-description-openapi-v3</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.webjars</groupId>
+            <artifactId>swagger-ui</artifactId>
+            <version>${cxf.swagger.ui.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.ant</groupId>
+            <artifactId>ant</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-server</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-io</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-security</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-plus</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-webapp</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>apache-jsp</artifactId>
+            <version>${cxf.jetty.version}</version>
+            <exclusions>
+              <exclusion>
+                <groupId>org.glassfish</groupId>
+                <artifactId>javax.el</artifactId>
+              </exclusion>
+              <exclusion>
+                <groupId>org.ow2.asm</groupId>
+                <artifactId>asm-commons</artifactId>
+              </exclusion>
+            </exclusions> 
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-util</artifactId>
+            <version>${cxf.jetty.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.cxf</groupId>
+            <artifactId>cxf-core</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.cxf</groupId>
+            <artifactId>cxf-rt-transports-http</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.cxf</groupId>
+            <artifactId>cxf-rt-transports-http-jetty</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.cxf</groupId>
+            <artifactId>cxf-rt-features-logging</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>        
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-jdk14</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.cxf</groupId>
+            <artifactId>cxf-rt-transports-local</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-rs-client</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.cxf</groupId>
+            <artifactId>cxf-testutils</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.cxf</groupId>
+            <artifactId>cxf-testutils</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+            <classifier>keys</classifier>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.cxf</groupId>
+            <artifactId>cxf-rt-transports-http-hc</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>jsr250-api</artifactId>
+            <version>1.0</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/systests/microprofile/client/jaxrs/src/test/java/org/apache/cxf/systest/microprofile/rest/client/JaxrsHeaderPropagationTest.java b/systests/microprofile/client/jaxrs/src/test/java/org/apache/cxf/systest/microprofile/rest/client/JaxrsHeaderPropagationTest.java
new file mode 100644
index 0000000..943fa80
--- /dev/null
+++ b/systests/microprofile/client/jaxrs/src/test/java/org/apache/cxf/systest/microprofile/rest/client/JaxrsHeaderPropagationTest.java
@@ -0,0 +1,118 @@
+/**
+ * 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.util.Collections;
+import java.util.logging.ConsoleHandler;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
+import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
+import org.apache.cxf.jaxrs.model.AbstractResourceInfo;
+import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase;
+import org.apache.cxf.testutil.common.AbstractBusTestServerBase;
+import org.eclipse.microprofile.config.spi.ConfigProviderResolver;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class JaxrsHeaderPropagationTest extends AbstractBusClientServerTestBase {
+    public static final String PORT = allocatePort(JaxrsHeaderPropagationTest.class);
+
+    WebClient client;
+    @Ignore
+    public static class Server extends AbstractBusTestServerBase {
+        protected void run() {
+            final JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
+            sf.setResourceClasses(JaxrsResource.class);
+            sf.setResourceProvider(JaxrsResource.class,
+                new SingletonResourceProvider(new JaxrsResource()));
+            sf.setAddress("http://localhost:" + PORT + "/");
+            sf.setPublishedEndpointUrl("/");
+            sf.create();
+        }
+
+        public static void main(String[] args) {
+            try {
+                Server s = new Server();
+                s.start();
+            } catch (Exception ex) {
+                ex.printStackTrace();
+                System.exit(-1);
+            } finally {
+                System.out.println("done!");
+            }
+        }
+    }
+
+    @BeforeClass
+    public static void startServers() throws Exception {
+        
+        AbstractResourceInfo.clearAllMaps();
+        //keep out of process due to stack traces testing failures
+        assertTrue("server did not launch correctly", launchServer(Server.class, true));
+        createStaticBus();
+        System.out.println("Listening on port " + PORT);
+    }
+
+    @Before
+    public void setUp() {
+        final Response r = createWebClient("/jaxrs/check").get();
+        assertEquals(Status.OK.getStatusCode(), r.getStatus());
+    }
+
+    @Test
+    public void testHeadersArePropagated() throws Exception {
+        ConfigProviderResolver.setInstance(
+            new MockConfigProviderResolver(Collections.singletonMap(
+                "org.eclipse.microprofile.rest.client.propagateHeaders", "Header1,MultiHeader")));
+        Logger logger = 
+            Logger.getLogger("org.eclipse.microprofile.rest.client.ext.DefaultClientHeadersFactoryImpl"); //NOPMD
+        logger.setLevel(Level.ALL);
+        ConsoleHandler h = new ConsoleHandler();
+        h.setLevel(Level.ALL);
+        logger.addHandler(new ConsoleHandler());
+        final Response r = createWebClient("/jaxrs/propagate")
+            .header("Header1", "Single")
+            .header("MultiHeader", "value1", "value2", "value3")
+            .get();
+        assertEquals(Status.OK.getStatusCode(), r.getStatus());
+        String propagatedHeaderContent = r.readEntity(String.class);
+        System.out.println("propagatedHeaderContent: " + propagatedHeaderContent);
+        assertTrue(propagatedHeaderContent.contains("Header1=Single"));
+        assertTrue(propagatedHeaderContent.contains("MultiHeader=value1,value2,value3"));
+    }
+
+    private static WebClient createWebClient(final String url) {
+        return WebClient
+            .create("http://localhost:" + PORT + url)
+            .accept(MediaType.TEXT_PLAIN);
+    }
+}
diff --git a/systests/microprofile/client/jaxrs/src/test/java/org/apache/cxf/systest/microprofile/rest/client/JaxrsResource.java b/systests/microprofile/client/jaxrs/src/test/java/org/apache/cxf/systest/microprofile/rest/client/JaxrsResource.java
new file mode 100644
index 0000000..455ef80
--- /dev/null
+++ b/systests/microprofile/client/jaxrs/src/test/java/org/apache/cxf/systest/microprofile/rest/client/JaxrsResource.java
@@ -0,0 +1,49 @@
+/**
+ * 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 javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Response;
+
+import org.eclipse.microprofile.rest.client.RestClientBuilder;
+
+@Path("/jaxrs")
+@Produces("text/plain")
+public class JaxrsResource {
+
+    @Path("/check")
+    @GET
+    public Response statusCheck() {
+        return Response.ok("up").build();
+    }
+
+    @Path("/propagate")
+    @GET
+    public String propagateHeadersToRestClient() {
+        RestClient client = RestClientBuilder.newBuilder()
+                                             .baseUri(URI.create("http://localhost:8080/ignored"))
+                                             .register(ReturnAllOutboundHeadersFilter.class)
+                                             .build(RestClient.class);
+        return client.getAllHeadersToBeSent();
+    }
+}
diff --git a/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/mock/MockConfigProviderResolver.java b/systests/microprofile/client/jaxrs/src/test/java/org/apache/cxf/systest/microprofile/rest/client/MockConfigProviderResolver.java
similarity index 96%
copy from rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/mock/MockConfigProviderResolver.java
copy to systests/microprofile/client/jaxrs/src/test/java/org/apache/cxf/systest/microprofile/rest/client/MockConfigProviderResolver.java
index 05267a1..6a11f26 100644
--- a/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/mock/MockConfigProviderResolver.java
+++ b/systests/microprofile/client/jaxrs/src/test/java/org/apache/cxf/systest/microprofile/rest/client/MockConfigProviderResolver.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.cxf.microprofile.client.mock;
+package org.apache.cxf.systest.microprofile.rest.client;
 
 import java.util.Arrays;
 import java.util.HashMap;
@@ -77,7 +77,6 @@ public class MockConfigProviderResolver extends ConfigProviderResolver {
     }
 
     public MockConfigProviderResolver(Map<String, String> configValues) {
-        System.out.println("MockConfigProviderResolver ctor " + configValues);
         this.configValues = configValues;
     }
 
diff --git a/systests/microprofile/client/jaxrs/src/test/java/org/apache/cxf/systest/microprofile/rest/client/RestClient.java b/systests/microprofile/client/jaxrs/src/test/java/org/apache/cxf/systest/microprofile/rest/client/RestClient.java
new file mode 100644
index 0000000..648aa66
--- /dev/null
+++ b/systests/microprofile/client/jaxrs/src/test/java/org/apache/cxf/systest/microprofile/rest/client/RestClient.java
@@ -0,0 +1,32 @@
+/**
+ * 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 javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+import org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders;
+
+@Path("/remote")
+@RegisterClientHeaders
+public interface RestClient {
+
+    @GET
+    String getAllHeadersToBeSent();
+}
diff --git a/systests/microprofile/client/jaxrs/src/test/java/org/apache/cxf/systest/microprofile/rest/client/ReturnAllOutboundHeadersFilter.java b/systests/microprofile/client/jaxrs/src/test/java/org/apache/cxf/systest/microprofile/rest/client/ReturnAllOutboundHeadersFilter.java
new file mode 100644
index 0000000..2d034e2
--- /dev/null
+++ b/systests/microprofile/client/jaxrs/src/test/java/org/apache/cxf/systest/microprofile/rest/client/ReturnAllOutboundHeadersFilter.java
@@ -0,0 +1,40 @@
+/**
+ * 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.io.IOException;
+import java.util.stream.Collectors;
+
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientRequestFilter;
+import javax.ws.rs.core.Response;
+
+public class ReturnAllOutboundHeadersFilter implements ClientRequestFilter {
+
+    @Override
+    public void filter(ClientRequestContext requestContext) throws IOException {
+        StringBuilder sb = new StringBuilder();
+        requestContext.getStringHeaders().forEach((k, v) -> {
+            sb.append(k).append("=").append(v.stream().collect(Collectors.joining(",")))
+                .append(System.lineSeparator());
+        });
+        requestContext.abortWith(Response.ok(sb.toString()).build());
+    }
+
+}
diff --git a/systests/microprofile/client/weld/testng.xml b/systests/microprofile/client/weld/testng.xml
index f2cea4b..70f37b9 100644
--- a/systests/microprofile/client/weld/testng.xml
+++ b/systests/microprofile/client/weld/testng.xml
@@ -6,6 +6,7 @@
             <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" />
+            <package name="org.eclipse.microprofile.rest.client.tck.timeout" />
         </packages>
     </test>
 </suite>
diff --git a/systests/microprofile/pom.xml b/systests/microprofile/pom.xml
index f92b748..8889de5 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.2-m2</cxf.microprofile.rest.client.version>
+        <cxf.microprofile.rest.client.version>1.2-RC1</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>
@@ -133,6 +133,7 @@
     </dependencyManagement>
     <modules>
         <module>client/async</module>
+        <module>client/jaxrs</module>
         <module>client/weld</module>
     </modules>
 </project>