Merge pull request #7270 from aguibert/rest-client-native-providers

Transfer providers discovered at build-time to new ClientBuilder instances
This commit is contained in:
Guillaume Smet
2020-02-20 20:19:07 +01:00
committed by GitHub
10 changed files with 193 additions and 1 deletions

View File

@@ -83,6 +83,9 @@ class RestClientProcessor {
private static final DotName REGISTER_PROVIDER = DotName.createSimple(RegisterProvider.class.getName());
private static final DotName REGISTER_PROVIDERS = DotName.createSimple(RegisterProviders.class.getName());
private static final DotName CLIENT_REQUEST_FILTER = DotName.createSimple(ClientRequestFilter.class.getName());
private static final DotName CLIENT_RESPONSE_FILTER = DotName.createSimple(ClientResponseFilter.class.getName());
private static final String PROVIDERS_SERVICE_FILE = "META-INF/services/" + Providers.class.getName();
@BuildStep
@@ -341,6 +344,15 @@ class RestClientProcessor {
reflectiveClass
.produce(new ReflectiveClassBuildItem(false, false, annotationInstance.value().asClass().toString()));
}
// now retain all un-annotated implementations of ClientRequestFilter and ClientResponseFilter
// in case they are programmatically registered by applications
for (ClassInfo info : index.getAllKnownImplementors(CLIENT_REQUEST_FILTER)) {
reflectiveClass.produce(new ReflectiveClassBuildItem(false, false, info.name().toString()));
}
for (ClassInfo info : index.getAllKnownImplementors(CLIENT_RESPONSE_FILTER)) {
reflectiveClass.produce(new ReflectiveClassBuildItem(false, false, info.name().toString()));
}
}
private boolean isRestClientInterface(IndexView index, ClassInfo classInfo) {

View File

@@ -18,11 +18,16 @@ import io.quarkus.runtime.annotations.Recorder;
@Recorder
public class RestClientRecorder {
public static ResteasyProviderFactory providerFactory;
public static boolean SSL_ENABLED;
public void setRestClientBuilderResolver() {
RestClientBuilderResolver.setInstance(new BuilderResolver());
}
public void setSslEnabled(boolean sslEnabled) {
SSL_ENABLED = sslEnabled;
RestClientBuilderImpl.setSslEnabled(sslEnabled);
}
@@ -57,6 +62,7 @@ public class RestClientRecorder {
}
RestClientBuilderImpl.setProviderFactory(clientProviderFactory);
providerFactory = clientProviderFactory;
}
private static void registerProviders(ResteasyProviderFactory clientProviderFactory, Set<String> providersToRegister,

View File

@@ -2,16 +2,29 @@ package io.quarkus.restclient.runtime.graal;
import javax.ws.rs.client.ClientBuilder;
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
import org.jboss.resteasy.client.jaxrs.engines.URLConnectionClientEngineBuilder;
import org.jboss.resteasy.client.jaxrs.internal.LocalResteasyProviderFactory;
import org.jboss.resteasy.client.jaxrs.internal.ResteasyClientBuilderImpl;
import com.oracle.svm.core.annotate.Substitute;
import com.oracle.svm.core.annotate.TargetClass;
import io.quarkus.restclient.runtime.RestClientRecorder;
@TargetClass(ClientBuilder.class)
final class ClientBuilderReplacement {
@Substitute
public static ClientBuilder newBuilder() {
return new ResteasyClientBuilderImpl();
ResteasyClientBuilder client = new ResteasyClientBuilderImpl();
client.providerFactory(new LocalResteasyProviderFactory(RestClientRecorder.providerFactory));
if (!RestClientRecorder.SSL_ENABLED) {
client.httpEngine(new URLConnectionClientEngineBuilder().resteasyClientBuilder(client).build());
client.sslContext(null);
client.trustStore(null);
client.keyStore(null, "");
}
return client;
}
}

View File

@@ -0,0 +1,31 @@
package io.quarkus.it.rest;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Produces;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
@ApplicationScoped
public class ClientProducer {
private Client client;
@PostConstruct
void init() {
client = ClientBuilder.newClient().register(LoggingFilter.class);
}
@PreDestroy
void close() {
if (client != null) {
client.close();
}
}
@Produces
@ApplicationScoped
public Client getClient() {
return client;
}
}

View File

@@ -6,11 +6,15 @@ import java.util.Map;
import java.util.concurrent.CompletionStage;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.client.Client;
import javax.ws.rs.core.MediaType;
import org.eclipse.microprofile.config.ConfigProvider;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.rest.client.RestClientBuilder;
import org.eclipse.microprofile.rest.client.inject.RestClient;
@@ -35,6 +39,13 @@ public class ClientResource {
@RestClient
RestClientBaseUriConfigKeyInterface restClientBaseUriConfigKeyInterface;
@Inject
@ConfigProperty(name = "loopback/mp-rest/url", defaultValue = "http://localhost:8080/loopback")
Provider<String> loopbackEndpoint;
@Inject
Client client;
@GET
@Path("/annotation/configKey")
public String configKey() {
@@ -140,4 +151,32 @@ public class ClientResource {
public String getDefaultInterfaceScope() {
return Arc.container().instance(RestClientInterface.class, RestClient.LITERAL).getBean().getScope().getName();
}
@GET
@Path("/jaxrs-client")
@Produces(MediaType.APPLICATION_JSON)
public Greeting testJaxrsClient() throws ClassNotFoundException {
Greeting greeting = client.target(loopbackEndpoint.get())
.request()
.get(Greeting.class);
// The LoggingFilter should be programmatically registered in io.quarkus.it.rest.ClientProducer.init()
if (!client.getConfiguration().isRegistered(Class.forName("io.quarkus.it.rest.LoggingFilter")))
throw new IllegalStateException("LoggingFilter should be registered on injected Client");
if (getFilterCount() != 2)
throw new IllegalStateException("Call count should have been 2 but was " + getFilterCount());
return greeting;
}
private int getFilterCount() {
try {
// Must use reflection to check filter call count to ensure that
// completely decoupled filters are not removed in native mode
return Class.forName("io.quarkus.it.rest.LoggingFilter")
.getDeclaredField("CALL_COUNT")
.getInt(null);
} catch (Exception e) {
e.printStackTrace();
return -1;
}
}
}

View File

@@ -0,0 +1,26 @@
package io.quarkus.it.rest;
import java.time.LocalDate;
import javax.json.bind.annotation.JsonbCreator;
import javax.json.bind.annotation.JsonbProperty;
public class Greeting {
private final String message;
private final LocalDate date;
@JsonbCreator
public Greeting(@JsonbProperty("message") String message, @JsonbProperty("date") LocalDate date) {
this.message = message;
this.date = date;
}
public String getMessage() {
return message;
}
public LocalDate getDate() {
return date;
}
}

View File

@@ -0,0 +1,33 @@
package io.quarkus.it.rest;
import java.io.IOException;
import javax.ws.rs.client.ClientRequestContext;
import javax.ws.rs.client.ClientRequestFilter;
import javax.ws.rs.client.ClientResponseContext;
import javax.ws.rs.client.ClientResponseFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LoggingFilter
implements ClientRequestFilter, ClientResponseFilter {
private final Logger log = LoggerFactory.getLogger(getClass());
public static int CALL_COUNT = 0;
@Override
public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext)
throws IOException {
CALL_COUNT++;
log.info("<< {} {}", responseContext.getStatusInfo().getStatusCode(),
responseContext.getStatusInfo().getReasonPhrase());
}
@Override
public void filter(ClientRequestContext requestContext)
throws IOException {
CALL_COUNT++;
log.info(">> {} {}", requestContext.getMethod(), requestContext.getUri());
}
}

View File

@@ -0,0 +1,19 @@
package io.quarkus.it.rest;
import java.time.LocalDate;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
@Path("/loopback")
public class LoopbackResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
public Greeting hello() {
return new Greeting("hello self", LocalDate.of(2020, 02, 13));
}
}

View File

@@ -3,6 +3,7 @@ io.quarkus.it.rest.RestInterface/mp-rest/scope=javax.inject.Singleton
io.quarkus.it.rest.RestClientInterface/mp-rest/url=${test.url}
restClientConfigKey/mp-rest/url=${test.url}
restClientBaseUriConfigKey/mp-rest/url=${test.url}
loopback/mp-rest/url=${test.url}/loopback
org.eclipse.microprofile.rest.client.propagateHeaders=header-name
# Disabled by default as it establishes external connections.

View File

@@ -1,5 +1,7 @@
package io.quarkus.it.main;
import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.Matchers.*;
import java.util.List;
@@ -118,6 +120,16 @@ public class RestClientTestCase {
Assertions.assertEquals("javax.enterprise.context.Dependent", responseWithDefaultScope);
}
@Test
void testJaxrsClientWithFilters() {
given()
.when().get("/client/jaxrs-client")
.then()
.statusCode(200)
.body(containsString("hello"))
.body(containsString("2020-02-13"));
}
@Test
@Disabled("Disabled by default as it establishes external connections, uncomment when you want to test SSL support")
public void testDegradedSslSupport() {