From 29181b3ef439a5bd082810252130e93dfef55839 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Mon, 28 Aug 2023 11:23:50 +0300 Subject: [PATCH 1/2] Warn about missing JSON extension when using REST Client --- .../io/quarkus/deployment/Capability.java | 4 +- .../JaxrsClientReactiveProcessor.java | 2 +- .../QuarkusClientEndpointIndexer.java | 84 +++++++++++++++++ .../QuarkusServerEndpointIndexer.java | 90 +++---------------- .../runtime/pom.xml | 2 +- .../runtime/pom.xml | 5 ++ .../scanning/ClientEndpointIndexer.java | 15 ++-- .../common/processor/EndpointIndexer.java | 83 +++++++++++++++++ 8 files changed, 197 insertions(+), 88 deletions(-) create mode 100644 extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/QuarkusClientEndpointIndexer.java diff --git a/core/deployment/src/main/java/io/quarkus/deployment/Capability.java b/core/deployment/src/main/java/io/quarkus/deployment/Capability.java index d0830d76d35b8..4d0fd6495051c 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/Capability.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/Capability.java @@ -44,7 +44,9 @@ public interface Capability { String REST = QUARKUS_PREFIX + ".rest"; String REST_CLIENT = REST + ".client"; String REST_CLIENT_REACTIVE = REST_CLIENT + ".reactive"; - String REST_CLIENT_REACTIVE_JACKSON = REST_CLIENT_REACTIVE + ".jackson"; + String REST_CLIENT_REACTIVE_JSON = REST_CLIENT + ".reactive.json"; + String REST_CLIENT_REACTIVE_JACKSON = REST_CLIENT_REACTIVE_JSON + ".jackson"; + String REST_CLIENT_REACTIVE_JSONB = REST_CLIENT_REACTIVE_JSON + ".jsonb"; String REST_JACKSON = REST + ".jackson"; String REST_JSONB = REST + ".jsonb"; diff --git a/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java b/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java index 7f8fcbc4577b7..6ee601bf8e874 100644 --- a/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java +++ b/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java @@ -295,7 +295,7 @@ void setupClientProxies(JaxrsClientReactiveRecorder recorder, AdditionalWriters additionalWriters = new AdditionalWriters(); IndexView index = beanArchiveIndexBuildItem.getIndex(); - ClientEndpointIndexer clientEndpointIndexer = new ClientEndpointIndexer.Builder() + ClientEndpointIndexer clientEndpointIndexer = new QuarkusClientEndpointIndexer.Builder(capabilities) .setIndex(index) .setApplicationIndex(applicationIndexBuildItem.getIndex()) .setExistingConverters(new HashMap<>()) diff --git a/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/QuarkusClientEndpointIndexer.java b/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/QuarkusClientEndpointIndexer.java new file mode 100644 index 0000000000000..d0a65afef4722 --- /dev/null +++ b/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/QuarkusClientEndpointIndexer.java @@ -0,0 +1,84 @@ +package io.quarkus.jaxrs.client.reactive.deployment; + +import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.DotName; +import org.jboss.jandex.IndexView; +import org.jboss.jandex.MethodInfo; +import org.jboss.jandex.Type; +import org.jboss.logging.Logger; +import org.jboss.resteasy.reactive.client.processor.scanning.ClientEndpointIndexer; +import org.jboss.resteasy.reactive.common.ResteasyReactiveConfig; +import org.jboss.resteasy.reactive.common.model.ResourceMethod; +import org.jboss.resteasy.reactive.common.processor.DefaultProducesHandler; +import org.jboss.resteasy.reactive.common.processor.transformation.AnnotationStore; + +import io.quarkus.deployment.Capabilities; +import io.quarkus.resteasy.reactive.common.deployment.JsonDefaultProducersHandler; + +public class QuarkusClientEndpointIndexer extends ClientEndpointIndexer { + + private static final org.jboss.logging.Logger LOGGER = Logger.getLogger(QuarkusClientEndpointIndexer.class); + + private final JsonDefaultProducersHandler jsonDefaultProducersHandler; + private final Capabilities capabilities; + + QuarkusClientEndpointIndexer(Builder builder, String defaultProduces, boolean smartDefaultProduces) { + super(builder, defaultProduces, smartDefaultProduces); + capabilities = builder.capabilities; + jsonDefaultProducersHandler = new JsonDefaultProducersHandler(); + } + + private DefaultProducesHandler.Context currentDefaultProducesContext; + + @Override + protected void setupApplyDefaults(Type nonAsyncReturnType, DotName httpMethod) { + currentDefaultProducesContext = new DefaultProducesHandler.Context() { + @Override + public Type nonAsyncReturnType() { + return nonAsyncReturnType; + } + + @Override + public DotName httpMethod() { + return httpMethod; + } + + @Override + public IndexView index() { + return applicationIndex; + } + + @Override + public ResteasyReactiveConfig config() { + return config; + } + }; + } + + @Override + protected void handleAdditionalMethodProcessing(ResourceMethod method, ClassInfo currentClassInfo, + MethodInfo info, AnnotationStore annotationStore) { + super.handleAdditionalMethodProcessing(method, currentClassInfo, info, annotationStore); + if (!capabilities.isCapabilityWithPrefixMissing("io.quarkus.rest.client.reactive.json")) { + return; + } + warnAboutMissingJsonProviderIfNeeded(method, info, jsonDefaultProducersHandler, currentDefaultProducesContext); + } + + @Override + protected void logMissingJsonWarning(MethodInfo info) { + LOGGER.warnf("Quarkus detected the use of JSON in REST Client method '" + info.declaringClass().name() + "#" + + info.name() + + "' but no JSON extension has been added. Consider adding 'quarkus-rest-client-reactive-jackson' or 'quarkus-rest-client-reactive-jsonb'."); + } + + public static final class Builder extends AbstractBuilder { + + private final Capabilities capabilities; + + public Builder(Capabilities capabilities) { + this.capabilities = capabilities; + } + + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/QuarkusServerEndpointIndexer.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/QuarkusServerEndpointIndexer.java index fb78eb42bf204..588f7f3caa3b6 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/QuarkusServerEndpointIndexer.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/QuarkusServerEndpointIndexer.java @@ -21,9 +21,7 @@ import org.jboss.resteasy.reactive.common.model.MethodParameter; import org.jboss.resteasy.reactive.common.model.ParameterType; import org.jboss.resteasy.reactive.common.processor.DefaultProducesHandler; -import org.jboss.resteasy.reactive.common.processor.scanning.ResteasyReactiveScanner; import org.jboss.resteasy.reactive.common.processor.scanning.ScannedSerializer; -import org.jboss.resteasy.reactive.common.processor.scanning.SerializerScanningResult; import org.jboss.resteasy.reactive.common.processor.transformation.AnnotationStore; import org.jboss.resteasy.reactive.server.model.ServerResourceMethod; import org.jboss.resteasy.reactive.server.processor.ServerEndpointIndexer; @@ -49,8 +47,6 @@ public class QuarkusServerEndpointIndexer private final JsonDefaultProducersHandler jsonDefaultProducersHandler; private final ResteasyReactiveRecorder resteasyReactiveRecorder; - private SerializerScanningResult serializerScanningResult; - QuarkusServerEndpointIndexer(Builder builder) { super(builder); this.capabilities = builder.capabilities; @@ -152,7 +148,12 @@ public Builder setDefaultProducesHandler(DefaultProducesHandler defaultProducesH protected void handleAdditionalMethodProcessing(ServerResourceMethod method, ClassInfo currentClassInfo, MethodInfo info, AnnotationStore annotationStore) { super.handleAdditionalMethodProcessing(method, currentClassInfo, info, annotationStore); - warnAboutMissingJsonProviderIfNeeded(method, info); + + if (!capabilities.isCapabilityWithPrefixMissing("io.quarkus.resteasy.reactive.json")) { + return; + } + + warnAboutMissingJsonProviderIfNeeded(method, info, jsonDefaultProducersHandler, currentDefaultProducesContext); } @Override @@ -255,80 +256,11 @@ private boolean isServerMessageBodyWriter(ClassInfo classInfo) { return index.getAllKnownImplementors(SERVER_MESSAGE_BODY_WRITER).contains(classInfo); } - private void warnAboutMissingJsonProviderIfNeeded(ServerResourceMethod method, MethodInfo info) { - if (!capabilities.isCapabilityWithPrefixMissing("io.quarkus.resteasy.reactive.json")) { - return; - } - if (hasJson(method) || (hasNoTypesDefined(method) && isDefaultJson())) { - boolean appProvidedJsonReaderExists = appProvidedJsonProviderExists(getSerializerScanningResult().getReaders()); - boolean appProvidedJsonWriterExists = appProvidedJsonProviderExists(getSerializerScanningResult().getWriters()); - if (!appProvidedJsonReaderExists || !appProvidedJsonWriterExists) { - LOGGER.warnf("Quarkus detected the use of JSON in JAX-RS method '" + info.declaringClass().name() + "#" - + info.name() - + "' but no JSON extension has been added. Consider adding 'quarkus-resteasy-reactive-jackson' or 'quarkus-resteasy-reactive-jsonb'."); - } - } - } - - private SerializerScanningResult getSerializerScanningResult() { - if (serializerScanningResult == null) { - serializerScanningResult = ResteasyReactiveScanner.scanForSerializers(index, applicationScanningResult); - } - return serializerScanningResult; - } - - private boolean appProvidedJsonProviderExists(List providers) { - boolean appProvidedJsonReaderExists = false; - for (ScannedSerializer provider : providers) { - for (String mt : provider.getMediaTypeStrings()) { - if (isJson(mt)) { - appProvidedJsonReaderExists = true; - break; - } - } - if (appProvidedJsonReaderExists) { - break; - } - } - return appProvidedJsonReaderExists; - } - - private boolean isDefaultJson() { - List mediaTypes = jsonDefaultProducersHandler.handle(currentDefaultProducesContext); - for (MediaType mediaType : mediaTypes) { - if (isJson(mediaType.toString())) { - return true; - } - } - return false; - } - - private boolean hasJson(ServerResourceMethod method) { - return hasJson(method.getProduces()) || hasJson(method.getConsumes()) || isJson(method.getStreamElementType()); - } - - private boolean hasNoTypesDefined(ServerResourceMethod method) { - return (method.getProduces() == null || method.getProduces().length == 0) && - (method.getConsumes() == null || method.getConsumes().length == 0) && - (method.getStreamElementType() == null); - } - - private boolean hasJson(String[] types) { - if (types == null) { - return false; - } - for (String type : types) { - if (isJson(type)) { - return true; - } - } - return false; + @Override + protected void logMissingJsonWarning(MethodInfo info) { + LOGGER.warnf("Quarkus detected the use of JSON in JAX-RS method '" + info.declaringClass().name() + "#" + + info.name() + + "' but no JSON extension has been added. Consider adding 'quarkus-resteasy-reactive-jackson' or 'quarkus-resteasy-reactive-jsonb'."); } - private boolean isJson(String type) { - if (type == null) { - return false; - } - return type.startsWith(MediaType.APPLICATION_JSON); - } } diff --git a/extensions/resteasy-reactive/rest-client-reactive-jackson/runtime/pom.xml b/extensions/resteasy-reactive/rest-client-reactive-jackson/runtime/pom.xml index 94d0ceb6d052a..eb69023bbcf70 100644 --- a/extensions/resteasy-reactive/rest-client-reactive-jackson/runtime/pom.xml +++ b/extensions/resteasy-reactive/rest-client-reactive-jackson/runtime/pom.xml @@ -34,7 +34,7 @@ quarkus-extension-maven-plugin - io.quarkus.rest.client.reactive.jackson + io.quarkus.rest.client.reactive.json.jackson diff --git a/extensions/resteasy-reactive/rest-client-reactive-jsonb/runtime/pom.xml b/extensions/resteasy-reactive/rest-client-reactive-jsonb/runtime/pom.xml index 81a288d4b6384..e7e2a063c8912 100644 --- a/extensions/resteasy-reactive/rest-client-reactive-jsonb/runtime/pom.xml +++ b/extensions/resteasy-reactive/rest-client-reactive-jsonb/runtime/pom.xml @@ -32,6 +32,11 @@ io.quarkus quarkus-extension-maven-plugin + + + io.quarkus.rest.client.reactive.json.jsonb + + maven-compiler-plugin diff --git a/independent-projects/resteasy-reactive/client/processor/src/main/java/org/jboss/resteasy/reactive/client/processor/scanning/ClientEndpointIndexer.java b/independent-projects/resteasy-reactive/client/processor/src/main/java/org/jboss/resteasy/reactive/client/processor/scanning/ClientEndpointIndexer.java index 550902548acae..1daece4915b77 100644 --- a/independent-projects/resteasy-reactive/client/processor/src/main/java/org/jboss/resteasy/reactive/client/processor/scanning/ClientEndpointIndexer.java +++ b/independent-projects/resteasy-reactive/client/processor/src/main/java/org/jboss/resteasy/reactive/client/processor/scanning/ClientEndpointIndexer.java @@ -53,7 +53,7 @@ public class ClientEndpointIndexer private final String[] defaultProducesNegotiated; private final boolean smartDefaultProduces; - ClientEndpointIndexer(Builder builder, String defaultProduces, boolean smartDefaultProduces) { + public ClientEndpointIndexer(AbstractBuilder builder, String defaultProduces, boolean smartDefaultProduces) { super(builder); this.defaultProduces = new String[] { defaultProduces }; this.defaultProducesNegotiated = new String[] { defaultProduces, MediaType.WILDCARD }; @@ -230,23 +230,26 @@ public static class ClientIndexedParam extends IndexedParameter { + public static abstract class AbstractBuilder> + extends EndpointIndexer.Builder { + private String defaultProduces = MediaType.TEXT_PLAIN; private boolean smartDefaultProduces = true; - public Builder setDefaultProduces(String defaultProduces) { + public B setDefaultProduces(String defaultProduces) { this.defaultProduces = defaultProduces; - return this; + return (B) this; } - public Builder setSmartDefaultProduces(boolean smartDefaultProduces) { + public B setSmartDefaultProduces(boolean smartDefaultProduces) { this.smartDefaultProduces = smartDefaultProduces; - return this; + return (B) this; } @Override public ClientEndpointIndexer build() { return new ClientEndpointIndexer(this, defaultProduces, smartDefaultProduces); } + } } diff --git a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/EndpointIndexer.java b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/EndpointIndexer.java index 5f6ce3d8a8b91..0f4dca20e286b 100644 --- a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/EndpointIndexer.java +++ b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/EndpointIndexer.java @@ -128,6 +128,9 @@ import org.jboss.resteasy.reactive.common.model.ResourceMethod; import org.jboss.resteasy.reactive.common.processor.TargetJavaVersion.Status; import org.jboss.resteasy.reactive.common.processor.scanning.ApplicationScanningResult; +import org.jboss.resteasy.reactive.common.processor.scanning.ResteasyReactiveScanner; +import org.jboss.resteasy.reactive.common.processor.scanning.ScannedSerializer; +import org.jboss.resteasy.reactive.common.processor.scanning.SerializerScanningResult; import org.jboss.resteasy.reactive.common.processor.transformation.AnnotationStore; import org.jboss.resteasy.reactive.common.processor.transformation.AnnotationsTransformer; import org.jboss.resteasy.reactive.common.util.URLUtils; @@ -231,6 +234,7 @@ public abstract class EndpointIndexer> isDisabledCreator; private final Predicate> skipMethodParameter; + private SerializerScanningResult serializerScanningResult; protected EndpointIndexer(Builder builder) { this.index = builder.index; @@ -1568,6 +1572,85 @@ protected String getSeparator(Map annotations) { return result; } + protected boolean hasJson(String[] types) { + if (types == null) { + return false; + } + for (String type : types) { + if (isJson(type)) { + return true; + } + } + return false; + } + + protected boolean isJson(String type) { + if (type == null) { + return false; + } + return type.startsWith(MediaType.APPLICATION_JSON); + } + + protected boolean hasJson(ResourceMethod method) { + return hasJson(method.getProduces()) || hasJson(method.getConsumes()) || isJson(method.getStreamElementType()); + } + + protected boolean hasNoTypesDefined(ResourceMethod method) { + return (method.getProduces() == null || method.getProduces().length == 0) && + (method.getConsumes() == null || method.getConsumes().length == 0) && + (method.getStreamElementType() == null); + } + + protected boolean isDefaultJson(DefaultProducesHandler jsonDefaultProducersHandler, + DefaultProducesHandler.Context context) { + List mediaTypes = jsonDefaultProducersHandler.handle(context); + for (MediaType mediaType : mediaTypes) { + if (isJson(mediaType.toString())) { + return true; + } + } + return false; + } + + protected SerializerScanningResult getSerializerScanningResult() { + if (serializerScanningResult == null) { + serializerScanningResult = ResteasyReactiveScanner.scanForSerializers(index, applicationScanningResult); + } + return serializerScanningResult; + } + + protected void warnAboutMissingJsonProviderIfNeeded(ResourceMethod method, MethodInfo info, + DefaultProducesHandler jsonDefaultProducersHandler, + DefaultProducesHandler.Context context) { + if (hasJson(method) || (hasNoTypesDefined(method) && isDefaultJson(jsonDefaultProducersHandler, context))) { + boolean appProvidedJsonReaderExists = appProvidedJsonProviderExists(getSerializerScanningResult().getReaders()); + boolean appProvidedJsonWriterExists = appProvidedJsonProviderExists(getSerializerScanningResult().getWriters()); + if (!appProvidedJsonReaderExists || !appProvidedJsonWriterExists) { + logMissingJsonWarning(info); + } + } + } + + private boolean appProvidedJsonProviderExists(List providers) { + boolean appProvidedJsonReaderExists = false; + for (ScannedSerializer provider : providers) { + for (String mt : provider.getMediaTypeStrings()) { + if (isJson(mt)) { + appProvidedJsonReaderExists = true; + break; + } + } + if (appProvidedJsonReaderExists) { + break; + } + } + return appProvidedJsonReaderExists; + } + + protected void logMissingJsonWarning(MethodInfo info) { + + } + @SuppressWarnings({ "unchecked", "rawtypes" }) public static abstract class Builder, B extends Builder, METHOD extends ResourceMethod> { private Function> factoryCreator; From c7767a253f7ec56e80ec51f5bcc0181ef0733f69 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Mon, 28 Aug 2023 11:43:41 +0300 Subject: [PATCH 2/2] Make recommendation in missing JSON library warning --- .../reactive/deployment/QuarkusClientEndpointIndexer.java | 2 +- .../server/deployment/QuarkusServerEndpointIndexer.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/QuarkusClientEndpointIndexer.java b/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/QuarkusClientEndpointIndexer.java index d0a65afef4722..fbebc7af0636e 100644 --- a/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/QuarkusClientEndpointIndexer.java +++ b/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/QuarkusClientEndpointIndexer.java @@ -69,7 +69,7 @@ protected void handleAdditionalMethodProcessing(ResourceMethod method, ClassInfo protected void logMissingJsonWarning(MethodInfo info) { LOGGER.warnf("Quarkus detected the use of JSON in REST Client method '" + info.declaringClass().name() + "#" + info.name() - + "' but no JSON extension has been added. Consider adding 'quarkus-rest-client-reactive-jackson' or 'quarkus-rest-client-reactive-jsonb'."); + + "' but no JSON extension has been added. Consider adding 'quarkus-rest-client-reactive-jackson' (recommended) or 'quarkus-rest-client-reactive-jsonb'."); } public static final class Builder extends AbstractBuilder { diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/QuarkusServerEndpointIndexer.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/QuarkusServerEndpointIndexer.java index 588f7f3caa3b6..455e7c3486424 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/QuarkusServerEndpointIndexer.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/QuarkusServerEndpointIndexer.java @@ -260,7 +260,7 @@ private boolean isServerMessageBodyWriter(ClassInfo classInfo) { protected void logMissingJsonWarning(MethodInfo info) { LOGGER.warnf("Quarkus detected the use of JSON in JAX-RS method '" + info.declaringClass().name() + "#" + info.name() - + "' but no JSON extension has been added. Consider adding 'quarkus-resteasy-reactive-jackson' or 'quarkus-resteasy-reactive-jsonb'."); + + "' but no JSON extension has been added. Consider adding 'quarkus-resteasy-reactive-jackson' (recommended) or 'quarkus-resteasy-reactive-jsonb'."); } }