Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Rest Client Reactive - MicroProfile 2.0 features
Browse files Browse the repository at this point in the history
michalszynkiewicz authored and cescoffier committed Apr 3, 2021

Unverified

This commit is not signed, but one or more authors requires that any commit attributed to them is signed.
1 parent ffc355d commit 186ad86
Showing 27 changed files with 662 additions and 342 deletions.
Original file line number Diff line number Diff line change
@@ -93,6 +93,7 @@
import io.quarkus.jaxrs.client.reactive.deployment.beanparam.QueryParamItem;
import io.quarkus.jaxrs.client.reactive.runtime.ClientResponseBuilderFactory;
import io.quarkus.jaxrs.client.reactive.runtime.JaxrsClientReactiveRecorder;
import io.quarkus.resteasy.reactive.client.runtime.ToObjectArray;
import io.quarkus.resteasy.reactive.common.deployment.ApplicationResultBuildItem;
import io.quarkus.resteasy.reactive.common.deployment.QuarkusFactoryCreator;
import io.quarkus.resteasy.reactive.common.deployment.ResourceScanningResultBuildItem;
@@ -451,7 +452,8 @@ public void close() {

// query params have to be set on a method-level web target (they vary between invocations)
methodCreator.assign(methodTarget, addQueryParam(methodCreator, methodTarget, param.name,
methodCreator.getMethodParam(paramIdx)));
methodCreator.getMethodParam(paramIdx),
jandexMethod.parameters().get(paramIdx), index));
} else if (param.parameterType == ParameterType.BEAN) {
// bean params require both, web-target and Invocation.Builder, modifications
// The web target changes have to be done on the method level.
@@ -470,7 +472,7 @@ public void close() {
handleBeanParamMethod.assign(invocationBuilderRef, handleBeanParamMethod.getMethodParam(0));
addBeanParamData(methodCreator, handleBeanParamMethod,
invocationBuilderRef, beanParam.getItems(),
methodCreator.getMethodParam(paramIdx), methodTarget);
methodCreator.getMethodParam(paramIdx), methodTarget, index);

handleBeanParamMethod.returnValue(invocationBuilderRef);
invocationBuilderEnrichers.put(handleBeanParamDescriptor, methodCreator.getMethodParam(paramIdx));
@@ -809,8 +811,8 @@ private void addBeanParamData(BytecodeCreator methodCreator,
AssignableResultHandle invocationBuilder,
List<Item> beanParamItems,
ResultHandle param,
AssignableResultHandle target // can only be used in the current method, not in `invocationBuilderEnricher`
) {
AssignableResultHandle target, // can only be used in the current method, not in `invocationBuilderEnricher`
IndexView index) {
BytecodeCreator creator = methodCreator.ifNotNull(param).trueBranch();
BytecodeCreator invoEnricher = invocationBuilderEnricher.ifNotNull(invocationBuilderEnricher.getMethodParam(1))
.trueBranch();
@@ -820,12 +822,15 @@ private void addBeanParamData(BytecodeCreator methodCreator,
BeanParamItem beanParamItem = (BeanParamItem) item;
ResultHandle beanParamElementHandle = beanParamItem.extract(creator, param);
addBeanParamData(creator, invoEnricher, invocationBuilder, beanParamItem.items(),
beanParamElementHandle, target);
beanParamElementHandle, target, index);
break;
case QUERY_PARAM:
QueryParamItem queryParam = (QueryParamItem) item;
creator.assign(target,
addQueryParam(creator, target, queryParam.name(), queryParam.extract(creator, param)));
addQueryParam(creator, target, queryParam.name(),
queryParam.extract(creator, param),
queryParam.getValueType(),
index));
break;
case COOKIE:
CookieParamItem cookieParam = (CookieParamItem) item;
@@ -848,13 +853,29 @@ private void addBeanParamData(BytecodeCreator methodCreator,
// takes a result handle to target as one of the parameters, returns a result handle to a modified target
private ResultHandle addQueryParam(BytecodeCreator methodCreator,
ResultHandle target,
String paramName, ResultHandle queryParamHandle) {
ResultHandle array = methodCreator.newArray(Object.class, 1);
methodCreator.writeArrayValue(array, 0, queryParamHandle);
String paramName,
ResultHandle queryParamHandle,
Type type,
IndexView index) {
ResultHandle paramArray;
if (type.kind() == Type.Kind.ARRAY) {
paramArray = methodCreator.checkCast(queryParamHandle, Object[].class);
} else if (index
.getClassByName(type.name()).interfaceNames().stream()
.anyMatch(DotName.createSimple(Collection.class.getName())::equals)) {
paramArray = methodCreator.invokeStaticMethod(
MethodDescriptor.ofMethod(ToObjectArray.class, "collection", Object[].class, Collection.class),
queryParamHandle);
} else {
paramArray = methodCreator.invokeStaticMethod(
MethodDescriptor.ofMethod(ToObjectArray.class, "value", Object[].class, Object.class),
queryParamHandle);
}

ResultHandle alteredTarget = methodCreator.invokeInterfaceMethod(
MethodDescriptor.ofMethod(WebTarget.class, "queryParam", WebTarget.class,
String.class, Object[].class),
target, methodCreator.load(paramName), array);
target, methodCreator.load(paramName), paramArray);
return alteredTarget;
}

Original file line number Diff line number Diff line change
@@ -29,11 +29,12 @@ public static List<Item> parse(ClassInfo beanParamClass, IndexView index) {
if (target.kind() == AnnotationTarget.Kind.FIELD) {
FieldInfo fieldInfo = target.asField();
resultList.add(new QueryParamItem(annotation.value().asString(),
new FieldExtractor(null, fieldInfo.name(), fieldInfo.declaringClass().name().toString())));
new FieldExtractor(null, fieldInfo.name(), fieldInfo.declaringClass().name().toString()),
fieldInfo.type()));
} else if (target.kind() == AnnotationTarget.Kind.METHOD) {
MethodInfo getterMethod = getGetterMethod(beanParamClass, target.asMethod());
resultList.add(new QueryParamItem(annotation.value().asString(),
new GetterExtractor(getterMethod)));
new GetterExtractor(getterMethod), getterMethod.returnType()));
}
}
}
@@ -116,4 +117,7 @@ private static MethodInfo getGetterMethod(ClassInfo beanParamClass, MethodInfo m
}
return getter;
}

private BeanParamParser() {
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
package io.quarkus.jaxrs.client.reactive.deployment.beanparam;

import org.jboss.jandex.Type;

public class QueryParamItem extends Item {

private final String name;
private final Type valueType;

public QueryParamItem(String name, ValueExtractor extractor) {
public QueryParamItem(String name, ValueExtractor extractor, Type valueType) {
super(ItemType.QUERY_PARAM, extractor);
this.name = name;
this.valueType = valueType;
}

public String name() {
return name;
}

public Type getValueType() {
return valueType;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.quarkus.jaxrs.client.reactive.runtime;

import java.util.Collection;

/**
* used by query param handling mechanism, in generated code
*/
@SuppressWarnings("unused")
public class ToObjectArray {

public static Object[] collection(Collection<?> collection) {
return collection.toArray();
}

public static Object[] value(Object value) {
return new Object[] { value };
}

private ToObjectArray() {
}
}
Original file line number Diff line number Diff line change
@@ -23,6 +23,9 @@
import javax.ws.rs.core.Configurable;
import javax.ws.rs.core.MultivaluedMap;

import io.quarkus.jaxrs.client.reactive.deployment.JaxrsClientReactiveEnricher;
import io.quarkus.rest.client.reactive.MicroProfileRestClientRequestFilter;
import io.quarkus.rest.client.reactive.runtime.NoOpHeaderFiller;
import org.eclipse.microprofile.rest.client.RestClientDefinitionException;
import org.eclipse.microprofile.rest.client.ext.ClientHeadersFactory;
import org.eclipse.microprofile.rest.client.ext.DefaultClientHeadersFactoryImpl;
@@ -42,6 +45,7 @@
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.builditem.GeneratedClassBuildItem;
import io.quarkus.gizmo.AssignableResultHandle;
import io.quarkus.gizmo.BranchResult;
import io.quarkus.gizmo.BytecodeCreator;
import io.quarkus.gizmo.CatchBlockCreator;
import io.quarkus.gizmo.ClassCreator;
@@ -52,10 +56,8 @@
import io.quarkus.gizmo.MethodDescriptor;
import io.quarkus.gizmo.ResultHandle;
import io.quarkus.gizmo.TryBlock;
import io.quarkus.jaxrs.client.reactive.deployment.JaxrsClientReactiveEnricher;
import io.quarkus.rest.client.reactive.BeanGrabber;
import io.quarkus.rest.client.reactive.HeaderFiller;
import io.quarkus.rest.client.reactive.runtime.NoOpHeaderFiller;
import io.quarkus.rest.client.reactive.runtime.RestClientReactiveRequestFilter;
import io.quarkus.runtime.util.HashUtil;

/**
@@ -64,8 +66,8 @@
* Used mostly to handle the `@RegisterProvider` annotation that e.g. registers filters
* and to add support for `@ClientHeaderParam` annotations for specifying (possibly) computed headers via annotations
*/
class RestClientReactiveEnricher implements JaxrsClientReactiveEnricher {
private static final Logger log = Logger.getLogger(RestClientReactiveEnricher.class);
class MicroProfileRestClientEnricher implements JaxrsClientReactiveEnricher {
private static final Logger log = Logger.getLogger(MicroProfileRestClientEnricher.class);

public static final String DEFAULT_HEADERS_FACTORY = DefaultClientHeadersFactoryImpl.class.getName();

@@ -128,7 +130,7 @@ public void forClass(MethodCreator constructor, AssignableResultHandle webTarget
}

ResultHandle restClientFilter = constructor.newInstance(
MethodDescriptor.ofConstructor(RestClientReactiveRequestFilter.class, ClientHeadersFactory.class),
MethodDescriptor.ofConstructor(MicroProfileRestClientRequestFilter.class, ClientHeadersFactory.class),
clientHeadersFactory);

constructor.assign(webTargetBase, constructor.invokeInterfaceMethod(
@@ -433,13 +435,35 @@ private AnnotationInstance[] extractAnnotations(AnnotationInstance groupAnnotati

private void addProvider(MethodCreator ctor, AssignableResultHandle target, IndexView index,
AnnotationInstance registerProvider) {
ResultHandle provider = ctor.newInstance(MethodDescriptor.ofConstructor(registerProvider.value().asString()));
ResultHandle alteredTarget = ctor.invokeInterfaceMethod(
// if a registered provider is a cdi bean, it has to be reused
// take the name of the provider class from the annotation:
String providerClass = registerProvider.value().asString();

// get bean, or null, with BeanGrabber.getBeanIfDefined(providerClass)
ResultHandle providerBean = ctor.invokeStaticMethod(
MethodDescriptor.ofMethod(BeanGrabber.class, "getBeanIfDefined", Object.class, Class.class),
ctor.loadClass(providerClass));

// if bean != null, register the bean
BranchResult branchResult = ctor.ifNotNull(providerBean);
BytecodeCreator beanProviderAvailable = branchResult.trueBranch();

ResultHandle alteredTarget = beanProviderAvailable.invokeInterfaceMethod(
MethodDescriptor.ofMethod(Configurable.class, "register", Configurable.class, Object.class,
int.class),
target, providerBean,
beanProviderAvailable.load(registerProvider.valueWithDefault(index, "priority").asInt()));
beanProviderAvailable.assign(target, alteredTarget);

// else, create a new instance of the provider class
BytecodeCreator beanProviderNotAvailable = branchResult.falseBranch();
ResultHandle provider = beanProviderNotAvailable.newInstance(MethodDescriptor.ofConstructor(providerClass));
alteredTarget = beanProviderNotAvailable.invokeInterfaceMethod(
MethodDescriptor.ofMethod(Configurable.class, "register", Configurable.class, Object.class,
int.class),
target, provider,
ctor.load(registerProvider.valueWithDefault(index, "priority").asInt()));
ctor.assign(target, alteredTarget);
beanProviderNotAvailable.load(registerProvider.valueWithDefault(index, "priority").asInt()));
beanProviderNotAvailable.assign(target, alteredTarget);
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package io.quarkus.rest.client.reactive.redirect;

import static org.assertj.core.api.Assertions.assertThat;

import java.net.URI;

import javax.ws.rs.core.Response;

import org.eclipse.microprofile.rest.client.RestClientBuilder;
import org.jboss.resteasy.reactive.client.api.QuarkusRestClientProperties;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;
import io.quarkus.test.common.http.TestHTTPResource;

public class RedirectTest {

@RegisterExtension
static final QuarkusUnitTest TEST = new QuarkusUnitTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClasses(RedirectingResourceClient.class, RedirectingResource.class));

@TestHTTPResource
URI uri;

@Test
void shouldRedirect3Times_whenMax4() {
RedirectingResourceClient client = RestClientBuilder.newBuilder()
.baseUri(uri)
.followRedirects(true)
.property(QuarkusRestClientProperties.MAX_REDIRECTS, 4)
.build(RedirectingResourceClient.class);
Response call = client.call(3);
assertThat(call.getStatus()).isEqualTo(200);
}

@Test
void shouldNotRedirect3Times_whenMax2() {
RedirectingResourceClient client = RestClientBuilder.newBuilder()
.baseUri(uri)
.followRedirects(true)
.property(QuarkusRestClientProperties.MAX_REDIRECTS, 2)
.build(RedirectingResourceClient.class);
assertThat(client.call(3).getStatus()).isEqualTo(307);

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.quarkus.rest.client.reactive.redirect;

import java.net.URI;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;

@Path("/redirect")
public class RedirectingResource {

@GET
public Response redirectedResponse(@QueryParam("redirects") Integer number) {
if (number == null || 0 == number) {
return Response.ok().build();
} else {
return Response.temporaryRedirect(URI.create("/redirect?redirects=" + (number - 1))).build();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package io.quarkus.rest.client.reactive.redirect;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;

@Path("/redirect")
public interface RedirectingResourceClient {
@GET
Response call(@QueryParam("redirects") Integer numberOfRedirects);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.quarkus.rest.client.reactive;

import io.quarkus.arc.Arc;
import io.quarkus.arc.InstanceHandle;

@SuppressWarnings("unused")
public class BeanGrabber {
public static <T> T getBeanIfDefined(Class<T> beanClass) {
InstanceHandle<T> instance = Arc.container().instance(beanClass);
if (instance.isAvailable()) {
return instance.get();
}
return null;
}

private BeanGrabber() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package io.quarkus.rest.client.reactive;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;

import org.eclipse.microprofile.rest.client.ext.ResponseExceptionMapper;

public class DefaultMicroprofileRestClientExceptionMapper implements ResponseExceptionMapper {

public Throwable toThrowable(Response response) {
try {
response.bufferEntity();
} catch (Exception var3) {
}

return new WebApplicationException("Unknown error, status code " + response.getStatus(), response);
}

public boolean handles(int status, MultivaluedMap headers) {
return status >= 400;
}

public int getPriority() {
return Integer.MAX_VALUE;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package io.quarkus.rest.client.reactive;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.annotation.Priority;
import javax.enterprise.context.RequestScoped;
import javax.ws.rs.client.ClientRequestContext;
import javax.ws.rs.client.ClientRequestFilter;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;

import io.quarkus.rest.client.reactive.runtime.HeaderContainer;
import org.eclipse.microprofile.rest.client.ext.ClientHeadersFactory;
import org.eclipse.microprofile.rest.client.ext.DefaultClientHeadersFactoryImpl;

import io.quarkus.arc.Arc;

@Priority(Integer.MIN_VALUE)
public class MicroProfileRestClientRequestFilter implements ClientRequestFilter {

private static final MultivaluedMap<String, String> EMPTY_MAP = new MultivaluedHashMap<>();

private final ClientHeadersFactory clientHeadersFactory;

public MicroProfileRestClientRequestFilter(ClientHeadersFactory clientHeadersFactory) {
this.clientHeadersFactory = clientHeadersFactory;
}

@Override
public void filter(ClientRequestContext requestContext) {
HeaderFiller headerFiller = (HeaderFiller) requestContext.getProperty(HeaderFiller.class.getName());

// mutable collection of headers
MultivaluedMap<String, String> headers = new MultivaluedHashMap<>();

// gather original headers
for (Map.Entry<String, List<Object>> headerEntry : requestContext.getHeaders().entrySet()) {
headers.put(headerEntry.getKey(), castToListOfStrings(headerEntry.getValue()));
}

// add headers from MP annotations
if (headerFiller != null) {
// add headers to a mutable headers collection
headerFiller.addHeaders(headers);
}

MultivaluedMap<String, String> incomingHeaders = MicroProfileRestClientRequestFilter.EMPTY_MAP;
if (Arc.container().getActiveContext(RequestScoped.class) != null) {
HeaderContainer headerContainer = Arc.container().instance(HeaderContainer.class).get();
if (headerContainer != null) {
incomingHeaders = headerContainer.getHeaders();
}
}

if (clientHeadersFactory instanceof DefaultClientHeadersFactoryImpl) {
// When using the default factory, pass the proposed outgoing headers onto the request context.
// Propagation with the default factory will then overwrite any values if required.
for (Map.Entry<String, List<String>> headerEntry : headers.entrySet()) {
requestContext.getHeaders().put(headerEntry.getKey(), castToListOfObjects(headerEntry.getValue()));
}
}

if (clientHeadersFactory != null) {
incomingHeaders = clientHeadersFactory.update(incomingHeaders, headers);
}

for (Map.Entry<String, List<String>> headerEntry : incomingHeaders.entrySet()) {
requestContext.getHeaders().put(headerEntry.getKey(), castToListOfObjects(headerEntry.getValue()));
}
}

private static List<String> castToListOfStrings(List<Object> values) {
List<String> result = new ArrayList<>();
for (Object value : values) {
if (value instanceof String) {
result.add((String) value);
} else {
result.add(String.valueOf(value));
}
}
return result;
}

@SuppressWarnings("unchecked")
private static List<Object> castToListOfObjects(List<String> values) {
return (List<Object>) (List<?>) values;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package io.quarkus.rest.client.reactive;

import java.io.IOException;
import java.util.List;

import javax.ws.rs.ProcessingException;
import javax.ws.rs.client.ClientRequestContext;
import javax.ws.rs.client.ClientResponseContext;
import javax.ws.rs.client.ClientResponseFilter;

import org.eclipse.microprofile.rest.client.ext.ResponseExceptionMapper;
import org.jboss.resteasy.reactive.client.handlers.ClientResponseRestHandler;
import org.jboss.resteasy.reactive.client.impl.ClientRequestContextImpl;
import org.jboss.resteasy.reactive.client.impl.ClientResponseContextImpl;
import org.jboss.resteasy.reactive.common.jaxrs.ResponseImpl;

public class
MicroProfileRestClientResponseFilter implements ClientResponseFilter {
private final List<ResponseExceptionMapper<?>> exceptionMappers;

public MicroProfileRestClientResponseFilter(List<ResponseExceptionMapper<?>> exceptionMappers) {
if (exceptionMappers == null) {
throw new NullPointerException("exceptionMappers cannot be null");
}
this.exceptionMappers = exceptionMappers;
}

@Override
public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException {
for (ResponseExceptionMapper exceptionMapper : exceptionMappers) {
if (exceptionMapper.handles(responseContext.getStatus(), responseContext.getHeaders())) {
// we have an exception mapper, we don't need the response anymore, we can map it to response right away (I hope :D)
ResponseImpl response = ClientResponseRestHandler.mapToResponse(
((ClientRequestContextImpl) requestContext).getRestClientRequestContext(),
(ClientResponseContextImpl) responseContext);
Throwable throwable = exceptionMapper.toThrowable(response);
if (throwable != null) {
throw new ProcessingException(throwable);
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.quarkus.rest.client.reactive.runtime;
package io.quarkus.rest.client.reactive;

import java.lang.reflect.InvocationTargetException;
import java.net.URISyntaxException;
@@ -14,7 +14,6 @@
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.ws.rs.RuntimeType;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.core.Configuration;

import org.eclipse.microprofile.config.ConfigProvider;
@@ -27,6 +26,10 @@
import org.jboss.resteasy.reactive.client.impl.ClientImpl;
import org.jboss.resteasy.reactive.client.impl.WebTargetImpl;
import org.jboss.resteasy.reactive.common.jaxrs.ConfigurationImpl;
import org.jboss.resteasy.reactive.common.jaxrs.MultiQueryParamMode;

import io.quarkus.arc.Arc;
import io.quarkus.arc.InstanceHandle;

/**
* Builder implementation for MicroProfile Rest Client
@@ -35,14 +38,12 @@ public class RestClientBuilderImpl implements RestClientBuilder {

private static final String DEFAULT_MAPPER_DISABLED = "microprofile.rest.client.disable.default.mapper";

private final ClientBuilder clientBuilder = new ClientBuilderImpl().withConfig(new ConfigurationImpl(RuntimeType.CLIENT));
private final ClientBuilderImpl clientBuilder = (ClientBuilderImpl) new ClientBuilderImpl()
.withConfig(new ConfigurationImpl(RuntimeType.CLIENT));
private final List<ResponseExceptionMapper<?>> exceptionMappers = new ArrayList<>();

private URL url;
// TODO - MP4 - Require Implementation
private boolean followRedirect;
private String host;
private Integer port;
private boolean followRedirects;
private QueryParamStyle queryParamStyle;

@Override
@@ -89,7 +90,7 @@ public RestClientBuilder hostnameVerifier(HostnameVerifier hostnameVerifier) {

@Override
public RestClientBuilder followRedirects(final boolean follow) {
this.followRedirect = follow;
this.followRedirects = follow;
return this;
}

@@ -102,8 +103,7 @@ public RestClientBuilder proxyAddress(final String proxyHost, final int proxyPor
throw new IllegalArgumentException("Invalid port number");
}

this.host = proxyHost;
this.port = proxyPort;
clientBuilder.proxy(proxyHost, proxyPort);
return this;
}

@@ -127,46 +127,53 @@ public RestClientBuilder property(String name, Object value) {

@Override
public RestClientBuilder register(Class<?> componentClass) {
registerMpSpecificProvider(componentClass);
clientBuilder.register(componentClass);
Object bean = BeanGrabber.getBeanIfDefined(componentClass);
if (bean != null) {
registerMpSpecificProvider(bean);
clientBuilder.register(bean);
} else {
registerMpSpecificProvider(componentClass);
clientBuilder.register(componentClass);
}
return this;
}

@Override
public RestClientBuilder register(Class<?> componentClass, int priority) {
registerMpSpecificProvider(componentClass);
clientBuilder.register(componentClass, priority);
return this;
}

private void registerMpSpecificProvider(Class<?> componentClass) {
if (ResponseExceptionMapper.class.isAssignableFrom(componentClass)) {
try {
registerMpSpecificProvider(componentClass.getDeclaredConstructor().newInstance());
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
throw new IllegalArgumentException("Failed to instantiate exception mapper " + componentClass
+ ". Does it have a public no-arg constructor?", e);
}
InstanceHandle<?> instance = Arc.container().instance(componentClass);
if (instance.isAvailable()) {
registerMpSpecificProvider(instance.get());
clientBuilder.register(instance.get(), priority);
} else {
registerMpSpecificProvider(componentClass);
clientBuilder.register(componentClass, priority);
}
return this;
}

private void registerMpSpecificProvider(Object component) {
if (component instanceof ResponseExceptionMapper) {
exceptionMappers.add((ResponseExceptionMapper<?>) component);
}
};

@Override
public RestClientBuilder register(Class<?> componentClass, Class<?>... contracts) {
registerMpSpecificProvider(componentClass);
clientBuilder.register(componentClass, contracts);
InstanceHandle<?> instance = Arc.container().instance(componentClass);
if (instance.isAvailable()) {
registerMpSpecificProvider(instance.get());
clientBuilder.register(instance.get(), contracts);
} else {
registerMpSpecificProvider(componentClass);
clientBuilder.register(componentClass, contracts);
}
return this;
}

@Override
public RestClientBuilder register(Class<?> componentClass, Map<Class<?>, Integer> contracts) {
registerMpSpecificProvider(componentClass);
clientBuilder.register(componentClass, contracts);
InstanceHandle<?> instance = Arc.container().instance(componentClass);
if (instance.isAvailable()) {
registerMpSpecificProvider(instance.get());
clientBuilder.register(instance.get(), contracts);
} else {
registerMpSpecificProvider(componentClass);
clientBuilder.register(componentClass, contracts);
}
return this;
}

@@ -198,9 +205,26 @@ public RestClientBuilder register(Object component, Map<Class<?>, Integer> contr
return this;
}

private void registerMpSpecificProvider(Class<?> componentClass) {
if (ResponseExceptionMapper.class.isAssignableFrom(componentClass)) {
try {
registerMpSpecificProvider(componentClass.getDeclaredConstructor().newInstance());
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
throw new IllegalArgumentException("Failed to instantiate exception mapper " + componentClass
+ ". Does it have a public no-arg constructor?", e);
}
}
}

private void registerMpSpecificProvider(Object component) {
if (component instanceof ResponseExceptionMapper) {
exceptionMappers.add((ResponseExceptionMapper<?>) component);
}
}

@Override
public RestClientBuilder queryParamStyle(final QueryParamStyle style) {
this.queryParamStyle = style;
queryParamStyle = style;
return this;
}

@@ -217,11 +241,15 @@ public <T> T build(Class<T> aClass) throws IllegalStateException, RestClientDefi
Boolean globallyDisabledMapper = ConfigProvider.getConfig()
.getOptionalValue(DEFAULT_MAPPER_DISABLED, Boolean.class).orElse(false);
if (!globallyDisabledMapper && !(defaultMapperDisabled instanceof Boolean && (Boolean) defaultMapperDisabled)) {
exceptionMappers.add(new DefaultRestClientReactiveExceptionMapper());
exceptionMappers.add(new DefaultMicroprofileRestClientExceptionMapper());
}

exceptionMappers.sort(Comparator.comparingInt(ResponseExceptionMapper::getPriority));
clientBuilder.register(new RestClientReactiveResponseFilter(exceptionMappers));
clientBuilder.register(new MicroProfileRestClientResponseFilter(exceptionMappers));
clientBuilder.followRedirects(followRedirects);

clientBuilder.multiQueryParamMode(toMultiQueryParamMode(queryParamStyle));

ClientImpl client = (ClientImpl) clientBuilder.build();
WebTargetImpl target = null;
try {
@@ -235,4 +263,19 @@ public <T> T build(Class<T> aClass) throws IllegalStateException, RestClientDefi
throw new RestClientDefinitionException(e);
}
}

private MultiQueryParamMode toMultiQueryParamMode(QueryParamStyle queryParamStyle) {
if (queryParamStyle == null) {
return null;
}
switch (queryParamStyle) {
case MULTI_PAIRS:
return MultiQueryParamMode.MULTI_PAIRS;
case COMMA_SEPARATED:
return MultiQueryParamMode.COMMA_SEPARATED;
case ARRAY_PAIRS:
return MultiQueryParamMode.ARRAY_PAIRS;
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.quarkus.rest.client.reactive;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.ServiceLoader;

import org.eclipse.microprofile.rest.client.spi.RestClientListener;

public class RestClientListeners {

private RestClientListeners() {
}

static Collection<RestClientListener> get() {
List<RestClientListener> result = new ArrayList<>();
ServiceLoader<RestClientListener> listeners = ServiceLoader.load(RestClientListener.class,
RestClientListeners.class.getClassLoader());
for (RestClientListener listener : listeners) {
result.add(listener);
}
return result;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.quarkus.rest.client.reactive.runtime;

import io.quarkus.rest.client.reactive.RestClientBuilderImpl;
import org.eclipse.microprofile.rest.client.RestClientBuilder;
import org.eclipse.microprofile.rest.client.spi.RestClientBuilderResolver;

Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ void setContainerRequestContext(ContainerRequestContext requestContext) {
this.requestContext = requestContext;
}

MultivaluedMap<String, String> getHeaders() {
public MultivaluedMap<String, String> getHeaders() {
if (requestContext == null) {
return EMPTY_MAP;
} else {
Original file line number Diff line number Diff line change
@@ -20,26 +20,35 @@
import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.ConfigProvider;
import org.eclipse.microprofile.rest.client.RestClientBuilder;
import org.eclipse.microprofile.rest.client.ext.QueryParamStyle;
import org.jboss.resteasy.reactive.client.api.QuarkusRestClientProperties;

public class RestClientCDIDelegateBuilder {

private static final String MP_REST = "mp-rest";
private static final String REST_URL_FORMAT = "%s/" + MP_REST + "/url";
private static final String REST_URI_FORMAT = "%s/" + MP_REST + "/uri";
private static final String REST_CONNECT_TIMEOUT_FORMAT = "%s/" + MP_REST + "/connectTimeout";
private static final String REST_READ_TIMEOUT_FORMAT = "%s/" + MP_REST + "/readTimeout";
public static final String REST_SCOPE_FORMAT = "%s/" + MP_REST + "/scope";
private static final String REST_FOLLOW_REDIRECTS = "%s/" + MP_REST + "/followRedirects";
private static final String REST_HOSTNAME_VERIFIER = "%s/" + MP_REST + "/hostnameVerifier";
private static final String REST_KEY_STORE = "%s/" + MP_REST + "/keyStore";
private static final String REST_KEY_STORE_PASSWORD = "%s/" + MP_REST + "/keyStorePassword";
private static final String REST_KEY_STORE_TYPE = "%s/" + MP_REST + "/keyStoreType";
private static final String REST_PROVIDERS = "%s/" + MP_REST + "/providers";
private static final String REST_PROXY_ADDRESS = "%s/" + MP_REST + "/proxyAddress";
private static final String REST_QUERY_PARAM_STYLE = "%s/" + MP_REST + "/queryParamStyle";
public static final String REST_SCOPE_FORMAT = "%s/" + MP_REST + "/scope";
private static final String REST_TIMEOUT_CONNECT = "%s/" + MP_REST + "/connectTimeout";
private static final String REST_TIMEOUT_READ = "%s/" + MP_REST + "/readTimeout";
private static final String REST_TRUST_STORE = "%s/" + MP_REST + "/trustStore";
private static final String REST_TRUST_STORE_PASSWORD = "%s/" + MP_REST + "/trustStorePassword";
private static final String REST_TRUST_STORE_TYPE = "%s/" + MP_REST + "/trustStoreType";
private static final String REST_KEY_STORE = "%s/" + MP_REST + "/keyStore";
private static final String REST_KEY_STORE_PASSWORD = "%s/" + MP_REST + "/keyStorePassword";
private static final String REST_KEY_STORE_TYPE = "%s/" + MP_REST + "/keyStoreType";
private static final String REST_HOSTNAME_VERIFIER = "%s/" + MP_REST + "/hostnameVerifier";
private static final String REST_NOOP_HOSTNAME_VERIFIER = "io.quarkus.restclient.NoopHostnameVerifier";
private static final String REST_URL_FORMAT = "%s/" + MP_REST + "/url";
private static final String REST_URI_FORMAT = "%s/" + MP_REST + "/uri";

private static final String MAX_REDIRECTS = "quarkus.rest.client.max-redirects";

private static final String TLS_TRUST_ALL = "quarkus.tls.trust-all";

private static final String REST_NOOP_HOSTNAME_VERIFIER = "io.quarkus.restclient.NoopHostnameVerifier";

private final Class<?> jaxrsInterface;
private final String baseUriFromAnnotation;
private final String propertyPrefix;
@@ -62,10 +71,57 @@ private Object build() {
configureTimeouts(builder);
configureProviders(builder);
configureSsl(builder);
configureRedirects(builder);
configureQueryParamStyle(builder);
configureProxy(builder);
Object result = builder.build(jaxrsInterface);
return result;
}

private void configureProxy(RestClientBuilder builder) {
Optional<String> maybeProxy = getOptionalDynamicProperty(REST_PROXY_ADDRESS, String.class);
if (maybeProxy.isPresent()) {
String proxyString = maybeProxy.get();

int lastColonIndex = proxyString.lastIndexOf(':');

if (lastColonIndex <= 0 || lastColonIndex == proxyString.length() - 1) {
throw new RuntimeException("Invalid proxy string. Expected <hostname>:<port>, found '" + proxyString + "'");
}

String host = proxyString.substring(0, lastColonIndex);
int port = 0;
try {
port = Integer.valueOf(proxyString.substring(lastColonIndex + 1));
} catch (NumberFormatException e) {
throw new RuntimeException("Invalid proxy setting. The port is not a number in '" + proxyString + "'", e);
}

builder.proxyAddress(host, port);
}
}

private void configureQueryParamStyle(RestClientBuilder builder) {
Optional<QueryParamStyle> maybeQueryParamStyle = getOptionalDynamicProperty(REST_QUERY_PARAM_STYLE,
QueryParamStyle.class);
if (maybeQueryParamStyle.isPresent()) {
QueryParamStyle queryParamStyle = maybeQueryParamStyle.get();
builder.queryParamStyle(queryParamStyle);
}
}

private void configureRedirects(RestClientBuilder builder) {
Optional<Integer> maxRedirects = getOptionalProperty(MAX_REDIRECTS, Integer.class);
if (maxRedirects.isPresent()) {
builder.property(QuarkusRestClientProperties.MAX_REDIRECTS, maxRedirects.get());
}

Optional<Boolean> maybeFollowRedirects = getOptionalDynamicProperty(REST_FOLLOW_REDIRECTS, Boolean.class);
if (maybeFollowRedirects.isPresent()) {
builder.followRedirects(maybeFollowRedirects.get());
}
}

private void configureSsl(RestClientBuilder builder) {

Optional<Boolean> trustAll = getOptionalProperty(TLS_TRUST_ALL, Boolean.class);
@@ -204,12 +260,12 @@ private Class<?> providerClassForName(String name) {
}

private void configureTimeouts(RestClientBuilder builder) {
Optional<Long> connectTimeout = getOptionalDynamicProperty(REST_CONNECT_TIMEOUT_FORMAT, Long.class);
Optional<Long> connectTimeout = getOptionalDynamicProperty(REST_TIMEOUT_CONNECT, Long.class);
if (connectTimeout.isPresent()) {
builder.connectTimeout(connectTimeout.get(), TimeUnit.MILLISECONDS);
}

Optional<Long> readTimeout = getOptionalDynamicProperty(REST_READ_TIMEOUT_FORMAT, Long.class);
Optional<Long> readTimeout = getOptionalDynamicProperty(REST_TIMEOUT_READ, Long.class);
if (readTimeout.isPresent()) {
builder.readTimeout(readTimeout.get(), TimeUnit.MILLISECONDS);
}
Original file line number Diff line number Diff line change
@@ -2,5 +2,9 @@

public class QuarkusRestClientProperties {
public static final String CONNECT_TIMEOUT = "io.quarkus.rest.client.connect-timeout";
/**
* maximum number of redirects for a client call. Works only if the client has `followingRedirects enabled
*/
public static final String MAX_REDIRECTS = "io.quarkus.rest.client.max-redirects";
public static final String READ_TIMEOUT = "io.quarkus.rest.client.read-timeout";
}
Original file line number Diff line number Diff line change
@@ -19,12 +19,18 @@
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Variant;

import org.jboss.resteasy.reactive.client.impl.AsyncInvokerImpl;
import org.jboss.resteasy.reactive.client.impl.RestClientRequestContext;
import org.jboss.resteasy.reactive.client.spi.ClientRestHandler;
import org.jboss.resteasy.reactive.common.core.Serialisers;

public class ClientSendRequestHandler implements ClientRestHandler {
private final boolean followRedirects;

public ClientSendRequestHandler(boolean followRedirects) {
this.followRedirects = followRedirects;
}

@Override
public void handle(RestClientRequestContext requestContext) {
@@ -114,13 +120,14 @@ public Future<HttpClientRequest> createRequest(RestClientRequestContext state) {
URI uri = state.getUri();
boolean isHttps = "https".equals(uri.getScheme());
int port = uri.getPort() != -1 ? uri.getPort() : (isHttps ? 443 : 80);
return httpClient.request(
new RequestOptions()
.setMethod(HttpMethod.valueOf(state.getHttpMethod()))
.setHost(uri.getHost())
.setURI(uri.getPath() + (uri.getQuery() == null ? "" : "?" + uri.getQuery()))
.setPort(port)
.setSsl(isHttps));
RequestOptions requestOptions = new RequestOptions();
requestOptions.setHost(uri.getHost());
requestOptions.setPort(port);
requestOptions.setMethod(HttpMethod.valueOf(state.getHttpMethod()));
requestOptions.setURI(uri.getPath() + (uri.getQuery() == null ? "" : "?" + uri.getQuery()));
requestOptions.setFollowRedirects(followRedirects);
requestOptions.setSsl(isHttps);
return httpClient.request(requestOptions);
}

private Buffer setRequestHeadersAndPrepareBody(HttpClientRequest httpClientRequest,
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.HttpClientOptions;
import io.vertx.core.net.JksOptions;
import io.vertx.core.net.ProxyOptions;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.KeyStore;
@@ -23,6 +24,7 @@
import org.jboss.logging.Logger;
import org.jboss.resteasy.reactive.client.spi.ClientContextResolver;
import org.jboss.resteasy.reactive.common.jaxrs.ConfigurationImpl;
import org.jboss.resteasy.reactive.common.jaxrs.MultiQueryParamMode;

public class ClientBuilderImpl extends ClientBuilder {

@@ -37,6 +39,12 @@ public class ClientBuilderImpl extends ClientBuilder {
private char[] keystorePassword;
private SSLContext sslContext;
private KeyStore trustStore;

private String proxyHost;
private int proxyPort;
private boolean followRedirects;
private MultiQueryParamMode multiQueryParamMode;

private HttpClientOptions httpClientOptions = new HttpClientOptions();

@Override
@@ -92,11 +100,27 @@ public ClientBuilder readTimeout(long timeout, TimeUnit unit) {
return this;
}

public ClientBuilder proxy(String proxyHost, int proxyPort) {
this.proxyPort = proxyPort;
this.proxyHost = proxyHost;
return this;
}

public ClientBuilder httpClientOptions(HttpClientOptions httpClientOptions) {
this.httpClientOptions = httpClientOptions;
return this;
}

public ClientBuilder followRedirects(boolean followRedirects) {
this.followRedirects = followRedirects;
return this;
}

public ClientBuilder multiQueryParamMode(MultiQueryParamMode multiQueryParamMode) {
this.multiQueryParamMode = multiQueryParamMode;
return this;
}

@Override
public ClientImpl build() {
Buffer keyStore = asBuffer(this.keyStore, keystorePassword);
@@ -120,11 +144,20 @@ public ClientImpl build() {
}
}

if (proxyHost != null) {
options.setProxyOptions(
new ProxyOptions()
.setHost(proxyHost)
.setPort(proxyPort));
}

return new ClientImpl(httpClientOptions,
configuration,
CLIENT_CONTEXT_RESOLVER.resolve(Thread.currentThread().getContextClassLoader()),
hostnameVerifier,
sslContext);
sslContext,
followRedirects,
multiQueryParamMode);

}

Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.jboss.resteasy.reactive.client.impl;

import static org.jboss.resteasy.reactive.client.api.QuarkusRestClientProperties.CONNECT_TIMEOUT;
import static org.jboss.resteasy.reactive.client.api.QuarkusRestClientProperties.MAX_REDIRECTS;

import io.netty.channel.EventLoopGroup;
import io.vertx.core.AsyncResult;
@@ -63,6 +64,8 @@
import org.jboss.resteasy.reactive.client.spi.ClientContext;
import org.jboss.resteasy.reactive.client.spi.ClientRestHandler;
import org.jboss.resteasy.reactive.common.jaxrs.ConfigurationImpl;
import org.jboss.resteasy.reactive.common.jaxrs.MultiQueryParamMode;
import org.jboss.resteasy.reactive.common.jaxrs.UriBuilderImpl;

public class ClientImpl implements Client {

@@ -78,16 +81,20 @@ public class ClientImpl implements Client {
final ClientRestHandler[] handlerChain;
final ClientRestHandler[] abortHandlerChain;
final Vertx vertx;
private final MultiQueryParamMode multiQueryParamMode;

public ClientImpl(HttpClientOptions options, ConfigurationImpl configuration, ClientContext clientContext,
HostnameVerifier hostnameVerifier,
SSLContext sslContext) {
SSLContext sslContext, boolean followRedirects,
MultiQueryParamMode multiQueryParamMode) {
configuration = configuration != null ? configuration : new ConfigurationImpl(RuntimeType.CLIENT);
// TODO: ssl context
// TODO: hostnameVerifier
this.configuration = configuration != null ? configuration : new ConfigurationImpl(RuntimeType.CLIENT);
this.configuration = configuration;
this.clientContext = clientContext;
this.hostnameVerifier = hostnameVerifier;
this.sslContext = sslContext;
this.multiQueryParamMode = multiQueryParamMode;
Supplier<Vertx> vertx = clientContext.getVertx();
if (vertx != null) {
this.vertx = vertx.get();
@@ -101,16 +108,21 @@ public Vertx get() {
});
closeVertx = true;
}
Object connectTimeoutMs = configuration == null ? null : configuration.getProperty(CONNECT_TIMEOUT);
Object connectTimeoutMs = configuration.getProperty(CONNECT_TIMEOUT);
if (connectTimeoutMs == null) {
options.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT);
} else {
options.setConnectTimeout((int) connectTimeoutMs);
}

Object maxRedirects = configuration.getProperty(MAX_REDIRECTS);
if (maxRedirects != null) {
options.setMaxRedirects((Integer) maxRedirects);
}
this.httpClient = this.vertx.createHttpClient(options);
abortHandlerChain = new ClientRestHandler[] { new ClientErrorHandler() };
handlerChain = new ClientRestHandler[] { new ClientRequestFiltersRestHandler(), new ClientSendRequestHandler(),
new ClientResponseRestHandler() };
handlerChain = new ClientRestHandler[] { new ClientRequestFiltersRestHandler(),
new ClientSendRequestHandler(followRedirects), new ClientResponseRestHandler() };
}

public ClientContext getClientContext() {
@@ -151,6 +163,9 @@ public WebTarget target(URI uri) {
public WebTarget target(UriBuilder uriBuilder) {
abortIfClosed();
Objects.requireNonNull(uriBuilder);
if (uriBuilder instanceof UriBuilderImpl && multiQueryParamMode != null) {
((UriBuilderImpl) uriBuilder).multiQueryParamMode(multiQueryParamMode);
}
return new WebTargetImpl(this, httpClient, uriBuilder, new ConfigurationImpl(configuration), handlerChain,
abortHandlerChain, null);
}
Original file line number Diff line number Diff line change
@@ -29,7 +29,9 @@ public class WebTargetImpl implements WebTarget {

public WebTargetImpl(ClientImpl restClient, HttpClient client, UriBuilder uriBuilder,
ConfigurationImpl configuration,
ClientRestHandler[] handlerChain, ClientRestHandler[] abortHandlerChain, ThreadSetupAction requestContext) {
ClientRestHandler[] handlerChain,
ClientRestHandler[] abortHandlerChain,
ThreadSetupAction requestContext) {
this.restClient = restClient;
this.client = client;
this.uriBuilder = uriBuilder;
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.jboss.resteasy.reactive.common.jaxrs;

public enum MultiQueryParamMode {
/**
* <code>foo=v1&amp;foo=v2&amp;foo=v3</code>
*/
MULTI_PAIRS,
/**
* <code>foo=v1,v2,v3</code>
*/
COMMA_SEPARATED,
/**
* <code>foo[]=v1&amp;foo[]=v2&amp;foo[]=v3</code>
*/
ARRAY_PAIRS
}
Original file line number Diff line number Diff line change
@@ -65,6 +65,8 @@ public boolean containsKey(Object key) {
private String ssp;
private String authority;

private MultiQueryParamMode queryParamMode = MultiQueryParamMode.MULTI_PAIRS;

public UriBuilder clone() {
UriBuilderImpl impl = new UriBuilderImpl();
impl.host = host;
@@ -76,6 +78,7 @@ public UriBuilder clone() {
impl.fragment = fragment;
impl.ssp = ssp;
impl.authority = authority;
impl.queryParamMode = queryParamMode;

return impl;
}
@@ -901,12 +904,29 @@ public UriBuilder queryParam(String name, Object... values) throws IllegalArgume
throw new IllegalArgumentException("Name parameter is null");
if (values == null)
throw new IllegalArgumentException("Values parameter is null");

if (queryParamMode == MultiQueryParamMode.COMMA_SEPARATED) {
sb.append(Encode.encodeQueryParam(name)).append("=");
}
for (Object value : values) {
if (value == null)
throw new IllegalArgumentException("Value is null");

sb.append(prefix);
prefix = "&";
sb.append(Encode.encodeQueryParam(name)).append("=").append(Encode.encodeQueryParam(value.toString()));
switch (queryParamMode) {
case MULTI_PAIRS:
prefix = "&";
sb.append(Encode.encodeQueryParam(name)).append("=").append(Encode.encodeQueryParam(value.toString()));
break;
case COMMA_SEPARATED:
prefix = ",";
sb.append(Encode.encodeQueryParam(value.toString()));
break;
case ARRAY_PAIRS:
prefix = "&";
sb.append(Encode.encodeQueryParam(name)).append("[]=").append(Encode.encodeQueryParam(value.toString()));
break;
}
}

query = sb.toString();
@@ -1067,4 +1087,9 @@ public UriBuilder resolveTemplatesFromEncoded(Map<String, Object> templateValues
throw new IllegalArgumentException("map key null");
return uriTemplate(buildCharSequence(templateValues, true, true, true));
}

public UriBuilder multiQueryParamMode(MultiQueryParamMode mode) {
queryParamMode = mode;
return this;
}
}
8 changes: 5 additions & 3 deletions tcks/microprofile-rest-client-reactive/pom.xml
Original file line number Diff line number Diff line change
@@ -49,11 +49,13 @@
<!-- into a release -->
<exclude>org.eclipse.microprofile.rest.client.tck.cditests.CDIInterceptorTest</exclude>

<!-- We have our own version of these tests which set up a config environment -->
<exclude>org.eclipse.microprofile.rest.client.tck.InvokeWithJsonBProviderTest</exclude>
<exclude>org.eclipse.microprofile.rest.client.tck.InvokeWithJsonPProviderTest</exclude>
<!-- We have our own version of this test, the original doesn't failing the build -->
<!-- on invalid interfaces -->
<exclude>org.eclipse.microprofile.rest.client.tck.InvalidInterfaceTest</exclude>

<!--TODO: at the moment we don't support providing serializers with @Provider:-->
<exclude>org.eclipse.microprofile.rest.client.tck.InvokeWithJsonPProviderTest</exclude>

<!-- currently not supported or unapplicable for non-blocking implementation: -->
<exclude>org.eclipse.microprofile.rest.client.tck.ssl.SslContextTest</exclude>
<exclude>org.eclipse.microprofile.rest.client.tck.ssl.SslHostnameVerifierTest</exclude>

This file was deleted.

This file was deleted.

0 comments on commit 186ad86

Please sign in to comment.