From ce874371fee27ac7c8b0adaea0f85f48acabb854 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20TAMA?= Date: Wed, 2 Dec 2020 23:35:29 +0100 Subject: [PATCH 01/62] Adding picocli codestart --- .../picocli-example/base/README.tpl.qute.md | 12 ++++++ .../examples/picocli-example/codestart.yml | 9 ++++ .../java/org/acme/picocli/EntryCommand.java | 9 ++++ .../java/org/acme/picocli/GoodbyeCommand.java | 21 ++++++++++ .../org/acme/picocli/GreetingService.java | 11 +++++ .../java/org/acme/picocli/HelloCommand.java | 39 ++++++++++++++++++ .../kotlin/org/acme/picocli/EntryCommand.kt | 8 ++++ .../kotlin/org/acme/picocli/GoodbyeCommand.kt | 17 ++++++++ .../org/acme/picocli/GreetingService.kt | 10 +++++ .../kotlin/org/acme/picocli/HelloCommand.kt | 27 ++++++++++++ .../resources/META-INF/quarkus-extension.yaml | 1 + .../QuarkusCodestartGenerationTest.java | 41 +++++++++++++++++++ 12 files changed, 205 insertions(+) create mode 100644 devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/base/README.tpl.qute.md create mode 100644 devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/codestart.yml create mode 100755 devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/java/src/main/java/org/acme/picocli/EntryCommand.java create mode 100644 devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/java/src/main/java/org/acme/picocli/GoodbyeCommand.java create mode 100644 devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/java/src/main/java/org/acme/picocli/GreetingService.java create mode 100644 devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/java/src/main/java/org/acme/picocli/HelloCommand.java create mode 100755 devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/kotlin/src/main/kotlin/org/acme/picocli/EntryCommand.kt create mode 100644 devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/kotlin/src/main/kotlin/org/acme/picocli/GoodbyeCommand.kt create mode 100644 devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/kotlin/src/main/kotlin/org/acme/picocli/GreetingService.kt create mode 100644 devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/kotlin/src/main/kotlin/org/acme/picocli/HelloCommand.kt diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/base/README.tpl.qute.md b/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/base/README.tpl.qute.md new file mode 100644 index 0000000000000..b7316e21ebe37 --- /dev/null +++ b/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/base/README.tpl.qute.md @@ -0,0 +1,12 @@ +# Picocli Example + +Hello and goodbye are civilization fundamentals. Let's not forget it with this picocli application by changing the `command` and `parameters`. + +Also for picocli applications the dev mode is supported. When running dev mode, the picocli application is executed and on press of the Enter key, is restarted. + +As picocli applications will often require arguments to be passed on the commandline, this is also possible in dev mode via: +```shell script +{buildtool.cli} {buildtool.cmd.dev} {#if buildtool.cli == 'gradle'}--quarkus-args{#else}-Dquarkus.args={/if}='help' +``` + +Guide: https://quarkus.io/guides/picocli diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/codestart.yml b/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/codestart.yml new file mode 100644 index 0000000000000..2266fba155f2e --- /dev/null +++ b/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/codestart.yml @@ -0,0 +1,9 @@ +name: picocli-example +ref: picocli +type: code +tags: example +language: + base: + dependencies: + - io.quarkus:quarkus-picocli + diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/java/src/main/java/org/acme/picocli/EntryCommand.java b/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/java/src/main/java/org/acme/picocli/EntryCommand.java new file mode 100755 index 0000000000000..4447b7610861e --- /dev/null +++ b/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/java/src/main/java/org/acme/picocli/EntryCommand.java @@ -0,0 +1,9 @@ +package org.acme.picocli; + +import io.quarkus.picocli.runtime.annotations.TopCommand; +import picocli.CommandLine; + +@TopCommand +@CommandLine.Command(mixinStandardHelpOptions = true, subcommands = { HelloCommand.class, GoodbyeCommand.class }) +public class EntryCommand { +} diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/java/src/main/java/org/acme/picocli/GoodbyeCommand.java b/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/java/src/main/java/org/acme/picocli/GoodbyeCommand.java new file mode 100644 index 0000000000000..653ba227a5540 --- /dev/null +++ b/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/picocli-example/java/src/main/java/org/acme/picocli/GoodbyeCommand.java @@ -0,0 +1,21 @@ +package org.acme.picocli; + +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; + +@Command(name = "goodbye") +public class GoodbyeCommand implements Runnable { + + @Option(names = {"--name"}, description = "Guest name") + String name; + + @Option(names = {"--times", "-t"}, defaultValue = "1", description = "How many time should we say goodbye") + int times; + + @Override + public void run() { + for (int i = 0;i Date: Fri, 27 Nov 2020 11:40:48 +0100 Subject: [PATCH 02/62] Add exposed option to ResourceProperties --- docs/src/main/asciidoc/rest-data-panache.adoc | 5 ++- .../panache/deployment/RestDataProcessor.java | 4 +- .../methods/AddMethodImplementor.java | 2 +- .../methods/DeleteMethodImplementor.java | 2 +- .../methods/GetMethodImplementor.java | 4 +- .../methods/ListMethodImplementor.java | 4 +- .../methods/UpdateMethodImplementor.java | 4 +- .../methods/hal/AddHalMethodImplementor.java | 4 +- .../methods/hal/GetHalMethodImplementor.java | 4 +- .../methods/hal/ListHalMethodImplementor.java | 4 +- .../hal/UpdateHalMethodImplementor.java | 4 +- .../properties/ResourceProperties.java | 38 ++++++++++++----- .../ResourcePropertiesProvider.java | 41 +++++++------------ .../rest/data/panache/ResourceProperties.java | 8 ++++ .../rest/data/panache/AuthorsResource.java | 17 ++++---- 15 files changed, 82 insertions(+), 63 deletions(-) diff --git a/docs/src/main/asciidoc/rest-data-panache.adoc b/docs/src/main/asciidoc/rest-data-panache.adoc index 3bf77a4050fd5..09b42804d8d46 100644 --- a/docs/src/main/asciidoc/rest-data-panache.adoc +++ b/docs/src/main/asciidoc/rest-data-panache.adoc @@ -163,13 +163,14 @@ public interface PeopleResource extends PanacheEntityResource { `@ResourceProperties` -* `hal` - in addition to the standard `application/json` responses, generates additional methods that can return `application/hal+json` responses if requested via an `Accept` header. -Default is `false`. +* `exposed` - whether resource could be exposed. A global resource property that can be overridden for each method. Default is `true`. * `path` - resource base path. Default path is a hyphenated lowercase resource name without a suffix of `resource` or `controller`. * `paged` - whether collection responses should be paged or not. First, last, previous and next page URIs are included in the response headers if they exist. Request page index and size are taken from the `page` and `size` query parameters that default to `0` and `20` respectively. Default is `true`. +* `hal` - in addition to the standard `application/json` responses, generates additional methods that can return `application/hal+json` responses if requested via an `Accept` header. +Default is `false`. `@MethodProperties` diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/RestDataProcessor.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/RestDataProcessor.java index 61bc75a1d8ee2..082c361d05b35 100644 --- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/RestDataProcessor.java +++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/RestDataProcessor.java @@ -41,7 +41,9 @@ void implementResources(CombinedIndexBuildItem index, List * {@code * @GET @@ -51,7 +51,7 @@ protected void implementInternal(ClassCreator classCreator, ResourceMetadata res resourceMetadata.getIdType()); // Add method annotations - addPathAnnotation(methodCreator, appendToPath(resourceProperties.getMethodPath(RESOURCE_METHOD_NAME), "{id}")); + addPathAnnotation(methodCreator, appendToPath(resourceProperties.getPath(RESOURCE_METHOD_NAME), "{id}")); addGetAnnotation(methodCreator); addProducesAnnotation(methodCreator, APPLICATION_JSON); addPathParamAnnotation(methodCreator.getParameterAnnotations(0), "id"); diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/ListMethodImplementor.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/ListMethodImplementor.java index cd680aec3fcd3..b06d5a4afcd9a 100644 --- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/ListMethodImplementor.java +++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/ListMethodImplementor.java @@ -87,7 +87,7 @@ private void implementPaged(ClassCreator classCreator, ResourceMetadata resource // Add method annotations addGetAnnotation(methodCreator); - addPathAnnotation(methodCreator, resourceProperties.getMethodPath(RESOURCE_METHOD_NAME)); + addPathAnnotation(methodCreator, resourceProperties.getPath(RESOURCE_METHOD_NAME)); addProducesAnnotation(methodCreator, APPLICATION_JSON); addLinksAnnotation(methodCreator, resourceMetadata.getEntityType(), REL); addSortQueryParamValidatorAnnotation(methodCreator); @@ -127,7 +127,7 @@ private void implementNotPaged(ClassCreator classCreator, ResourceMetadata resou // Add method annotations addGetAnnotation(methodCreator); - addPathAnnotation(methodCreator, resourceProperties.getMethodPath(RESOURCE_METHOD_NAME)); + addPathAnnotation(methodCreator, resourceProperties.getPath(RESOURCE_METHOD_NAME)); addProducesAnnotation(methodCreator, APPLICATION_JSON); addLinksAnnotation(methodCreator, resourceMetadata.getEntityType(), REL); addQueryParamAnnotation(methodCreator.getParameterAnnotations(0), "sort"); diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/UpdateMethodImplementor.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/UpdateMethodImplementor.java index 96f0ba0f5df83..ddecbb9813165 100644 --- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/UpdateMethodImplementor.java +++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/UpdateMethodImplementor.java @@ -29,7 +29,7 @@ public final class UpdateMethodImplementor extends StandardMethodImplementor { * Generate JAX-RS UPDATE method that exposes {@link RestDataResource#update(Object, Object)}. * Expose {@link RestDataResource#update(Object, Object)} via JAX-RS method. * Generated code looks more or less like this: - * + * *
      * {@code
      *     @Transactional
@@ -69,7 +69,7 @@ protected void implementInternal(ClassCreator classCreator, ResourceMetadata res
 
         // Add method annotations
         addPathAnnotation(methodCreator,
-                appendToPath(resourceProperties.getMethodPath(RESOURCE_UPDATE_METHOD_NAME), "{id}"));
+                appendToPath(resourceProperties.getPath(RESOURCE_UPDATE_METHOD_NAME), "{id}"));
         addTransactionalAnnotation(methodCreator);
         addPutAnnotation(methodCreator);
         addPathParamAnnotation(methodCreator.getParameterAnnotations(0), "id");
diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/hal/AddHalMethodImplementor.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/hal/AddHalMethodImplementor.java
index 027368138ef77..1e4aac9db311b 100644
--- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/hal/AddHalMethodImplementor.java
+++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/hal/AddHalMethodImplementor.java
@@ -22,7 +22,7 @@ public final class AddHalMethodImplementor extends HalMethodImplementor {
     /**
      * Expose {@link RestDataResource#add(Object)} via HAL JAX-RS method.
      * Generated code looks more or less like this:
-     * 
+     *
      * 
      * {@code
      *     @Transactional
@@ -53,7 +53,7 @@ protected void implementInternal(ClassCreator classCreator, ResourceMetadata res
                 resourceMetadata.getEntityType());
 
         // Add method annotations
-        addPathAnnotation(methodCreator, resourceProperties.getMethodPath(RESOURCE_METHOD_NAME));
+        addPathAnnotation(methodCreator, resourceProperties.getPath(RESOURCE_METHOD_NAME));
         addTransactionalAnnotation(methodCreator);
         addPostAnnotation(methodCreator);
         addConsumesAnnotation(methodCreator, APPLICATION_JSON);
diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/hal/GetHalMethodImplementor.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/hal/GetHalMethodImplementor.java
index 71ec7493f483f..671d8f7d212ee 100644
--- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/hal/GetHalMethodImplementor.java
+++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/hal/GetHalMethodImplementor.java
@@ -22,7 +22,7 @@ public final class GetHalMethodImplementor extends HalMethodImplementor {
     /**
      * Expose {@link RestDataResource#get(Object)} via HAL JAX-RS method.
      * Generated code looks more or less like this:
-     * 
+     *
      * 
      * {@code
      *     @GET
@@ -46,7 +46,7 @@ protected void implementInternal(ClassCreator classCreator, ResourceMetadata res
                 resourceMetadata.getIdType());
 
         // Add method annotations
-        addPathAnnotation(methodCreator, appendToPath(resourceProperties.getMethodPath(RESOURCE_METHOD_NAME), "{id}"));
+        addPathAnnotation(methodCreator, appendToPath(resourceProperties.getPath(RESOURCE_METHOD_NAME), "{id}"));
         addGetAnnotation(methodCreator);
         addProducesAnnotation(methodCreator, APPLICATION_HAL_JSON);
         addPathParamAnnotation(methodCreator.getParameterAnnotations(0), "id");
diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/hal/ListHalMethodImplementor.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/hal/ListHalMethodImplementor.java
index b225a59b6efd0..18455972f33bf 100644
--- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/hal/ListHalMethodImplementor.java
+++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/hal/ListHalMethodImplementor.java
@@ -85,7 +85,7 @@ private void implementPaged(ClassCreator classCreator, ResourceMetadata resource
                 int.class, UriInfo.class);
 
         // Add method annotations
-        addPathAnnotation(methodCreator, resourceProperties.getMethodPath(RESOURCE_METHOD_NAME));
+        addPathAnnotation(methodCreator, resourceProperties.getPath(RESOURCE_METHOD_NAME));
         addGetAnnotation(methodCreator);
         addProducesAnnotation(methodCreator, APPLICATION_HAL_JSON);
         addSortQueryParamValidatorAnnotation(methodCreator);
@@ -129,7 +129,7 @@ private void implementNotPaged(ClassCreator classCreator, ResourceMetadata resou
         MethodCreator methodCreator = classCreator.getMethodCreator(METHOD_NAME, Response.class, List.class);
 
         // Add method annotations
-        addPathAnnotation(methodCreator, resourceProperties.getMethodPath(RESOURCE_METHOD_NAME));
+        addPathAnnotation(methodCreator, resourceProperties.getPath(RESOURCE_METHOD_NAME));
         addGetAnnotation(methodCreator);
         addProducesAnnotation(methodCreator, APPLICATION_HAL_JSON);
         addQueryParamAnnotation(methodCreator.getParameterAnnotations(0), "sort");
diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/hal/UpdateHalMethodImplementor.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/hal/UpdateHalMethodImplementor.java
index f680069007980..ab508c867b00f 100644
--- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/hal/UpdateHalMethodImplementor.java
+++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/hal/UpdateHalMethodImplementor.java
@@ -26,7 +26,7 @@ public final class UpdateHalMethodImplementor extends HalMethodImplementor {
     /**
      * Expose {@link RestDataResource#update(Object, Object)} via HAL JAX-RS method.
      * Generated code looks more or less like this:
-     * 
+     *
      * 
      * {@code
      *     @Transactional
@@ -63,7 +63,7 @@ protected void implementInternal(ClassCreator classCreator, ResourceMetadata res
 
         // Add method annotations
         addPathAnnotation(methodCreator,
-                appendToPath(resourceProperties.getMethodPath(RESOURCE_UPDATE_METHOD_NAME), "{id}"));
+                appendToPath(resourceProperties.getPath(RESOURCE_UPDATE_METHOD_NAME), "{id}"));
         addTransactionalAnnotation(methodCreator);
         addPutAnnotation(methodCreator);
         addPathParamAnnotation(methodCreator.getParameterAnnotations(0), "id");
diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/properties/ResourceProperties.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/properties/ResourceProperties.java
index 5fe5eccac7f59..4318e021166a9 100644
--- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/properties/ResourceProperties.java
+++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/properties/ResourceProperties.java
@@ -4,34 +4,53 @@
 
 public class ResourceProperties {
 
-    private final boolean hal;
+    private final boolean exposed;
 
     private final String path;
 
     private final boolean paged;
 
+    private final boolean hal;
+
     private final String halCollectionName;
 
     private final Map methodProperties;
 
-    public ResourceProperties(boolean hal, String path, boolean paged, String halCollectionName,
+    public ResourceProperties(boolean exposed, String path, boolean paged, boolean hal, String halCollectionName,
             Map methodProperties) {
-        this.hal = hal;
+        this.exposed = exposed;
         this.path = path;
         this.paged = paged;
+        this.hal = hal;
         this.halCollectionName = halCollectionName;
         this.methodProperties = methodProperties;
     }
 
-    public boolean isHal() {
-        return hal;
+    public boolean isExposed() {
+        if (exposed) {
+            return true;
+        }
+        // If at least one method is explicitly exposed, resource should also be exposed
+        for (MethodProperties properties : this.methodProperties.values()) {
+            if (properties.isExposed()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public boolean isExposed(String methodName) {
+        if (methodProperties.containsKey(methodName)) {
+            return methodProperties.get(methodName).isExposed();
+        }
+        return exposed;
     }
 
     public String getPath() {
         return path;
     }
 
-    public String getMethodPath(String methodName) {
+    public String getPath(String methodName) {
         if (methodProperties.containsKey(methodName)) {
             return methodProperties.get(methodName).getPath();
         }
@@ -42,11 +61,8 @@ public boolean isPaged() {
         return paged;
     }
 
-    public boolean isExposed(String methodName) {
-        if (methodProperties.containsKey(methodName)) {
-            return methodProperties.get(methodName).isExposed();
-        }
-        return true;
+    public boolean isHal() {
+        return hal;
     }
 
     public String getHalCollectionName() {
diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/properties/ResourcePropertiesProvider.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/properties/ResourcePropertiesProvider.java
index c8b008067e378..13fc2fd1bcc80 100644
--- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/properties/ResourcePropertiesProvider.java
+++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/properties/ResourcePropertiesProvider.java
@@ -1,9 +1,7 @@
 package io.quarkus.rest.data.panache.deployment.properties;
 
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Map;
-import java.util.Set;
 
 import org.jboss.jandex.AnnotationInstance;
 import org.jboss.jandex.ClassInfo;
@@ -34,10 +32,16 @@ public ResourcePropertiesProvider(IndexView index) {
     public ResourceProperties getForInterface(String resourceInterface) {
         DotName resourceInterfaceName = DotName.createSimple(resourceInterface);
         AnnotationInstance annotation = findResourcePropertiesAnnotation(resourceInterfaceName);
-
-        return new ResourceProperties(isHal(annotation), getPath(annotation, resourceInterface),
-                isPaged(annotation), getHalCollectionName(annotation, resourceInterface),
-                getMethodPropertiesInfoMap(resourceInterfaceName));
+        Map methodProperties = new HashMap<>();
+        collectMethodProperties(resourceInterfaceName, methodProperties);
+
+        return new ResourceProperties(
+                isExposed(annotation),
+                getPath(annotation, resourceInterface),
+                isPaged(annotation),
+                isHal(annotation),
+                getHalCollectionName(annotation, resourceInterface),
+                methodProperties);
     }
 
     private AnnotationInstance findResourcePropertiesAnnotation(DotName className) {
@@ -54,37 +58,22 @@ private AnnotationInstance findResourcePropertiesAnnotation(DotName className) {
         return null;
     }
 
-    private Map getMethodPropertiesInfoMap(DotName className) {
-        Set methodNames = new HashSet<>();
-        Map annotations = new HashMap<>();
-        collectMethods(className, methodNames, annotations);
-
-        Map methodPropertiesInfoMap = new HashMap<>();
-        for (String methodName : methodNames) {
-            methodPropertiesInfoMap.put(methodName, getMethodPropertiesInfo(annotations.get(methodName)));
-        }
-
-        return methodPropertiesInfoMap;
-    }
-
-    private void collectMethods(DotName className, Set methodNames,
-            Map annotations) {
+    private void collectMethodProperties(DotName className, Map properties) {
         ClassInfo classInfo = index.getClassByName(className);
         if (classInfo == null) {
             return;
         }
         for (MethodInfo method : classInfo.methods()) {
-            if (method.hasAnnotation(METHOD_PROPERTIES_ANNOTATION)) {
-                annotations.putIfAbsent(method.name(), method.annotation(METHOD_PROPERTIES_ANNOTATION));
+            if (!properties.containsKey(method.name()) && method.hasAnnotation(METHOD_PROPERTIES_ANNOTATION)) {
+                properties.put(method.name(), getMethodProperties(method.annotation(METHOD_PROPERTIES_ANNOTATION)));
             }
-            methodNames.add(method.name());
         }
         if (classInfo.superName() != null) {
-            collectMethods(classInfo.superName(), methodNames, annotations);
+            collectMethodProperties(classInfo.superName(), properties);
         }
     }
 
-    private MethodProperties getMethodPropertiesInfo(AnnotationInstance annotation) {
+    private MethodProperties getMethodProperties(AnnotationInstance annotation) {
         return new MethodProperties(isExposed(annotation), getPath(annotation));
     }
 
diff --git a/extensions/panache/rest-data-panache/runtime/src/main/java/io/quarkus/rest/data/panache/ResourceProperties.java b/extensions/panache/rest-data-panache/runtime/src/main/java/io/quarkus/rest/data/panache/ResourceProperties.java
index 21bf3879f11e9..e412941cf10c3 100644
--- a/extensions/panache/rest-data-panache/runtime/src/main/java/io/quarkus/rest/data/panache/ResourceProperties.java
+++ b/extensions/panache/rest-data-panache/runtime/src/main/java/io/quarkus/rest/data/panache/ResourceProperties.java
@@ -15,6 +15,14 @@
 @Target({ TYPE })
 public @interface ResourceProperties {
 
+    /**
+     * Whether this resource operations should be exposed as JAX-RS methods by default.
+     * Separate methods can override this setting by using {@link MethodProperties} annotation.
+     * 

+ * Default: true. + */ + boolean exposed() default true; + /** * URL path segment that should be used to access the resources. * This path segment is prepended to the segments specified with the {@link MethodProperties} annotations used on this diff --git a/integration-tests/hibernate-orm-rest-data-panache/src/main/java/io/quarkus/it/hibernate/orm/rest/data/panache/AuthorsResource.java b/integration-tests/hibernate-orm-rest-data-panache/src/main/java/io/quarkus/it/hibernate/orm/rest/data/panache/AuthorsResource.java index f90e4dd22d815..44a015bbf5016 100644 --- a/integration-tests/hibernate-orm-rest-data-panache/src/main/java/io/quarkus/it/hibernate/orm/rest/data/panache/AuthorsResource.java +++ b/integration-tests/hibernate-orm-rest-data-panache/src/main/java/io/quarkus/it/hibernate/orm/rest/data/panache/AuthorsResource.java @@ -1,16 +1,19 @@ package io.quarkus.it.hibernate.orm.rest.data.panache; +import java.util.List; + import io.quarkus.hibernate.orm.rest.data.panache.PanacheEntityResource; +import io.quarkus.panache.common.Page; +import io.quarkus.panache.common.Sort; import io.quarkus.rest.data.panache.MethodProperties; +import io.quarkus.rest.data.panache.ResourceProperties; +@ResourceProperties(exposed = false) public interface AuthorsResource extends PanacheEntityResource { - @MethodProperties(exposed = false) - Author add(Author entity); - - @MethodProperties(exposed = false) - Author update(Long id, Author entity); + @MethodProperties + List list(Page page, Sort sort); - @MethodProperties(exposed = false) - boolean delete(Long id); + @MethodProperties + Author get(Long id); } From 19edc8304dcc1dbb97de56a481f363ad253779ae Mon Sep 17 00:00:00 2001 From: Zach Kimberg Date: Tue, 27 Oct 2020 16:02:08 -0700 Subject: [PATCH 03/62] Add RuntimeInitializedPackageBuildItem --- .../RuntimeInitializedClassBuildItem.java | 3 +++ .../RuntimeInitializedPackageBuildItem.java | 19 +++++++++++++++++ .../steps/NativeImageAutoFeatureStep.java | 21 +++++++++++++++++-- 3 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/RuntimeInitializedPackageBuildItem.java diff --git a/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/RuntimeInitializedClassBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/RuntimeInitializedClassBuildItem.java index 61ba6bfeb7f70..484dec11f422c 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/RuntimeInitializedClassBuildItem.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/RuntimeInitializedClassBuildItem.java @@ -2,6 +2,9 @@ import io.quarkus.builder.item.MultiBuildItem; +/** + * A class that will be initialized at runtime in native mode. + */ public final class RuntimeInitializedClassBuildItem extends MultiBuildItem { private final String className; diff --git a/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/RuntimeInitializedPackageBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/RuntimeInitializedPackageBuildItem.java new file mode 100644 index 0000000000000..29c2cde897ca1 --- /dev/null +++ b/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/RuntimeInitializedPackageBuildItem.java @@ -0,0 +1,19 @@ +package io.quarkus.deployment.builditem.nativeimage; + +import io.quarkus.builder.item.MultiBuildItem; + +/** + * A package that will be initialized at runtime in native mode. + */ +public final class RuntimeInitializedPackageBuildItem extends MultiBuildItem { + + private final String packageName; + + public RuntimeInitializedPackageBuildItem(String packageName) { + this.packageName = packageName; + } + + public String getPackageName() { + return packageName; + } +} diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageAutoFeatureStep.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageAutoFeatureStep.java index 42f9649dde5b9..5a857915e99a3 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageAutoFeatureStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageAutoFeatureStep.java @@ -31,6 +31,7 @@ import io.quarkus.deployment.builditem.nativeimage.ReflectiveFieldBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveMethodBuildItem; import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem; +import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedPackageBuildItem; import io.quarkus.deployment.builditem.nativeimage.RuntimeReinitializedClassBuildItem; import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem; import io.quarkus.deployment.builditem.nativeimage.UnsafeAccessedFieldBuildItem; @@ -48,8 +49,10 @@ public class NativeImageAutoFeatureStep { private static final String GRAAL_AUTOFEATURE = "io/quarkus/runner/AutoFeature"; private static final MethodDescriptor IMAGE_SINGLETONS_LOOKUP = ofMethod(ImageSingletons.class, "lookup", Object.class, Class.class); - private static final MethodDescriptor INITIALIZE_AT_RUN_TIME = ofMethod(RuntimeClassInitialization.class, + private static final MethodDescriptor INITIALIZE_CLASSES_AT_RUN_TIME = ofMethod(RuntimeClassInitialization.class, "initializeAtRunTime", void.class, Class[].class); + private static final MethodDescriptor INITIALIZE_PACKAGES_AT_RUN_TIME = ofMethod(RuntimeClassInitialization.class, + "initializeAtRunTime", void.class, String[].class); private static final MethodDescriptor RERUN_INITIALIZATION = ofMethod( "org.graalvm.nativeimage.impl.RuntimeClassInitializationSupport", "rerunInitialization", void.class, Class.class, String.class); @@ -68,6 +71,7 @@ public class NativeImageAutoFeatureStep { @BuildStep void generateFeature(BuildProducer nativeImageClass, List runtimeInitializedClassBuildItems, + List runtimeInitializedPackageBuildItems, List runtimeReinitializedClassBuildItems, List proxies, List resources, @@ -124,7 +128,20 @@ public void write(String s, byte[] bytes) { CatchBlockCreator cc = tc.addCatch(Throwable.class); cc.invokeVirtualMethod(ofMethod(Throwable.class, "printStackTrace", void.class), cc.getCaughtException()); } - overallCatch.invokeStaticMethod(INITIALIZE_AT_RUN_TIME, classes); + overallCatch.invokeStaticMethod(INITIALIZE_CLASSES_AT_RUN_TIME, classes); + } + + if (!runtimeInitializedPackageBuildItems.isEmpty()) { + ResultHandle packages = overallCatch.newArray(String.class, + overallCatch.load(runtimeInitializedPackageBuildItems.size())); + for (int i = 0; i < runtimeInitializedPackageBuildItems.size(); i++) { + TryBlock tc = overallCatch.tryBlock(); + ResultHandle pkg = tc.load(runtimeInitializedPackageBuildItems.get(i).getPackageName()); + tc.writeArrayValue(packages, i, pkg); + CatchBlockCreator cc = tc.addCatch(Throwable.class); + cc.invokeVirtualMethod(ofMethod(Throwable.class, "printStackTrace", void.class), cc.getCaughtException()); + } + overallCatch.invokeStaticMethod(INITIALIZE_PACKAGES_AT_RUN_TIME, packages); } // hack in reinitialization of process info classes From 9ff7e16088dc07043556c51269a502fea9292a20 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Mon, 7 Dec 2020 18:49:58 +0100 Subject: [PATCH 04/62] Add a proper warning in RuntimeInitializedPackageBuildItem And also exercise the bytecode in the test extension. --- .../nativeimage/RuntimeInitializedPackageBuildItem.java | 5 +++++ .../java/io/quarkus/extest/deployment/TestProcessor.java | 7 +++++++ .../runtimeinitializedpackage/RuntimeInitializedClass.java | 5 +++++ 3 files changed, 17 insertions(+) create mode 100644 core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/runtimeinitializedpackage/RuntimeInitializedClass.java diff --git a/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/RuntimeInitializedPackageBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/RuntimeInitializedPackageBuildItem.java index 29c2cde897ca1..25e9ccdcf7fb6 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/RuntimeInitializedPackageBuildItem.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/RuntimeInitializedPackageBuildItem.java @@ -4,6 +4,11 @@ /** * A package that will be initialized at runtime in native mode. + *

+ * WARNING: this build item should not be used in Quarkus itself and is only provided + * to simplify the early stages of external extensions development. + *

+ * For Quarkus development, please take the time to surgically mark individual classes as runtime initialized. */ public final class RuntimeInitializedPackageBuildItem extends MultiBuildItem { diff --git a/core/test-extension/deployment/src/main/java/io/quarkus/extest/deployment/TestProcessor.java b/core/test-extension/deployment/src/main/java/io/quarkus/extest/deployment/TestProcessor.java index 450146949f3c3..e93eea246e5ff 100644 --- a/core/test-extension/deployment/src/main/java/io/quarkus/extest/deployment/TestProcessor.java +++ b/core/test-extension/deployment/src/main/java/io/quarkus/extest/deployment/TestProcessor.java @@ -51,6 +51,7 @@ import io.quarkus.deployment.builditem.ShutdownContextBuildItem; import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; +import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedPackageBuildItem; import io.quarkus.extest.runtime.FinalFieldReflectionObject; import io.quarkus.extest.runtime.IConfigConsumer; import io.quarkus.extest.runtime.RuntimeXmlConfigService; @@ -67,6 +68,7 @@ import io.quarkus.extest.runtime.config.TestRunTimeConfig; import io.quarkus.extest.runtime.config.XmlConfig; import io.quarkus.extest.runtime.logging.AdditionalLogHandlerValueFactory; +import io.quarkus.extest.runtime.runtimeinitializedpackage.RuntimeInitializedClass; import io.quarkus.extest.runtime.subst.DSAPublicKeyObjectSubstitution; import io.quarkus.extest.runtime.subst.KeyProxy; import io.quarkus.runtime.RuntimeValue; @@ -468,6 +470,11 @@ void checkMapMap(TestBuildAndRunTimeConfig btrt, TestBuildTimeConfig bt, BuildPr } } + @BuildStep + RuntimeInitializedPackageBuildItem runtimeInitializedPackage() { + return new RuntimeInitializedPackageBuildItem(RuntimeInitializedClass.class.getPackage().getName()); + } + @BuildStep(onlyIf = Never.class) void neverRunThisOne() { throw new IllegalStateException("Not supposed to run!"); diff --git a/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/runtimeinitializedpackage/RuntimeInitializedClass.java b/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/runtimeinitializedpackage/RuntimeInitializedClass.java new file mode 100644 index 0000000000000..5425cca0aa0e8 --- /dev/null +++ b/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/runtimeinitializedpackage/RuntimeInitializedClass.java @@ -0,0 +1,5 @@ +package io.quarkus.extest.runtime.runtimeinitializedpackage; + +public class RuntimeInitializedClass { + +} From 298cad30b8cc93309f3c6a63046a22f26908d2f4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Dec 2020 22:09:39 +0000 Subject: [PATCH 05/62] Bump caffeine from 2.8.6 to 2.8.7 Bumps [caffeine](https://github.com/ben-manes/caffeine) from 2.8.6 to 2.8.7. - [Release notes](https://github.com/ben-manes/caffeine/releases) - [Commits](https://github.com/ben-manes/caffeine/compare/v2.8.6...v2.8.7) Signed-off-by: dependabot[bot] --- bom/application/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 36b264a8a1221..23ca6d3990056 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -131,7 +131,7 @@ 2.3 11.0.4.Final 4.3.4.Final - 2.8.6 + 2.8.7 4.1.49.Final 1.0.3 3.4.1.Final From 2eec59f22d366dbdfeead8f5a0d55af95cae6cfa Mon Sep 17 00:00:00 2001 From: Christoph Hermann Date: Mon, 7 Dec 2020 11:21:10 +0100 Subject: [PATCH 06/62] Use setters for Person In the repository pattern example the fields of Person are private. Therefore, in the following usage example setters have to be used instead of direct field access. --- docs/src/main/asciidoc/hibernate-orm-panache.adoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/src/main/asciidoc/hibernate-orm-panache.adoc b/docs/src/main/asciidoc/hibernate-orm-panache.adoc index 180c1696e05d6..65a8c5c2f80fb 100644 --- a/docs/src/main/asciidoc/hibernate-orm-panache.adoc +++ b/docs/src/main/asciidoc/hibernate-orm-panache.adoc @@ -341,9 +341,9 @@ Once you have written your repository, here are the most common operations you w ---- // creating a person Person person = new Person(); -person.name = "Stef"; -person.birth = LocalDate.of(1910, Month.FEBRUARY, 1); -person.status = Status.Alive; +person.setName("Stef"); +person.setBirth(LocalDate.of(1910, Month.FEBRUARY, 1)); +person.setStatus(Status.Alive); // persist it personRepository.persist(person); From d5d4ba62912b1f6fab7c0fa3147464ea3ebb9f1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Marti=C5=A1ka?= Date: Fri, 27 Nov 2020 14:06:05 +0100 Subject: [PATCH 07/62] SmallRye GraphQL 1.0.19 + tests for context propagation --- bom/application/pom.xml | 2 +- .../smallrye-graphql/deployment/pom.xml | 5 + .../GraphQLCDIContextPropagationTest.java | 188 +++++++++++++ .../graphql/deployment/SecurityTest.java | 85 +++++- .../SmallRyeGraphQLContextTestCase.java | 256 ++++++++++++++++++ extensions/smallrye-graphql/runtime/pom.xml | 4 + 6 files changed, 530 insertions(+), 10 deletions(-) create mode 100644 extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/GraphQLCDIContextPropagationTest.java create mode 100644 extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/SmallRyeGraphQLContextTestCase.java diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 23ca6d3990056..89774fc3ea5d7 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -42,7 +42,7 @@ 2.2.5 2.4.4 2.0.16 - 1.0.18 + 1.0.19 1.3.4 4.3.2 2.4.0 diff --git a/extensions/smallrye-graphql/deployment/pom.xml b/extensions/smallrye-graphql/deployment/pom.xml index e687f4c196396..cf6474ef19815 100644 --- a/extensions/smallrye-graphql/deployment/pom.xml +++ b/extensions/smallrye-graphql/deployment/pom.xml @@ -48,6 +48,11 @@ io.smallrye smallrye-graphql-ui-graphiql + + + io.quarkus + quarkus-smallrye-context-propagation-deployment + diff --git a/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/GraphQLCDIContextPropagationTest.java b/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/GraphQLCDIContextPropagationTest.java new file mode 100644 index 0000000000000..7535660ca60fe --- /dev/null +++ b/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/GraphQLCDIContextPropagationTest.java @@ -0,0 +1,188 @@ +package io.quarkus.smallrye.graphql.deployment; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.ThreadLocalRandom; +import java.util.stream.Collectors; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.context.RequestScoped; +import javax.inject.Inject; + +import org.eclipse.microprofile.context.ManagedExecutor; +import org.eclipse.microprofile.graphql.GraphQLApi; +import org.eclipse.microprofile.graphql.Name; +import org.eclipse.microprofile.graphql.Query; +import org.eclipse.microprofile.graphql.Source; +import org.hamcrest.Matchers; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.EmptyAsset; +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.restassured.RestAssured; +import io.restassured.http.ContentType; + +/** + * Testing scenarios which require CDI context propagation to work under the hood. + */ +public class GraphQLCDIContextPropagationTest extends AbstractGraphQLTest { + + @RegisterExtension + static QuarkusUnitTest test = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addClasses(TestPojo.class, ResourceThatNeedsCdiContextPropagation.class) + .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml")); + + /** + * Call a query which needs the CDI context to be propagated, because it is on a RequestScoped bean + * and involves retrieving a batch source field (these are retrieved asynchronously). + */ + @Test + public void testCdiContextPropagationForBatchSources() { + String pingRequest = getPayload("{\n" + + " pojos {\n" + + " duplicatedMessage\n" + + " }\n" + + "}"); + + RestAssured.given().when() + .accept(ContentType.JSON) + .contentType(ContentType.JSON) + .body(pingRequest) + .post("/graphql") + .then() + .assertThat() + .statusCode(200) + .and() + .body("data.pojos.duplicatedMessage", Matchers.contains("AA", "BB")); + } + + /** + * Same as above, but the batch source returns a list of CompletionStages. + */ + @Test + public void testCdiContextPropagationForBatchSourcesAsync() { + String pingRequest = getPayload("{\n" + + " pojos {\n" + + " duplicatedMessageAsync\n" + + " }\n" + + "}"); + + RestAssured.given().when() + .accept(ContentType.JSON) + .contentType(ContentType.JSON) + .body(pingRequest) + .post("/graphql") + .then() + .assertThat() + .statusCode(200) + .and() + .body("data.pojos.duplicatedMessageAsync", Matchers.contains("AA", "BB")); + } + + /** + * In this case, there is an asynchronous query and the application launches a CompletableFuture, + * therefore the application is responsible for propagating the context to that future. + */ + @Test + public void testManualContextPropagation() { + String pingRequest = getPayload("{\n" + + " pojo_manual_propagation {\n" + + " message\n" + + " }\n" + + "}"); + + RestAssured.given().when() + .accept(ContentType.JSON) + .contentType(ContentType.JSON) + .body(pingRequest) + .post("/graphql") + .then() + .assertThat() + .statusCode(200) + .and() + .body("data.pojo_manual_propagation.message", Matchers.equalTo("A")); + } + + @GraphQLApi + @ApplicationScoped + public static class ResourceThatNeedsCdiContextPropagation { + + /** + * This is to make sure that `getPojos` and `duplicatedMessage` are called on the same class instance - they + * need to share the same CDI request context rather than create a new one for calling `duplicatedMessage`. + */ + private volatile Long injectedBeanId; + + @Inject + InjectedBean injectedBean; + + @Inject + ManagedExecutor executor; + + @Query("pojos") + public List getPojos() { + this.injectedBeanId = injectedBean.getId(); + List pojos = new ArrayList<>(); + pojos.add(new TestPojo("A")); + pojos.add(new TestPojo("B")); + return pojos; + } + + @Query("pojo_manual_propagation") + public CompletionStage getPojoWithProgrammaticContextPropagation() { + Long id = injectedBean.getId(); + return executor.supplyAsync( + () -> { + if (!injectedBean.getId().equals(id)) { + throw new IllegalStateException("The future was not executed in the correct request context"); + } + return new TestPojo("A"); + }); + } + + /** + * This source field duplicates the message of a TestPojo (repeats it twice). + */ + @Name("duplicatedMessage") + public List duplicatedMessage(@Source List pojos) { + if (!this.injectedBean.getId().equals(this.injectedBeanId)) { + throw new IllegalStateException("duplicatedMessage must be executed in the same request context as getPojos"); + } + return pojos.stream() + .map(pojo -> pojo.getMessage() + pojo.getMessage()) + .collect(Collectors.toList()); + } + + /** + * This source field duplicates the message of a TestPojo (repeats it twice) and does it asynchronously. + */ + @Name("duplicatedMessageAsync") + public CompletionStage> duplicatedMessageAsync(@Source List pojos) { + if (!this.injectedBean.getId().equals(this.injectedBeanId)) { + throw new IllegalStateException( + "duplicatedMessageAsync must be executed in the same request context as getPojos"); + } + return CompletableFuture.completedFuture(pojos.stream() + .map(pojo -> pojo.getMessage() + pojo.getMessage()) + .collect(Collectors.toList())); + } + + } + + @RequestScoped + public static class InjectedBean { + + private Long id = ThreadLocalRandom.current().nextLong(); + + public Long getId() { + return id; + } + } + +} diff --git a/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/SecurityTest.java b/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/SecurityTest.java index d156ab94a277d..6f9ed1d431c78 100644 --- a/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/SecurityTest.java +++ b/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/SecurityTest.java @@ -4,11 +4,16 @@ import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; +import java.util.List; +import java.util.stream.Collectors; + import javax.annotation.security.RolesAllowed; import javax.enterprise.context.ApplicationScoped; import org.eclipse.microprofile.graphql.GraphQLApi; +import org.eclipse.microprofile.graphql.Name; import org.eclipse.microprofile.graphql.Query; +import org.eclipse.microprofile.graphql.Source; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.asset.EmptyAsset; import org.jboss.shrinkwrap.api.spec.JavaArchive; @@ -24,7 +29,7 @@ public class SecurityTest extends AbstractGraphQLTest { @RegisterExtension static QuarkusUnitTest test = new QuarkusUnitTest() .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) - .addClass(SecuredApi.class) + .addClasses(SecuredApi.class, Foo.class) .addAsResource("application-secured.properties", "application.properties") .addAsResource("users.properties") .addAsResource("roles.properties") @@ -32,7 +37,21 @@ public class SecurityTest extends AbstractGraphQLTest { @Test public void testAuthenticatedUser() { - String query = getPayload("{ foo }"); + String query = getPayload("{ foo { message} }"); + RestAssured.given() + .header(new Header("Authorization", "Basic ZGF2aWQ6cXdlcnR5MTIz")) + .body(query) + .contentType(MEDIATYPE_JSON) + .post("/graphql/") + .then() + .assertThat() + .body("errors", nullValue()) + .body("data.foo.message", equalTo("foo")); + } + + @Test + public void testAuthenticatedUserWithSource() { + String query = getPayload("{ foo { bonusFoo } }"); RestAssured.given() .header(new Header("Authorization", "Basic ZGF2aWQ6cXdlcnR5MTIz")) .body(query) @@ -41,12 +60,12 @@ public void testAuthenticatedUser() { .then() .assertThat() .body("errors", nullValue()) - .body("data.foo", equalTo("foo")); + .body("data.foo.bonusFoo", equalTo("bonus")); } @Test public void testUnauthorizedRole() { - String query = getPayload("{ bar }"); + String query = getPayload("{ bar { message } }"); RestAssured.given() .header(new Header("Authorization", "Basic ZGF2aWQ6cXdlcnR5MTIz")) .body(query) @@ -55,7 +74,43 @@ public void testUnauthorizedRole() { .then() .assertThat() .body("errors", notNullValue()) - .body("data.bar", nullValue()); + .body("data.bar.message", nullValue()); + } + + /** + * Call a query that we are authorized to call, but within that, retrieve a source field that we're not authorized + * to retrieve. + */ + @Test + public void testUnauthorizedForSource() { + String query = getPayload("{ foo { bonusBar } }"); + RestAssured.given() + .header(new Header("Authorization", "Basic ZGF2aWQ6cXdlcnR5MTIz")) + .body(query) + .contentType(MEDIATYPE_JSON) + .post("/graphql/") + .then() + .assertThat() + .body("errors", notNullValue()) + .body("data.foo.bonusBar", nullValue()); + } + + static class Foo { + + private String message; + + public Foo(String foo) { + this.message = foo; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + } @GraphQLApi @@ -64,14 +119,26 @@ public static class SecuredApi { @Query @RolesAllowed("fooRole") - public String foo() { - return "foo"; + public Foo foo() { + return new Foo("foo"); + } + + @Name("bonusFoo") + @RolesAllowed("fooRole") + public List bonusFoo(@Source List foos) { + return foos.stream().map(foo -> "bonus").collect(Collectors.toList()); } @Query @RolesAllowed("barRole") - public String bar() { - return "bar"; + public Foo bar() { + return new Foo("bar"); + } + + @Name("bonusBar") + @RolesAllowed("barRole") + public List bonusBar(@Source List foos) { + return foos.stream().map(foo -> "bonus").collect(Collectors.toList()); } } diff --git a/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/SmallRyeGraphQLContextTestCase.java b/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/SmallRyeGraphQLContextTestCase.java new file mode 100644 index 0000000000000..0d117b54bb53a --- /dev/null +++ b/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/SmallRyeGraphQLContextTestCase.java @@ -0,0 +1,256 @@ +package io.quarkus.smallrye.graphql.deployment; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.ExecutionException; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.inject.Instance; +import javax.inject.Inject; + +import org.eclipse.microprofile.context.ManagedExecutor; +import org.eclipse.microprofile.graphql.GraphQLApi; +import org.eclipse.microprofile.graphql.Name; +import org.eclipse.microprofile.graphql.Query; +import org.eclipse.microprofile.graphql.Source; +import org.hamcrest.Matchers; +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.restassured.RestAssured; +import io.restassured.http.ContentType; +import io.smallrye.graphql.api.Context; + +public class SmallRyeGraphQLContextTestCase extends AbstractGraphQLTest { + + @RegisterExtension + static QuarkusUnitTest test = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addClasses(Dummy.class, ContextPropagationResource.class)); + + @Test + public void testAsyncQuery() { + String request = getPayload("{\n" + + " testAsyncQuery {\n" + + " stringField\n" + + " }\n" + + "}"); + + RestAssured.given().when() + .accept(ContentType.JSON) + .contentType(ContentType.JSON) + .body(request) + .post("/graphql") + .then() + .log() + .body(true) + .assertThat() + .statusCode(200) + .and() + .body("data.testAsyncQuery.stringField", Matchers.equalTo("OK")); + } + + @Test + public void testAsyncQueryWithAsyncSource() { + String request = getPayload("{\n" + + " testAsyncQuery {\n" + + " stringField\n" + + " stringBatchSourceAsync\n" + + " }\n" + + "}"); + + RestAssured.given().when() + .accept(ContentType.JSON) + .contentType(ContentType.JSON) + .body(request) + .post("/graphql") + .then() + .log() + .body(true) + .assertThat() + .statusCode(200) + .and() + .body("data.testAsyncQuery.stringField", Matchers.equalTo("OK")) + .body("data.testAsyncQuery.stringBatchSourceAsync", Matchers.equalTo("hello")); + } + + @Test + public void testManualPropagation() { + String request = getPayload("{\n" + + " testManualPropagation {\n" + + " stringField\n" + + " }\n" + + "}"); + + RestAssured.given().when() + .accept(ContentType.JSON) + .contentType(ContentType.JSON) + .body(request) + .post("/graphql") + .then() + .log() + .body(true) + .assertThat() + .statusCode(200) + .and() + .body("data.testManualPropagation.stringField", Matchers.equalTo("OK")); + } + + @Test + public void testSourceMethods() { + String request = getPayload("{\n" + + " testSourceMethods {\n" + + " stringBatchSource,\n" + + " numberBatchSource\n" + + " }\n" + + "}"); + + RestAssured.given().when() + .accept(ContentType.JSON) + .contentType(ContentType.JSON) + .body(request) + .post("/graphql") + .then() + .log() + .body(true) + .assertThat() + .statusCode(200) + .and() + .body("data.testSourceMethods.stringBatchSource", Matchers.equalTo("hello")); + } + + @GraphQLApi + @ApplicationScoped + public static class ContextPropagationResource { + + @Inject + Instance context; + + @Inject + ManagedExecutor managedExecutor; + + @Query + public CompletableFuture testAsyncQuery() { + final String executionId = context.get().getExecutionId(); + assertNotNull(executionId); + this.executionId = context.get().getExecutionId(); + assertEquals("testAsyncQuery", context.get().getFieldName()); + Dummy result = new Dummy(); + result.setStringField("OK"); + return CompletableFuture.completedFuture(result); + } + + @Query + public Dummy testManualPropagation() throws ExecutionException, InterruptedException { + final String executionId = context.get().getExecutionId(); + assertNotNull(executionId); + + CompletableFuture numberFuture = CompletableFuture.supplyAsync(() -> { + assertNotNull(context); + assertEquals(executionId, context.get().getExecutionId()); + assertEquals("testManualPropagation", context.get().getFieldName()); + return 42; + }, managedExecutor); + + CompletableFuture stringFuture = CompletableFuture.supplyAsync(() -> { + assertNotNull(context, "Context must to be available inside an async task"); + assertEquals(executionId, context.get().getExecutionId(), "Execution ID must be the same inside an async task"); + assertEquals("testManualPropagation", context.get().getFieldName()); + return "OK"; + }, managedExecutor); + + Dummy result = new Dummy(); + result.setNumberField(numberFuture.get()); + result.setStringField(stringFuture.get()); + return result; + } + + private volatile String executionId; + + @Query + public Dummy testSourceMethods() { + this.executionId = context.get().getExecutionId(); + assertEquals("testSourceMethods", context.get().getFieldName()); + return new Dummy(); + } + + @Name("stringBatchSource") + public List stringBatchSource(@Source List source) { + assertEquals("stringBatchSource", context.get().getFieldName()); + assertEquals(this.executionId, context.get().getExecutionId(), "Wrong execution ID propagated from the root query"); + + List result = new ArrayList<>(); + for (Dummy dummy : source) { + result.add("hello"); + } + return result; + } + + @Name("stringBatchSourceAsync") + public CompletionStage> stringBatchSourceAsync(@Source List source) { + assertEquals("stringBatchSourceAsync", context.get().getFieldName()); + assertEquals(this.executionId, context.get().getExecutionId(), "Wrong execution ID propagated from the root query"); + + List result = new ArrayList<>(); + for (Dummy dummy : source) { + result.add("hello"); + } + return CompletableFuture.completedFuture(result); + } + + @Name("numberBatchSource") + public List numberBatchSource(@Source List source) { + assertEquals("numberBatchSource", context.get().getFieldName()); + assertEquals(this.executionId, context.get().getExecutionId(), "Wrong execution ID propagated from the root query"); + + List result = new ArrayList<>(); + for (Dummy dummy : source) { + result.add(123); + } + return result; + } + + } + + public static class Dummy { + + private String stringField; + + private Integer numberField; + + private Boolean booleanField; + + public String getStringField() { + return stringField; + } + + public void setStringField(String stringField) { + this.stringField = stringField; + } + + public Integer getNumberField() { + return numberField; + } + + public void setNumberField(Integer numberField) { + this.numberField = numberField; + } + + public Boolean getBooleanField() { + return booleanField; + } + + public void setBooleanField(Boolean booleanField) { + this.booleanField = booleanField; + } + } + +} diff --git a/extensions/smallrye-graphql/runtime/pom.xml b/extensions/smallrye-graphql/runtime/pom.xml index 64cd6980be6b2..2ddb6d7a45711 100644 --- a/extensions/smallrye-graphql/runtime/pom.xml +++ b/extensions/smallrye-graphql/runtime/pom.xml @@ -35,6 +35,10 @@ io.smallrye smallrye-graphql-cdi + + io.quarkus + quarkus-smallrye-context-propagation + From be7a2b3bc3b7a0e86ab0708d865ecbf5eea20d78 Mon Sep 17 00:00:00 2001 From: George Andrinopoulos Date: Mon, 7 Dec 2020 22:44:00 +0200 Subject: [PATCH 08/62] Introduce new quarkus cli command create-jbang --- .../main/java/io/quarkus/cli/CreateJBang.java | 60 +++++++++++++++++++ .../main/java/io/quarkus/cli/QuarkusCli.java | 2 +- .../src/test/java/io/quarkus/cli/CliTest.java | 10 ++++ 3 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 devtools/cli/src/main/java/io/quarkus/cli/CreateJBang.java diff --git a/devtools/cli/src/main/java/io/quarkus/cli/CreateJBang.java b/devtools/cli/src/main/java/io/quarkus/cli/CreateJBang.java new file mode 100644 index 0000000000000..41f3ddbf2cb79 --- /dev/null +++ b/devtools/cli/src/main/java/io/quarkus/cli/CreateJBang.java @@ -0,0 +1,60 @@ +package io.quarkus.cli; + +import java.io.File; +import java.util.Set; +import java.util.concurrent.Callable; + +import io.quarkus.cli.core.BaseSubCommand; +import io.quarkus.devtools.commands.CreateJBangProject; +import io.quarkus.platform.tools.config.QuarkusPlatformConfig; +import picocli.CommandLine; + +@CommandLine.Command(name = "create-jbang", sortOptions = false, usageHelpAutoWidth = true, mixinStandardHelpOptions = false, description = "Create a new quarkus jbang project.") +public class CreateJBang extends BaseSubCommand implements Callable { + + @CommandLine.Option(names = { "-n", + "--no-wrapper" }, order = 1, description = "Generate without JBang wrapper.") + boolean noJBangWrapper = false; + + @CommandLine.Option(names = { "-o", + "--output-folder" }, order = 2, paramLabel = "OUTPUT-FOLDER", description = "The output folder for project") + String outputFolder = "jbang-with-quarkus"; + + @CommandLine.Parameters(arity = "0..1", paramLabel = "EXTENSION", description = "Extensions to add to project") + Set extensions; + + @Override + public Integer call() throws Exception { + try { + File projectDirectory = new File(System.getProperty("user.dir")); + + File projectRoot = new File(projectDirectory.getAbsoluteFile(), outputFolder); + if (projectRoot.exists()) { + err().println("Unable to create the project, " + + "the directory " + projectRoot.getAbsolutePath() + " already exists"); + return CommandLine.ExitCode.SOFTWARE; + } + + boolean status = new CreateJBangProject(projectRoot.getAbsoluteFile().toPath(), + QuarkusPlatformConfig.getGlobalDefault().getPlatformDescriptor()) + .extensions(extensions) + .setValue("noJBangWrapper", noJBangWrapper) + .execute() + .isSuccess(); + + if (status) { + out().println("JBang project created."); + parent.setProjectDirectory(projectRoot.toPath().toAbsolutePath()); + } else { + err().println("Failed to create JBang project"); + return CommandLine.ExitCode.SOFTWARE; + } + } catch (Exception e) { + if (parent.showErrors) + e.printStackTrace(err()); + err().println("JBang project creation failed, " + e.getMessage()); + return CommandLine.ExitCode.SOFTWARE; + } + return CommandLine.ExitCode.OK; + } +} diff --git a/devtools/cli/src/main/java/io/quarkus/cli/QuarkusCli.java b/devtools/cli/src/main/java/io/quarkus/cli/QuarkusCli.java index e7225f6c6b665..00b91bd33ce19 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/QuarkusCli.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/QuarkusCli.java @@ -21,7 +21,7 @@ @CommandLine.Command(name = "quarkus", aliases = { "qs" }, versionProvider = QuarkusVersion.class, usageHelpAutoWidth = true, subcommandsRepeatable = true, mixinStandardHelpOptions = true, subcommands = { Build.class, - Clean.class, Create.class, List.class, Add.class, Remove.class, Dev.class }) + Clean.class, Create.class, CreateJBang.class, List.class, Add.class, Remove.class, Dev.class }) public class QuarkusCli implements QuarkusApplication { public void usage() { diff --git a/devtools/cli/src/test/java/io/quarkus/cli/CliTest.java b/devtools/cli/src/test/java/io/quarkus/cli/CliTest.java index 99e3e9ff7c405..5a1eeb2ffdb3a 100644 --- a/devtools/cli/src/test/java/io/quarkus/cli/CliTest.java +++ b/devtools/cli/src/test/java/io/quarkus/cli/CliTest.java @@ -302,6 +302,16 @@ public void testMavenBuild() throws Exception { } + @Test + public void testCreateJBang() throws Exception { + + execute("create-jbang", "--output-folder=my-jbang-project", "resteasy-jsonb"); + + Path project = workspace.resolve("my-jbang-project"); + System.setProperty("user.dir", project.toFile().getAbsolutePath()); + Assertions.assertEquals(CommandLine.ExitCode.OK, exitCode); + } + private void deleteDir(Path path) throws Exception { if (!path.toFile().exists()) return; From 880846aa99249dd805df619b22843fd35171bc04 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Tue, 8 Dec 2020 11:44:00 +0200 Subject: [PATCH 09/62] Convert test logging level to debug The previous error logging was obnoxious and cluttered the test logs for no good reason --- .../resource/basic/DefaultMediaTypeTest.java | 18 +++++++++--------- .../test/resource/basic/ReponseInfoTest.java | 4 ++-- .../resource/GenericEntityDoubleWriter.java | 10 +++++----- .../resource/GenericEntitytFloatWriter.java | 4 ++-- .../basic/resource/ParameterSubResSubImpl.java | 4 ++-- .../resource/ResourceLocatorBaseResource.java | 6 +++--- .../resource/ResourceLocatorSubresource.java | 14 +++++++------- .../resource/ResourceLocatorSubresource2.java | 8 ++++---- ...Resource.java => ResponseInfoResource.java} | 8 ++++---- .../resource/SpecialResourceApiResource.java | 4 ++-- .../basic/resource/UriInfoSimpleResource.java | 16 ++++++++-------- .../UriInfoSimpleSingletonResource.java | 14 +++++++------- .../async/filters/AsyncRequestFilter.java | 7 +++---- .../async/filters/AsyncResponseFilter.java | 10 ++++------ .../reactive/server/test/simple/TestUtil.java | 6 +++--- 15 files changed, 65 insertions(+), 68 deletions(-) rename extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/{ReponseInfoResource.java => ResponseInfoResource.java} (82%) diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/DefaultMediaTypeTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/DefaultMediaTypeTest.java index fdae4cd469d0e..bb2dc56fd6271 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/DefaultMediaTypeTest.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/DefaultMediaTypeTest.java @@ -35,7 +35,7 @@ @DisplayName("Default Media Type Test") public class DefaultMediaTypeTest { - protected final Logger logger = Logger.getLogger(DefaultMediaTypeResource.class.getName()); + private final Logger LOG = Logger.getLogger(DefaultMediaTypeResource.class.getName()); static Client client; @@ -81,7 +81,7 @@ public void postDateProduce() throws Exception { Response response = target.request().post(Entity.entity(baos.toByteArray(), MediaType.APPLICATION_OCTET_STREAM)); Assertions.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); String responseContent = response.readEntity(String.class); - logger.info(String.format("Response: %s", responseContent)); + LOG.debug(String.format("Response: %s", responseContent)); } /** @@ -102,7 +102,7 @@ public void postDate() throws Exception { Assertions.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); String responseContent = response.readEntity(String.class); - logger.info(String.format("Response: %s", responseContent)); + LOG.debug(String.format("Response: %s", responseContent)); } /** @@ -120,7 +120,7 @@ public void postFooProduce() throws Exception { Response response = target.request().post(Entity.entity(baos.toByteArray(), MediaType.APPLICATION_OCTET_STREAM)); Assertions.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); String responseContent = response.readEntity(String.class); - logger.info(String.format("Response: %s", responseContent)); + LOG.debug(String.format("Response: %s", responseContent)); } /** @@ -141,7 +141,7 @@ public void postFoo() throws Exception { Assertions.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); String responseContent = response.readEntity(String.class); - logger.info(String.format("Response: %s", responseContent)); + LOG.debug(String.format("Response: %s", responseContent)); } /** @@ -159,7 +159,7 @@ public void postIntProduce() throws Exception { Response response = target.request().post(Entity.entity(baos.toByteArray(), MediaType.APPLICATION_OCTET_STREAM)); Assertions.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); String responseContent = response.readEntity(String.class); - logger.info(String.format("Response: %s", responseContent)); + LOG.debug(String.format("Response: %s", responseContent)); } /** @@ -178,7 +178,7 @@ public void postInt() throws Exception { Assertions.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); String responseContent = response.readEntity(String.class); - logger.info(String.format("Response: %s", responseContent)); + LOG.debug(String.format("Response: %s", responseContent)); } /** @@ -196,7 +196,7 @@ public void postIntegerProduce() throws Exception { Response response = target.request().post(Entity.entity(baos.toByteArray(), MediaType.APPLICATION_OCTET_STREAM)); Assertions.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); String responseContent = response.readEntity(String.class); - logger.info(String.format("Response: %s", responseContent)); + LOG.debug(String.format("Response: %s", responseContent)); } /** @@ -215,6 +215,6 @@ public void postInteger() throws Exception { Assertions.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); String responseContent = response.readEntity(String.class); - logger.info(String.format("Response: %s", responseContent)); + LOG.debug(String.format("Response: %s", responseContent)); } } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/ReponseInfoTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/ReponseInfoTest.java index 94982cea3058b..545cad1d85762 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/ReponseInfoTest.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/ReponseInfoTest.java @@ -16,7 +16,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; -import io.quarkus.resteasy.reactive.server.test.resource.basic.resource.ReponseInfoResource; +import io.quarkus.resteasy.reactive.server.test.resource.basic.resource.ResponseInfoResource; import io.quarkus.resteasy.reactive.server.test.simple.PortProviderUtil; import io.quarkus.test.QuarkusUnitTest; @@ -48,7 +48,7 @@ public JavaArchive get() { JavaArchive war = ShrinkWrap.create(JavaArchive.class); war.addClasses(PortProviderUtil.class, ReponseInfoTest.class); // Use of PortProviderUtil in the deployment - war.addClasses(ReponseInfoResource.class); + war.addClasses(ResponseInfoResource.class); return war; } }); diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/GenericEntityDoubleWriter.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/GenericEntityDoubleWriter.java index 9a7f0856519b8..b0f01844c9a0c 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/GenericEntityDoubleWriter.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/GenericEntityDoubleWriter.java @@ -20,21 +20,21 @@ @Produces("*/*") public class GenericEntityDoubleWriter implements MessageBodyWriter> { - private static Logger logger = Logger.getLogger(GenericEntityDoubleWriter.class); + private static final Logger LOG = Logger.getLogger(GenericEntityDoubleWriter.class); public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { - logger.info("DoubleWriter type: " + type.getName()); + LOG.debug("DoubleWriter type: " + type.getName()); if (!List.class.isAssignableFrom(type)) { return false; } - logger.info("DoubleWriter: " + genericType); + LOG.debug("DoubleWriter: " + genericType); if (!(genericType instanceof ParameterizedType)) { return false; } - logger.info("DoubleWriter"); + LOG.debug("DoubleWriter"); ParameterizedType pt = (ParameterizedType) genericType; boolean result = pt.getActualTypeArguments()[0].equals(Double.class); - logger.info("Doublewriter result!!!: " + result); + LOG.debug("Doublewriter result!!!: " + result); return result; } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/GenericEntitytFloatWriter.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/GenericEntitytFloatWriter.java index 7ff532ada324a..4d5fb86d0f729 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/GenericEntitytFloatWriter.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/GenericEntitytFloatWriter.java @@ -20,7 +20,7 @@ @Produces("*/*") public class GenericEntitytFloatWriter implements MessageBodyWriter> { - private static Logger logger = Logger.getLogger(GenericEntitytFloatWriter.class); + private static final Logger LOG = Logger.getLogger(GenericEntitytFloatWriter.class); public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { if (!List.class.isAssignableFrom(type)) { @@ -31,7 +31,7 @@ public boolean isWriteable(Class type, Type genericType, Annotation[] annotat } ParameterizedType pt = (ParameterizedType) genericType; boolean result = pt.getActualTypeArguments()[0].equals(Float.class); - logger.info("FloatWriter result!!!: " + result); + LOG.debug("FloatWriter result!!!: " + result); return result; } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/ParameterSubResSubImpl.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/ParameterSubResSubImpl.java index 8723a55eb9404..881da3124776a 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/ParameterSubResSubImpl.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/ParameterSubResSubImpl.java @@ -3,7 +3,7 @@ import org.jboss.logging.Logger; public class ParameterSubResSubImpl implements ParameterSubResSub, ParameterSubResInternalInterface { - private static Logger logger = Logger.getLogger(ParameterSubResSubImpl.class); + private static Logger LOG = Logger.getLogger(ParameterSubResSubImpl.class); private final String path; @@ -18,7 +18,7 @@ public String get() { @Override public void foo(T value) { - logger.info("foo: " + value); + LOG.debug("foo: " + value); } } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/ResourceLocatorBaseResource.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/ResourceLocatorBaseResource.java index 7e5dfee8674d4..a6b50a2c5bfd1 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/ResourceLocatorBaseResource.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/ResourceLocatorBaseResource.java @@ -20,16 +20,16 @@ public class ResourceLocatorBaseResource { @Path("base/{param}/resources") public Object getSubresource(@PathParam("param") String param, @Context UriInfo uri) { - LOG.info("Here in BaseResource"); + LOG.debug("Here in BaseResource"); Assertions.assertEquals("1", param); List matchedURIs = uri.getMatchedURIs(); Assertions.assertEquals(2, matchedURIs.size()); Assertions.assertEquals("base/1/resources", matchedURIs.get(0)); Assertions.assertEquals("", matchedURIs.get(1)); for (String ancestor : matchedURIs) - LOG.info(" " + ancestor); + LOG.debug(" " + ancestor); - LOG.info("Uri Ancesstors Object for Subresource.doGet():"); + LOG.debug("Uri Ancestors Object for Subresource.doGet():"); Assertions.assertEquals(1, uri.getMatchedResources().size()); Assertions.assertEquals(ResourceLocatorBaseResource.class, uri.getMatchedResources().get(0).getClass()); return new ResourceLocatorSubresource(); diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/ResourceLocatorSubresource.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/ResourceLocatorSubresource.java index 445a3c80ba996..088fe7c7179f0 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/ResourceLocatorSubresource.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/ResourceLocatorSubresource.java @@ -22,34 +22,34 @@ public String doGet(@Context UriInfo uri) { Assertions.assertEquals("base/1/resources", matchedURIs.get(0)); Assertions.assertEquals("", matchedURIs.get(1)); for (String ancestor : matchedURIs) - LOG.info(" " + ancestor); + LOG.debug(" " + ancestor); - LOG.info("Uri Ancesstors Object for Subresource.doGet():"); + LOG.info("Uri Ancestors Object for Subresource.doGet():"); Assertions.assertEquals(2, uri.getMatchedResources().size()); Assertions.assertEquals(ResourceLocatorSubresource.class, uri.getMatchedResources().get(0).getClass()); Assertions.assertEquals(ResourceLocatorBaseResource.class, uri.getMatchedResources().get(1).getClass()); for (Object ancestor : uri.getMatchedResources()) - LOG.infov(" {0}", ancestor.getClass().getName()); + LOG.debugv(" {0}", ancestor.getClass().getName()); return this.getClass().getName(); } @Path("/subresource2") public Object getSubresource2(@Context UriInfo uri) { - LOG.info("Uri Ancesstors for Subresource.getSubresource2():"); + LOG.info("Uri Ancestors for Subresource.getSubresource2():"); List matchedURIs = uri.getMatchedURIs(); Assertions.assertEquals(3, matchedURIs.size()); Assertions.assertEquals("base/1/resources/subresource2", matchedURIs.get(0)); Assertions.assertEquals("base/1/resources", matchedURIs.get(1)); Assertions.assertEquals("", matchedURIs.get(2)); for (String ancestor : matchedURIs) - LOG.info(" " + ancestor); + LOG.debug(" " + ancestor); - LOG.info("Uri Ancesstors Object for Subresource.getSubresource2():"); + LOG.info("Uri Ancestors Object for Subresource.getSubresource2():"); Assertions.assertEquals(2, uri.getMatchedResources().size()); Assertions.assertEquals(ResourceLocatorSubresource.class, uri.getMatchedResources().get(0).getClass()); Assertions.assertEquals(ResourceLocatorBaseResource.class, uri.getMatchedResources().get(1).getClass()); for (Object ancestor : uri.getMatchedResources()) - LOG.infov(" {0}", ancestor.getClass().getName()); + LOG.debugv(" {0}", ancestor.getClass().getName()); return new ResourceLocatorSubresource2(); } } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/ResourceLocatorSubresource2.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/ResourceLocatorSubresource2.java index ceb4fc0a2054e..069786972a9dc 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/ResourceLocatorSubresource2.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/ResourceLocatorSubresource2.java @@ -16,22 +16,22 @@ public class ResourceLocatorSubresource2 { @GET @Path("stuff/{param}/bar") public String doGet(@PathParam("param") String param, @Context UriInfo uri) { - LOG.info("Uri Ancesstors for Subresource2.doGet():"); + LOG.debug("Uri Ancestors for Subresource2.doGet():"); Assertions.assertEquals(4, uri.getMatchedURIs().size()); Assertions.assertEquals("base/1/resources/subresource2/stuff/2/bar", uri.getMatchedURIs().get(0)); Assertions.assertEquals("base/1/resources/subresource2", uri.getMatchedURIs().get(1)); Assertions.assertEquals("base/1/resources", uri.getMatchedURIs().get(2)); Assertions.assertEquals("", uri.getMatchedURIs().get(3)); for (String ancestor : uri.getMatchedURIs()) - LOG.infov(" {0}", ancestor); + LOG.debugv(" {0}", ancestor); - LOG.info("Uri Ancesstors Object for Subresource2.doGet():"); + LOG.debug("Uri Ancestors Object for Subresource2.doGet():"); Assertions.assertEquals(3, uri.getMatchedResources().size()); Assertions.assertEquals(ResourceLocatorSubresource2.class, uri.getMatchedResources().get(0).getClass()); Assertions.assertEquals(ResourceLocatorSubresource.class, uri.getMatchedResources().get(1).getClass()); Assertions.assertEquals(ResourceLocatorBaseResource.class, uri.getMatchedResources().get(2).getClass()); for (Object ancestor : uri.getMatchedResources()) - LOG.infov(" {0}", ancestor.getClass().getName()); + LOG.debugv(" {0}", ancestor.getClass().getName()); Assertions.assertEquals("2", param); return this.getClass().getName() + "-" + param; } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/ReponseInfoResource.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/ResponseInfoResource.java similarity index 82% rename from extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/ReponseInfoResource.java rename to extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/ResponseInfoResource.java index 7590f81da26d1..0c518d330a1f0 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/ReponseInfoResource.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/ResponseInfoResource.java @@ -14,13 +14,13 @@ import io.quarkus.resteasy.reactive.server.test.simple.PortProviderUtil; @Path("/") -public class ReponseInfoResource { - private static Logger logger = Logger.getLogger(ReponseInfoResource.class); +public class ResponseInfoResource { + private static Logger LOG = Logger.getLogger(ResponseInfoResource.class); @Path("/simple") @GET public String get(@QueryParam("abs") String abs) { - logger.info("abs query: " + abs); + LOG.debug("abs query: " + abs); URI base; if (abs == null) { base = PortProviderUtil.createURI("/new/one"); @@ -29,7 +29,7 @@ public String get(@QueryParam("abs") String abs) { } Response response = Response.temporaryRedirect(URI.create("new/one")).build(); URI uri = (URI) response.getMetadata().getFirst(HttpHeaders.LOCATION); - logger.info("Location uri: " + uri); + LOG.debug("Location uri: " + uri); Assertions.assertEquals(base.getPath(), uri.getPath()); return "CONTENT"; } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/SpecialResourceApiResource.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/SpecialResourceApiResource.java index a4e38a8f48728..4262119cf37e6 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/SpecialResourceApiResource.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/SpecialResourceApiResource.java @@ -12,7 +12,7 @@ @Path("/{api:(?i:api)}") public class SpecialResourceApiResource { - private static Logger logger = Logger.getLogger(SpecialResourceApiResource.class); + private static Logger LOG = Logger.getLogger(SpecialResourceApiResource.class); @Path("/{func:(?i:func)}") @GET @@ -23,7 +23,7 @@ public String func() { @PUT public void put(@Context HttpHeaders headers, String val) { - logger.info(headers.getMediaType()); + LOG.debug(headers.getMediaType()); Assertions.assertEquals("Wrong request content", val, "hello"); } } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/UriInfoSimpleResource.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/UriInfoSimpleResource.java index 76e6c250c4a6d..9d88885907f11 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/UriInfoSimpleResource.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/UriInfoSimpleResource.java @@ -15,14 +15,14 @@ @Path("/UriInfoSimpleResource") public class UriInfoSimpleResource { - private static Logger logger = Logger.getLogger(UriInfoSimpleResource.class); + private static Logger LOG = Logger.getLogger(UriInfoSimpleResource.class); @Context UriInfo myInfo; @Path("/simple") @GET public String get(@Context UriInfo info, @QueryParam("abs") String abs) { - logger.info("abs query: " + abs); + LOG.debug("abs query: " + abs); URI base = null; if (abs == null) { base = PortProviderUtil.createURI("/"); @@ -30,9 +30,9 @@ public String get(@Context UriInfo info, @QueryParam("abs") String abs) { base = PortProviderUtil.createURI("/" + abs + "/"); } - logger.info("BASE URI: " + info.getBaseUri()); - logger.info("Request URI: " + info.getRequestUri()); - logger.info("Absolute URI: " + info.getAbsolutePath()); + LOG.debug("BASE URI: " + info.getBaseUri()); + LOG.debug("Request URI: " + info.getRequestUri()); + LOG.debug("Absolute URI: " + info.getAbsolutePath()); Assertions.assertEquals(base.getPath(), info.getBaseUri().getPath()); Assertions.assertEquals("/UriInfoSimpleResource/simple", info.getPath()); return "CONTENT"; @@ -41,7 +41,7 @@ public String get(@Context UriInfo info, @QueryParam("abs") String abs) { @Path("/simple/fromField") @GET public String getField(@QueryParam("abs") String abs) { - logger.info("abs query: " + abs); + LOG.debug("abs query: " + abs); URI base = null; if (abs == null) { base = PortProviderUtil.createURI("/"); @@ -49,8 +49,8 @@ public String getField(@QueryParam("abs") String abs) { base = PortProviderUtil.createURI("/" + abs + "/"); } - logger.info("BASE URI: " + myInfo.getBaseUri()); - logger.info("Request URI: " + myInfo.getRequestUri()); + LOG.debug("BASE URI: " + myInfo.getBaseUri()); + LOG.debug("Request URI: " + myInfo.getRequestUri()); Assertions.assertEquals(base.getPath(), myInfo.getBaseUri().getPath()); Assertions.assertEquals("/UriInfoSimpleResource/simple/fromField", myInfo.getPath()); return "CONTENT"; diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/UriInfoSimpleSingletonResource.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/UriInfoSimpleSingletonResource.java index 0cbb4b423e217..5c92562566c6f 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/UriInfoSimpleSingletonResource.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/UriInfoSimpleSingletonResource.java @@ -15,7 +15,7 @@ @Path("UriInfoSimpleSingletonResource") public class UriInfoSimpleSingletonResource { - private static Logger logger = Logger.getLogger(UriInfoSimpleSingletonResource.class); + private static Logger LOG = Logger.getLogger(UriInfoSimpleSingletonResource.class); @Context UriInfo myInfo; @@ -23,7 +23,7 @@ public class UriInfoSimpleSingletonResource { @Path("/simple") @GET public String get(@Context UriInfo info, @QueryParam("abs") String abs) { - logger.info("abs query: " + abs); + LOG.debug("abs query: " + abs); URI base = null; if (abs == null) { base = PortProviderUtil.createURI("/"); @@ -31,8 +31,8 @@ public String get(@Context UriInfo info, @QueryParam("abs") String abs) { base = PortProviderUtil.createURI("/" + abs + "/"); } - logger.info("BASE URI: " + info.getBaseUri()); - logger.info("Request URI: " + info.getRequestUri()); + LOG.debug("BASE URI: " + info.getBaseUri()); + LOG.debug("Request URI: " + info.getRequestUri()); Assertions.assertEquals(base.getPath(), info.getBaseUri().getPath()); Assertions.assertEquals("/simple", info.getPath()); return "CONTENT"; @@ -41,7 +41,7 @@ public String get(@Context UriInfo info, @QueryParam("abs") String abs) { @Path("/simple/fromField") @GET public String get(@QueryParam("abs") String abs) { - logger.info("abs query: " + abs); + LOG.debug("abs query: " + abs); URI base = null; if (abs == null) { base = PortProviderUtil.createURI("/"); @@ -49,8 +49,8 @@ public String get(@QueryParam("abs") String abs) { base = PortProviderUtil.createURI("/" + abs + "/"); } - logger.info("BASE URI: " + myInfo.getBaseUri()); - logger.info("Request URI: " + myInfo.getRequestUri()); + LOG.debug("BASE URI: " + myInfo.getBaseUri()); + LOG.debug("Request URI: " + myInfo.getRequestUri()); Assertions.assertEquals(base.getPath(), myInfo.getBaseUri().getPath()); Assertions.assertEquals("/simple/fromField", myInfo.getPath()); return "CONTENT"; diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resteasy/async/filters/AsyncRequestFilter.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resteasy/async/filters/AsyncRequestFilter.java index ba4373c8d0062..ca648199ec2b0 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resteasy/async/filters/AsyncRequestFilter.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resteasy/async/filters/AsyncRequestFilter.java @@ -27,7 +27,7 @@ public void filter(ResteasyReactiveContainerRequestContext ctx) { callbackException = null; String action = ctx.getHeaderString(name); - LOG.error("Filter request for " + name + " with action: " + action); + LOG.debug("Filter request for " + name + " with action: " + action); if ("sync-pass".equals(action)) { // do nothing } else if ("sync-fail".equals(action)) { @@ -54,8 +54,7 @@ public void filter(ResteasyReactiveContainerRequestContext ctx) { try { Thread.sleep(2000); } catch (InterruptedException e) { - // TODO Auto-generated catch block - LOG.error("Error:", e); + LOG.debug("Error:", e); } resteasyReactiveCallbackContext.registerCompletionCallback((t) -> { if (callbackException != null) @@ -68,7 +67,7 @@ public void filter(ResteasyReactiveContainerRequestContext ctx) { ctx.resume(new Throwable("ouch")); }); } - LOG.error("Filter request for " + name + " with action: " + action + " done"); + LOG.debug("Filter request for " + name + " with action: " + action + " done"); } } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resteasy/async/filters/AsyncResponseFilter.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resteasy/async/filters/AsyncResponseFilter.java index 1c63bfbdf09fe..733af34cbaffc 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resteasy/async/filters/AsyncResponseFilter.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resteasy/async/filters/AsyncResponseFilter.java @@ -34,7 +34,7 @@ public void filter(ResteasyReactiveContainerRequestContext requestContext, Conta callbackException = null; String action = requestContext.getHeaderString(name); - LOG.error("Filter response for " + name + " with action: " + action); + LOG.debug("Filter response for " + name + " with action: " + action); if ("sync-pass".equals(action)) { // do nothing } else if ("sync-fail".equals(action)) { @@ -60,8 +60,7 @@ public void filter(ResteasyReactiveContainerRequestContext requestContext, Conta try { Thread.sleep(2000); } catch (InterruptedException e) { - // TODO Auto-generated catch block - LOG.error("Error:", e); + LOG.debug("Error:", e); } ctx.setEntity(name); requestContext.resume(); @@ -80,8 +79,7 @@ public void filter(ResteasyReactiveContainerRequestContext requestContext, Conta try { Thread.sleep(2000); } catch (InterruptedException e) { - // TODO Auto-generated catch block - LOG.error("Error:", e); + LOG.debug("Error:", e); } ctx.setEntity(name); resteasyReactiveCallbackContext.registerCompletionCallback((t) -> { @@ -95,7 +93,7 @@ public void filter(ResteasyReactiveContainerRequestContext requestContext, Conta requestContext.resume(new Throwable("ouch")); }); } - LOG.error("Filter response for " + name + " with action: " + action + " done"); + LOG.debug("Filter response for " + name + " with action: " + action + " done"); } @SuppressWarnings("unchecked") diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/TestUtil.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/TestUtil.java index d4369ce804a5a..15ec0dd37d3fb 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/TestUtil.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/TestUtil.java @@ -28,7 +28,7 @@ */ public class TestUtil { - protected static Logger logger; + protected static Logger LOG; private static String baseResourcePath = new StringBuilder() .append("src").append(File.separator) @@ -40,7 +40,7 @@ public class TestUtil { */ static { try { - logger = Logger.getLogger(TestUtil.class.getName()); + LOG = Logger.getLogger(TestUtil.class.getName()); } catch (NoClassDefFoundError e) { // unable to initialize logger, finishContainerPrepare method could not be used } @@ -109,7 +109,7 @@ public static Archive finishContainerPrepare(WebArchive war, Map entry : contextParams.entrySet()) { String paramName = entry.getKey(); String paramValue = entry.getValue(); - logger.info("Context param " + paramName + " value " + paramValue); + LOG.debug("Context param " + paramName + " value " + paramValue); webXml.append("\n"); webXml.append("" + paramName + "\n"); From 1bf0de43f86967006566ae7da138ad851024c588 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Tue, 8 Dec 2020 17:01:26 +0100 Subject: [PATCH 10/62] Remove an unnecessary modification of MetadataSources MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. It's not necessary, because we will contribute annotated classes through the scanner. 2. It's harmful, because getManagedClassNames() can also return *package* names, while annotatedClassNames is exclusively about classes. In the end, this leads to a ClassNotFoundError when Hibernate ORM interprets annotatedClassNames. Regarding the assertion "getManagedClassNames() can also return *package* names", see for proof: - how org.hibernate.boot.archive.scan.internal.ScanResultCollector.isListedOrDetectable is used for packages too, even though it relies (indirectly) on getManagedClassNames(). - the comment at org/hibernate/boot/model/process/internal/ScanningCoordinator.java:246: "IMPL NOTE : "explicitlyListedClassNames" can contain class or package names..." Signed-off-by: Yoann Rodière --- .../orm/runtime/boot/FastBootMetadataBuilder.java | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/FastBootMetadataBuilder.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/FastBootMetadataBuilder.java index 07ad4515b94e1..5aa9a89d754b8 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/FastBootMetadataBuilder.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/FastBootMetadataBuilder.java @@ -154,7 +154,7 @@ public FastBootMetadataBuilder(final QuarkusPersistenceUnitDefinition puDefiniti } final MetadataSources metadataSources = new MetadataSources(ssrBuilder.getBootstrapServiceRegistry()); - addPUManagedClassNamesToMetadataSources(persistenceUnit, metadataSources); + // No need to populate annotatedClassNames/annotatedPackages: they are populated through scanning this.metamodelBuilder = (MetadataBuilderImplementor) metadataSources .getMetadataBuilder(standardServiceRegistry); @@ -186,13 +186,6 @@ public FastBootMetadataBuilder(final QuarkusPersistenceUnitDefinition puDefiniti } - private void addPUManagedClassNamesToMetadataSources(PersistenceUnitDescriptor persistenceUnit, - MetadataSources metadataSources) { - for (String className : persistenceUnit.getManagedClassNames()) { - metadataSources.addAnnotatedClassName(className); - } - } - /** * Simplified copy of * org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl#mergeSettings(org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor, From 8e35c63259cd819c4865ba4e8ff98203681bebfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Tue, 8 Dec 2020 16:25:49 +0100 Subject: [PATCH 11/62] Support package names in PersistenceUnitDescriptor#getManagedClassNames() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit getManagedClassNames() can also return *package* names. See for proof: - how org.hibernate.boot.archive.scan.internal.ScanResultCollector.isListedOrDetectable is used for packages too, even though it relies (indirectly) on getManagedClassNames(). - the comment at org/hibernate/boot/model/process/internal/ScanningCoordinator.java:246: "IMPL NOTE : "explicitlyListedClassNames" can contain class or package names..." Signed-off-by: Yoann Rodière --- .../orm/deployment/HibernateOrmProcessor.java | 45 +++++++++++-------- .../orm/deployment/ProxyBuildingHelper.java | 10 ++++- 2 files changed, 35 insertions(+), 20 deletions(-) diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java index 53ed037f33365..70bb218f70156 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java @@ -362,12 +362,16 @@ public ProxyDefinitionsBuildItem pregenProxies( List persistenceUnitDescriptorBuildItems, BuildProducer generatedClassBuildItemBuildProducer, LiveReloadBuildItem liveReloadBuildItem) { - Set entitiesToGenerateProxiesFor = new HashSet<>(domainObjects.getEntityClassNames()); + Set managedClassAndPackageNames = new HashSet<>(domainObjects.getEntityClassNames()); for (PersistenceUnitDescriptorBuildItem pud : persistenceUnitDescriptorBuildItems) { - entitiesToGenerateProxiesFor.addAll(pud.getManagedClassNames()); - } - PreGeneratedProxies proxyDefinitions = generatedProxies(entitiesToGenerateProxiesFor, indexBuildItem.getIndex(), - generatedClassBuildItemBuildProducer, liveReloadBuildItem); + // Note: getManagedClassNames() can also return *package* names + // See the source code of Hibernate ORM for proof: + // org.hibernate.boot.archive.scan.internal.ScanResultCollector.isListedOrDetectable + // is used for packages too, and it relies (indirectly) on getManagedClassNames(). + managedClassAndPackageNames.addAll(pud.getManagedClassNames()); + } + PreGeneratedProxies proxyDefinitions = generatedProxies(managedClassAndPackageNames, + indexBuildItem.getIndex(), generatedClassBuildItemBuildProducer, liveReloadBuildItem); return new ProxyDefinitionsBuildItem(proxyDefinitions); } @@ -1183,7 +1187,7 @@ private static MultiTenancyStrategy getMultiTenancyStrategy(Optional mul return multiTenancyStrategy; } - private PreGeneratedProxies generatedProxies(Set entityClassNames, IndexView combinedIndex, + private PreGeneratedProxies generatedProxies(Set managedClassAndPackageNames, IndexView combinedIndex, BuildProducer generatedClassBuildItemBuildProducer, LiveReloadBuildItem liveReloadBuildItem) { ProxyCache proxyCache = liveReloadBuildItem.getContextObject(ProxyCache.class); @@ -1209,24 +1213,27 @@ private PreGeneratedProxies generatedProxies(Set entityClassNames, Index proxyAnnotations.put(i.target().asClass().name().toString(), proxyClass.asClass().name().toString()); } try (ProxyBuildingHelper proxyHelper = new ProxyBuildingHelper(Thread.currentThread().getContextClassLoader())) { - for (String entity : entityClassNames) { + for (String managedClassOrPackageName : managedClassAndPackageNames) { CachedProxy result; - if (proxyCache.cache.containsKey(entity) && !isModified(entity, changedClasses, combinedIndex)) { - result = proxyCache.cache.get(entity); + if (proxyCache.cache.containsKey(managedClassOrPackageName) && !isModified(managedClassOrPackageName, changedClasses, combinedIndex)) { + result = proxyCache.cache.get(managedClassOrPackageName); } else { Set> proxyInterfaces = new HashSet<>(); proxyInterfaces.add(HibernateProxy.class); //always added - Class mappedClass = proxyHelper.uninitializedClass(entity); - String proxy = proxyAnnotations.get(entity); - if (proxy != null) { + String proxy = proxyAnnotations.get(managedClassOrPackageName); + if (proxy == null) { + if (!proxyHelper.isProxiable(managedClassOrPackageName)) { + //if there is no @Proxy we need to make sure the actual class is proxiable + continue; + } + } + else { proxyInterfaces.add(proxyHelper.uninitializedClass(proxy)); - } else if (!proxyHelper.isProxiable(mappedClass)) { - //if there is no @Proxy we need to make sure the actual class is proxiable - continue; } - for (ClassInfo subclass : combinedIndex.getAllKnownSubclasses(DotName.createSimple(entity))) { + Class mappedClass = proxyHelper.uninitializedClass(managedClassOrPackageName); + for (ClassInfo subclass : combinedIndex.getAllKnownSubclasses(DotName.createSimple(managedClassOrPackageName))) { String subclassName = subclass.name().toString(); - if (!entityClassNames.contains(subclassName)) { + if (!managedClassAndPackageNames.contains(subclassName)) { //not an entity continue; } @@ -1239,13 +1246,13 @@ private PreGeneratedProxies generatedProxies(Set entityClassNames, Index toArray(proxyInterfaces)); result = new CachedProxy(unloaded, proxyInterfaces.stream().map(Class::getName).collect(Collectors.toSet())); - proxyCache.cache.put(entity, result); + proxyCache.cache.put(managedClassOrPackageName, result); } for (Entry i : result.proxyDef.getAllTypes().entrySet()) { generatedClassBuildItemBuildProducer .produce(new GeneratedClassBuildItem(true, i.getKey().getName(), i.getValue())); } - preGeneratedProxies.getProxies().put(entity, + preGeneratedProxies.getProxies().put(managedClassOrPackageName, new PreGeneratedProxies.ProxyClassDetailsHolder(result.proxyDef.getTypeDescription().getName(), result.interfaces)); } diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/ProxyBuildingHelper.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/ProxyBuildingHelper.java index bcdba4e5a0e34..5c4070262887d 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/ProxyBuildingHelper.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/ProxyBuildingHelper.java @@ -43,7 +43,15 @@ public Class uninitializedClass(String entity) { } } - public boolean isProxiable(Class mappedClass) { + public boolean isProxiable(String managedClassOrPackageName) { + Class mappedClass; + try { + mappedClass = Class.forName(managedClassOrPackageName, false, contextClassLoader); + } catch (ClassNotFoundException e) { + // Probably a package name - consider it's not proxiable. + return false; + } + if (Modifier.isFinal(mappedClass.getModifiers())) { return false; } From 7f0d9f42119960c5ae4dcf968ae71ab58235041d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Tue, 8 Dec 2020 15:26:24 +0100 Subject: [PATCH 12/62] Scan for packages in the Hibernate ORM extension MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yoann Rodière --- .../deployment/HibernateOrmAnnotations.java | 46 +++++++++++ .../orm/deployment/HibernateOrmProcessor.java | 79 +++++++++++++------ .../orm/deployment/JpaEntitiesBuildItem.java | 12 +++ .../orm/deployment/JpaJandexScavenger.java | 33 ++++++++ .../orm/runtime/boot/scan/QuarkusScanner.java | 53 ++++++++++++- 5 files changed, 195 insertions(+), 28 deletions(-) create mode 100644 extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmAnnotations.java diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmAnnotations.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmAnnotations.java new file mode 100644 index 0000000000000..45e00130cf9c9 --- /dev/null +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmAnnotations.java @@ -0,0 +1,46 @@ +package io.quarkus.hibernate.orm.deployment; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.hibernate.annotations.AnyMetaDef; +import org.hibernate.annotations.AnyMetaDefs; +import org.hibernate.annotations.FetchProfile; +import org.hibernate.annotations.FetchProfiles; +import org.hibernate.annotations.FilterDef; +import org.hibernate.annotations.FilterDefs; +import org.hibernate.annotations.GenericGenerator; +import org.hibernate.annotations.GenericGenerators; +import org.hibernate.annotations.ListIndexBase; +import org.hibernate.annotations.NamedNativeQueries; +import org.hibernate.annotations.NamedNativeQuery; +import org.hibernate.annotations.NamedQueries; +import org.hibernate.annotations.NamedQuery; +import org.hibernate.annotations.TypeDef; +import org.hibernate.annotations.TypeDefs; +import org.jboss.jandex.DotName; + +public final class HibernateOrmAnnotations { + + private HibernateOrmAnnotations() { + } + + public static final List PACKAGE_ANNOTATIONS = Collections.unmodifiableList(Arrays.asList( + DotName.createSimple(AnyMetaDef.class.getName()), + DotName.createSimple(AnyMetaDefs.class.getName()), + DotName.createSimple(FetchProfile.class.getName()), + DotName.createSimple(FetchProfile.FetchOverride.class.getName()), + DotName.createSimple(FetchProfiles.class.getName()), + DotName.createSimple(FilterDef.class.getName()), + DotName.createSimple(FilterDefs.class.getName()), + DotName.createSimple(GenericGenerator.class.getName()), + DotName.createSimple(GenericGenerators.class.getName()), + DotName.createSimple(ListIndexBase.class.getName()), + DotName.createSimple(NamedNativeQueries.class.getName()), + DotName.createSimple(NamedNativeQuery.class.getName()), + DotName.createSimple(NamedQueries.class.getName()), + DotName.createSimple(NamedQuery.class.getName()), + DotName.createSimple(TypeDef.class.getName()), + DotName.createSimple(TypeDefs.class.getName()))); +} diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java index 70bb218f70156..28527979f0850 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java @@ -41,6 +41,7 @@ import org.hibernate.MultiTenancyStrategy; import org.hibernate.annotations.Proxy; import org.hibernate.boot.archive.scan.spi.ClassDescriptor; +import org.hibernate.boot.archive.scan.spi.PackageDescriptor; import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.beanvalidation.BeanValidationIntegrator; import org.hibernate.dialect.DB297Dialect; @@ -662,9 +663,9 @@ private void handleHibernateORMWithNoPersistenceXml( && hibernateOrmConfig.persistenceUnits.isEmpty()) || hibernateOrmConfig.defaultPersistenceUnit.isAnyPropertySet(); - Map> modelClassesPerPersistencesUnits = getModelClassesPerPersistenceUnits(hibernateOrmConfig, - jpaEntities, index.getIndex(), enableDefaultPersistenceUnit); - Set modelClassesForDefaultPersistenceUnit = modelClassesPerPersistencesUnits + Map> modelClassesAndPackagesPerPersistencesUnits = getModelClassesAndPackagesPerPersistenceUnits( + hibernateOrmConfig, jpaEntities, index.getIndex(), enableDefaultPersistenceUnit); + Set modelClassesAndPackagesForDefaultPersistenceUnit = modelClassesAndPackagesPerPersistencesUnits .getOrDefault(PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME, Collections.emptySet()); Set storageEngineCollector = new HashSet<>(); @@ -673,11 +674,11 @@ private void handleHibernateORMWithNoPersistenceXml( producePersistenceUnitDescriptorFromConfig( hibernateOrmConfig, PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME, hibernateOrmConfig.defaultPersistenceUnit, - modelClassesForDefaultPersistenceUnit, + modelClassesAndPackagesForDefaultPersistenceUnit, jdbcDataSources, applicationArchivesBuildItem, launchMode, capabilities, systemProperties, nativeImageResources, hotDeploymentWatchedFiles, persistenceUnitDescriptors, storageEngineCollector); - } else if (!modelClassesForDefaultPersistenceUnit.isEmpty() + } else if (!modelClassesAndPackagesForDefaultPersistenceUnit.isEmpty() && (!hibernateOrmConfig.defaultPersistenceUnit.datasource.isPresent() || DataSourceUtil.isDefault(hibernateOrmConfig.defaultPersistenceUnit.datasource.get())) && !defaultJdbcDataSource.isPresent()) { @@ -689,7 +690,8 @@ private void handleHibernateORMWithNoPersistenceXml( .entrySet()) { producePersistenceUnitDescriptorFromConfig( hibernateOrmConfig, persistenceUnitEntry.getKey(), persistenceUnitEntry.getValue(), - modelClassesPerPersistencesUnits.getOrDefault(persistenceUnitEntry.getKey(), Collections.emptySet()), + modelClassesAndPackagesPerPersistencesUnits.getOrDefault(persistenceUnitEntry.getKey(), + Collections.emptySet()), jdbcDataSources, applicationArchivesBuildItem, launchMode, capabilities, systemProperties, nativeImageResources, hotDeploymentWatchedFiles, persistenceUnitDescriptors, storageEngineCollector); @@ -705,7 +707,7 @@ private static void producePersistenceUnitDescriptorFromConfig( HibernateOrmConfig hibernateOrmConfig, String persistenceUnitName, HibernateOrmConfigPersistenceUnit persistenceUnitConfig, - Set modelClasses, + Set modelClassesAndPackages, List jdbcDataSources, ApplicationArchivesBuildItem applicationArchivesBuildItem, LaunchMode launchMode, @@ -756,10 +758,20 @@ private static void producePersistenceUnitDescriptorFromConfig( descriptor.setName(persistenceUnitName); descriptor.setExcludeUnlistedClasses(true); - if (modelClasses.isEmpty()) { + if (modelClassesAndPackages.isEmpty()) { LOG.warnf("Could not find any entities affected to the persistence unit '%s'.", persistenceUnitName); } else { - descriptor.addClasses(new ArrayList<>(modelClasses)); + // That's right, we're pushing both class names and package names + // to a method called "addClasses". + // It's a misnomer: while the method populates the set that backs getManagedClasses(), + // that method is also poorly named because it can actually return both class names + // and package names. + // See for proof: + // - how org.hibernate.boot.archive.scan.internal.ScanResultCollector.isListedOrDetectable + // is used for packages too, even though it relies (indirectly) on getManagedClassNames(). + // - the comment at org/hibernate/boot/model/process/internal/ScanningCoordinator.java:246: + // "IMPL NOTE : "explicitlyListedClassNames" can contain class or package names..." + descriptor.addClasses(new ArrayList<>(modelClassesAndPackages)); } descriptor.setTransactionType(PersistenceUnitTransactionType.JTA); @@ -959,16 +971,17 @@ private void enhanceEntities(final JpaEntitiesBuildItem domainObjects, } } - private static Map> getModelClassesPerPersistenceUnits(HibernateOrmConfig hibernateOrmConfig, + private static Map> getModelClassesAndPackagesPerPersistenceUnits(HibernateOrmConfig hibernateOrmConfig, JpaEntitiesBuildItem jpaEntities, IndexView index, boolean enableDefaultPersistenceUnit) { if (hibernateOrmConfig.persistenceUnits.isEmpty()) { // no named persistence units, all the entities will be associated with the default one // so we don't need to split them - return Collections.singletonMap(PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME, - jpaEntities.getAllModelClassNames()); + Set allModelClassesAndPackages = new HashSet<>(jpaEntities.getAllModelClassNames()); + allModelClassesAndPackages.addAll(jpaEntities.getAllModelPackageNames()); + return Collections.singletonMap(PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME, allModelClassesAndPackages); } - Map> modelClassesPerPersistenceUnits = new HashMap<>(); + Map> modelClassesAndPackagesPerPersistenceUnits = new HashMap<>(); boolean hasPackagesInQuarkusConfig = hasPackagesInQuarkusConfig(hibernateOrmConfig); Collection packageLevelPersistenceUnitAnnotations = getPackageLevelPersistenceUnitAnnotations( @@ -1046,14 +1059,14 @@ private static Map> getModelClassesPerPersistenceUnits(Hiber for (Entry> packageRuleEntry : packageRules.entrySet()) { if (modelClassName.startsWith(packageRuleEntry.getKey())) { for (String persistenceUnitName : packageRuleEntry.getValue()) { - modelClassesPerPersistenceUnits.putIfAbsent(persistenceUnitName, new HashSet<>()); - modelClassesPerPersistenceUnits.get(persistenceUnitName).add(modelClassName); + modelClassesAndPackagesPerPersistenceUnits.putIfAbsent(persistenceUnitName, new HashSet<>()); + modelClassesAndPackagesPerPersistenceUnits.get(persistenceUnitName).add(modelClassName); // also add the hierarchy to the persistence unit // we would need to add all the underlying model to it but adding the hierarchy // is necessary for Panache as we need to add PanacheEntity to the PU for (String relatedModelClassName : relatedModelClassNames) { - modelClassesPerPersistenceUnits.get(persistenceUnitName).add(relatedModelClassName); + modelClassesAndPackagesPerPersistenceUnits.get(persistenceUnitName).add(relatedModelClassName); } } } @@ -1066,7 +1079,7 @@ private static Map> getModelClassesPerPersistenceUnits(Hiber String.join("\n\t- ", modelClassesWithPersistenceUnitAnnotations))); } - Set affectedModelClasses = modelClassesPerPersistenceUnits.values().stream().flatMap(Set::stream) + Set affectedModelClasses = modelClassesAndPackagesPerPersistenceUnits.values().stream().flatMap(Set::stream) .collect(Collectors.toSet()); Set unaffectedModelClasses = jpaEntities.getAllModelClassNames().stream() .filter(c -> !affectedModelClasses.contains(c)) @@ -1076,7 +1089,18 @@ private static Map> getModelClassesPerPersistenceUnits(Hiber String.join("\n\t- ", unaffectedModelClasses)); } - return modelClassesPerPersistenceUnits; + for (String modelPackageName : jpaEntities.getAllModelPackageNames()) { + Set persistenceUnitNames = packageRules.get(modelPackageName); + if (persistenceUnitNames == null) { + continue; + } + for (String persistenceUnitName : persistenceUnitNames) { + modelClassesAndPackagesPerPersistenceUnits.putIfAbsent(persistenceUnitName, new HashSet<>()); + modelClassesAndPackagesPerPersistenceUnits.get(persistenceUnitName).add(modelPackageName); + } + } + + return modelClassesAndPackagesPerPersistenceUnits; } private static Set getRelatedModelClassNames(IndexView index, Set knownModelClassNames, @@ -1165,9 +1189,15 @@ private boolean shouldIgnorePersistenceXmlResources() { */ public static QuarkusScanner buildQuarkusScanner(JpaEntitiesBuildItem domainObjects) { QuarkusScanner scanner = new QuarkusScanner(); + Set packageDescriptors = new HashSet<>(); + for (String packageName : domainObjects.getAllModelPackageNames()) { + QuarkusScanner.PackageDescriptorImpl desc = new QuarkusScanner.PackageDescriptorImpl(packageName); + packageDescriptors.add(desc); + } + scanner.setPackageDescriptors(packageDescriptors); Set classDescriptors = new HashSet<>(); - for (String i : domainObjects.getAllModelClassNames()) { - QuarkusScanner.ClassDescriptorImpl desc = new QuarkusScanner.ClassDescriptorImpl(i, + for (String className : domainObjects.getAllModelClassNames()) { + QuarkusScanner.ClassDescriptorImpl desc = new QuarkusScanner.ClassDescriptorImpl(className, ClassDescriptor.Categorization.MODEL); classDescriptors.add(desc); } @@ -1215,7 +1245,8 @@ private PreGeneratedProxies generatedProxies(Set managedClassAndPackageN try (ProxyBuildingHelper proxyHelper = new ProxyBuildingHelper(Thread.currentThread().getContextClassLoader())) { for (String managedClassOrPackageName : managedClassAndPackageNames) { CachedProxy result; - if (proxyCache.cache.containsKey(managedClassOrPackageName) && !isModified(managedClassOrPackageName, changedClasses, combinedIndex)) { + if (proxyCache.cache.containsKey(managedClassOrPackageName) + && !isModified(managedClassOrPackageName, changedClasses, combinedIndex)) { result = proxyCache.cache.get(managedClassOrPackageName); } else { Set> proxyInterfaces = new HashSet<>(); @@ -1226,12 +1257,12 @@ private PreGeneratedProxies generatedProxies(Set managedClassAndPackageN //if there is no @Proxy we need to make sure the actual class is proxiable continue; } - } - else { + } else { proxyInterfaces.add(proxyHelper.uninitializedClass(proxy)); } Class mappedClass = proxyHelper.uninitializedClass(managedClassOrPackageName); - for (ClassInfo subclass : combinedIndex.getAllKnownSubclasses(DotName.createSimple(managedClassOrPackageName))) { + for (ClassInfo subclass : combinedIndex + .getAllKnownSubclasses(DotName.createSimple(managedClassOrPackageName))) { String subclassName = subclass.name().toString(); if (!managedClassAndPackageNames.contains(subclassName)) { //not an entity diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/JpaEntitiesBuildItem.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/JpaEntitiesBuildItem.java index 94e27eed18c6b..84de37d9c3dd0 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/JpaEntitiesBuildItem.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/JpaEntitiesBuildItem.java @@ -15,9 +15,14 @@ */ public final class JpaEntitiesBuildItem extends SimpleBuildItem { + private final Set allModelPackageNames = new HashSet(); private final Set entityClassNames = new HashSet(); private final Set allModelClassNames = new HashSet(); + public void addModelPackage(String packageName) { + allModelPackageNames.add(packageName); + } + void addEntityClass(final String className) { entityClassNames.add(className); allModelClassNames.add(className); @@ -33,6 +38,13 @@ void registerAllForReflection(final BuildProducer refl } } + /** + * @return the list of packages annotated with a JPA annotation. + */ + public Set getAllModelPackageNames() { + return allModelPackageNames; + } + /** * @return the list of entities (i.e. classes marked with {@link Entity}) */ diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/JpaJandexScavenger.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/JpaJandexScavenger.java index e99d4650fdf71..1387ca6e17cc6 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/JpaJandexScavenger.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/JpaJandexScavenger.java @@ -75,6 +75,9 @@ public JpaEntitiesBuildItem discoverModelAndRegisterForReflection() throws IOExc final Set javaTypeCollector = new HashSet<>(); final Set unindexedClasses = new TreeSet<>(); + for (DotName packageAnnotation : HibernateOrmAnnotations.PACKAGE_ANNOTATIONS) { + enlistJPAModelAnnotatedPackages(indexView, domainObjectCollector, packageAnnotation); + } enlistJPAModelClasses(indexView, domainObjectCollector, enumTypeCollector, javaTypeCollector, JPA_ENTITY, unindexedClasses); enlistJPAModelClasses(indexView, domainObjectCollector, enumTypeCollector, javaTypeCollector, EMBEDDABLE, @@ -170,6 +173,30 @@ private static void enlistEmbeddedsAndElementCollections(IndexView index, JpaEnt } } + private void enlistJPAModelAnnotatedPackages(IndexView index, JpaEntitiesBuildItem domainObjectCollector, DotName dotName) { + Collection jpaAnnotations = index.getAnnotations(dotName); + + if (jpaAnnotations == null) { + return; + } + + for (AnnotationInstance annotation : jpaAnnotations) { + if (annotation.target().kind() != AnnotationTarget.Kind.CLASS) { + continue; // Annotation on field, method, etc. + } + ClassInfo klass = annotation.target().asClass(); + if (!klass.simpleName().equals("package-info")) { + continue; // Annotation on an actual class, not a package. + } + DotName targetDotName = klass.name(); + // ignore non-jpa model classes that we think belong to JPA + if (nonJpaModelClasses.contains(targetDotName.toString())) { + continue; + } + collectPackage(domainObjectCollector, klass); + } + } + private void enlistJPAModelClasses(IndexView index, JpaEntitiesBuildItem domainObjectCollector, Set enumTypeCollector, Set javaTypeCollector, DotName dotName, Set unindexedClasses) { Collection jpaAnnotations = index.getAnnotations(dotName); @@ -240,6 +267,12 @@ private static void addClassHierarchyToReflectiveList(IndexView index, JpaEntiti } } + private static void collectPackage(JpaEntitiesBuildItem domainObjectCollector, ClassInfo classOrPackageInfo) { + String classOrPackageInfoName = classOrPackageInfo.name().toString(); + String packageName = classOrPackageInfoName.substring(0, classOrPackageInfoName.lastIndexOf('.')); + domainObjectCollector.addModelPackage(packageName); + } + private static void collectDomainObject(JpaEntitiesBuildItem domainObjectCollector, ClassInfo modelClass) { if (modelClass.classAnnotation(JPA_ENTITY) != null) { domainObjectCollector.addEntityClass(modelClass.name().toString()); diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/scan/QuarkusScanner.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/scan/QuarkusScanner.java index 59037c0df8665..b00343709b9ad 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/scan/QuarkusScanner.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/scan/QuarkusScanner.java @@ -23,11 +23,20 @@ */ public class QuarkusScanner implements Scanner { + private Set packageDescriptors; private Set classDescriptors; @Override public ScanResult scan(ScanEnvironment scanEnvironment, ScanOptions scanOptions, ScanParameters scanParameters) { - return new Result(classDescriptors, scanEnvironment, scanOptions); + return new Result(packageDescriptors, classDescriptors, scanEnvironment, scanOptions); + } + + public Set getPackageDescriptors() { + return packageDescriptors; + } + + public void setPackageDescriptors(Set packageDescriptors) { + this.packageDescriptors = packageDescriptors; } public Set getClassDescriptors() { @@ -40,11 +49,21 @@ public void setClassDescriptors(Set classDescriptors) { public static class Result implements ScanResult { + private final Set selectedPackageDescriptors; private final Set selectedClassDescriptors; - Result(Set classDescriptors, ScanEnvironment scanEnvironment, ScanOptions scanOptions) { + Result(Set packageDescriptors, Set classDescriptors, + ScanEnvironment scanEnvironment, ScanOptions scanOptions) { + this.selectedPackageDescriptors = new HashSet<>(); this.selectedClassDescriptors = new HashSet<>(); + for (PackageDescriptor packageDescriptor : packageDescriptors) { + if (scanOptions.canDetectUnlistedClassesInRoot() || + scanEnvironment.getExplicitlyListedClassNames().contains(packageDescriptor.getName())) { + this.selectedPackageDescriptors.add(packageDescriptor); + } + } + for (ClassDescriptor classDescriptor : classDescriptors) { if (scanOptions.canDetectUnlistedClassesInRoot() || scanEnvironment.getExplicitlyListedClassNames().contains(classDescriptor.getName())) { @@ -55,8 +74,7 @@ public static class Result implements ScanResult { @Override public Set getLocatedPackages() { - //todo: handle packages - return Collections.emptySet(); + return selectedPackageDescriptors; } @Override @@ -71,6 +89,33 @@ public Set getLocatedMappingFiles() { } } + public static class PackageDescriptorImpl implements PackageDescriptor { + + private String name; + + public PackageDescriptorImpl(String name) { + this.name = name; + } + + public PackageDescriptorImpl() { + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String getName() { + return name; + } + + @Override + public InputStreamAccess getStreamAccess() { + return new UrlInputStreamAccess( + Thread.currentThread().getContextClassLoader().getResource(name.replace('.', '/') + "/package-info.class")); + } + } + public static class ClassDescriptorImpl implements ClassDescriptor { private String name; From b8042547575d2d7e52dd5bd99bb0a290c5f0798a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Tue, 8 Dec 2020 12:51:51 +0100 Subject: [PATCH 13/62] Test package-level annotations in the ORM extension MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yoann Rodière --- .../hibernate/orm/packages/ChildEntity1.java | 43 +++++++++++ .../hibernate/orm/packages/ChildEntity2.java | 43 +++++++++++ .../packages/PackageLevelAnnotationTest.java | 73 +++++++++++++++++++ .../hibernate/orm/packages/ParentEntity.java | 59 +++++++++++++++ .../hibernate/orm/packages/package-info.java | 8 ++ 5 files changed, 226 insertions(+) create mode 100644 extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/packages/ChildEntity1.java create mode 100644 extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/packages/ChildEntity2.java create mode 100644 extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/packages/PackageLevelAnnotationTest.java create mode 100644 extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/packages/ParentEntity.java create mode 100644 extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/packages/package-info.java diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/packages/ChildEntity1.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/packages/ChildEntity1.java new file mode 100644 index 0000000000000..9f83eee305db2 --- /dev/null +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/packages/ChildEntity1.java @@ -0,0 +1,43 @@ +package io.quarkus.hibernate.orm.packages; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +@Entity +public class ChildEntity1 { + + @Id + @GeneratedValue + private long id; + + private String name; + + public ChildEntity1() { + } + + @Override + public String toString() { + return "ChildEntity1:" + name; + } + + public ChildEntity1(String name) { + this.name = name; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/packages/ChildEntity2.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/packages/ChildEntity2.java new file mode 100644 index 0000000000000..665d572744948 --- /dev/null +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/packages/ChildEntity2.java @@ -0,0 +1,43 @@ +package io.quarkus.hibernate.orm.packages; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +@Entity +public class ChildEntity2 { + + @Id + @GeneratedValue + private long id; + + private String name; + + public ChildEntity2() { + } + + @Override + public String toString() { + return "ChildEntity1:" + name; + } + + public ChildEntity2(String name) { + this.name = name; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/packages/PackageLevelAnnotationTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/packages/PackageLevelAnnotationTest.java new file mode 100644 index 0000000000000..8272eb753d774 --- /dev/null +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/packages/PackageLevelAnnotationTest.java @@ -0,0 +1,73 @@ +package io.quarkus.hibernate.orm.packages; + +import static org.assertj.core.api.Assertions.assertThat; + +import javax.inject.Inject; +import javax.persistence.EntityManager; +import javax.transaction.NotSupportedException; +import javax.transaction.SystemException; +import javax.transaction.UserTransaction; + +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; + +public class PackageLevelAnnotationTest { + + @RegisterExtension + static QuarkusUnitTest runner = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addPackage(PackageLevelAnnotationTest.class.getPackage()) + .addAsResource("application.properties")); + + @Inject + EntityManager entityManager; + + @Inject + UserTransaction transaction; + + @Test + public void test() { + // If we get here, the package-level @AnyMetaDef was correctly detected: + // otherwise, we would have had a failure on ORM bootstrap. + + ParentEntity parent1 = new ParentEntity("parent1"); + ParentEntity parent2 = new ParentEntity("parent2"); + ChildEntity1 child1 = new ChildEntity1("child1"); + ChildEntity2 child2 = new ChildEntity2("child2"); + parent1.setChild(child1); + parent2.setChild(child2); + + inTransaction(() -> { + entityManager.persist(child1); + entityManager.persist(child2); + entityManager.persist(parent1); + entityManager.persist(parent2); + }); + + // Check that the @Any relation works correctly, just in case + inTransaction(() -> { + ParentEntity savedParent1 = entityManager.find(ParentEntity.class, parent1.getId()); + assertThat(savedParent1.getChild()).isInstanceOf(ChildEntity1.class); + ParentEntity savedParent2 = entityManager.find(ParentEntity.class, parent2.getId()); + assertThat(savedParent2.getChild()).isInstanceOf(ChildEntity2.class); + }); + } + + private void inTransaction(Runnable runnable) { + try { + transaction.begin(); + try { + runnable.run(); + transaction.commit(); + } catch (Exception e) { + transaction.rollback(); + } + } catch (SystemException | NotSupportedException e) { + throw new IllegalStateException("Transaction exception", e); + } + } +} diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/packages/ParentEntity.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/packages/ParentEntity.java new file mode 100644 index 0000000000000..747a825970d3a --- /dev/null +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/packages/ParentEntity.java @@ -0,0 +1,59 @@ +package io.quarkus.hibernate.orm.packages; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.JoinColumn; + +import org.hibernate.annotations.Any; + +@Entity +public class ParentEntity { + + @Id + @GeneratedValue + private long id; + + private String name; + + @Any(metaDef = "childrenAnyMetaDef", metaColumn = @Column(name = "child_type")) + @JoinColumn(name = "child_id") + private Object child; + + public ParentEntity() { + } + + public ParentEntity(String name) { + this.name = name; + } + + @Override + public String toString() { + return "ParentEntity:" + name; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Object getChild() { + return child; + } + + public void setChild(Object child) { + this.child = child; + } +} diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/packages/package-info.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/packages/package-info.java new file mode 100644 index 0000000000000..419886f36205b --- /dev/null +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/packages/package-info.java @@ -0,0 +1,8 @@ +@AnyMetaDef(name = "childrenAnyMetaDef", idType = "long", metaType = "string", metaValues = { + @MetaValue(targetEntity = ChildEntity1.class, value = "child1"), + @MetaValue(targetEntity = ChildEntity2.class, value = "child2") +}) +package io.quarkus.hibernate.orm.packages; + +import org.hibernate.annotations.AnyMetaDef; +import org.hibernate.annotations.MetaValue; \ No newline at end of file From c579cd35fb41479d36170c939d6e2291d06ef66f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Tue, 8 Dec 2020 17:44:40 +0100 Subject: [PATCH 14/62] Move a few indexing utils to IndexingUtil MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yoann Rodière --- .../index/ApplicationArchiveBuildStep.java | 47 ++--------------- .../deployment/index/IndexingUtil.java | 51 +++++++++++++++++++ 2 files changed, 55 insertions(+), 43 deletions(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/index/ApplicationArchiveBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/index/ApplicationArchiveBuildStep.java index 4c703eb7360d4..4154dd75786cf 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/index/ApplicationArchiveBuildStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/index/ApplicationArchiveBuildStep.java @@ -2,28 +2,22 @@ import java.io.FileInputStream; import java.io.IOException; -import java.io.InputStream; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.ProviderNotFoundException; import java.util.ArrayList; -import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Function; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; import java.util.stream.Stream; -import java.util.zip.ZipEntry; import org.jboss.jandex.CompositeIndex; import org.jboss.jandex.Index; -import org.jboss.jandex.IndexReader; import org.jboss.jandex.IndexView; import org.jboss.jandex.Indexer; import org.jboss.logging.Logger; @@ -55,11 +49,6 @@ public class ApplicationArchiveBuildStep { private static final Logger LOGGER = Logger.getLogger(ApplicationArchiveBuildStep.class); - private static final String JANDEX_INDEX = "META-INF/jandex.idx"; - - // At least Jandex 2.1 is needed - private static final int REQUIRED_INDEX_VERSION = 8; - IndexDependencyConfiguration config; @ConfigRoot(phase = ConfigPhase.BUILD_TIME) @@ -125,7 +114,7 @@ private List scanForOtherIndexes(QuarkusBuildCloseablesBuild //get paths that are included via marker files Set markers = new HashSet<>(applicationArchiveFiles); - markers.add(JANDEX_INDEX); + markers.add(IndexingUtil.JANDEX_INDEX); addMarkerFilePaths(markers, root, curateOutcomeBuildItem, indexedPaths, appArchives, buildCloseables, classLoader, indexCache); @@ -271,26 +260,12 @@ private static Index indexFilePath(Path path) throws IOException { return indexer.complete(); } - private static Index handleJarPath(Path path, IndexCache indexCache) throws IOException { + private static Index handleJarPath(Path path, IndexCache indexCache) { return indexCache.cache.computeIfAbsent(path, new Function() { @Override public Index apply(Path path) { - try (JarFile file = new JarFile(path.toFile())) { - ZipEntry existing = file.getEntry(JANDEX_INDEX); - if (existing != null) { - try (InputStream in = file.getInputStream(existing)) { - IndexReader reader = new IndexReader(in); - if (reader.getIndexVersion() < REQUIRED_INDEX_VERSION) { - LOGGER.warnf( - "Re-indexing %s - at least Jandex 2.1 must be used to index an application dependency", - path); - return indexJar(file); - } else { - return reader.read(); - } - } - } - return indexJar(file); + try { + return IndexingUtil.indexJar(path); } catch (IOException e) { throw new RuntimeException("Failed to process " + path, e); } @@ -298,20 +273,6 @@ public Index apply(Path path) { }); } - private static Index indexJar(JarFile file) throws IOException { - Indexer indexer = new Indexer(); - Enumeration e = file.entries(); - while (e.hasMoreElements()) { - JarEntry entry = e.nextElement(); - if (entry.getName().endsWith(".class")) { - try (InputStream inputStream = file.getInputStream(entry)) { - indexer.index(inputStream); - } - } - } - return indexer.complete(); - } - /** * When running in hot deployment mode we know that java archives will never change, there is no need * to re-index them each time. We cache them here to reduce the hot reload time. diff --git a/core/deployment/src/main/java/io/quarkus/deployment/index/IndexingUtil.java b/core/deployment/src/main/java/io/quarkus/deployment/index/IndexingUtil.java index 832382496ef45..c8952642a0930 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/index/IndexingUtil.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/index/IndexingUtil.java @@ -1,12 +1,20 @@ package io.quarkus.deployment.index; import java.io.ByteArrayInputStream; +import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.nio.file.Path; +import java.util.Enumeration; import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.zip.ZipEntry; import org.jboss.jandex.ClassInfo; import org.jboss.jandex.DotName; +import org.jboss.jandex.Index; +import org.jboss.jandex.IndexReader; import org.jboss.jandex.IndexView; import org.jboss.jandex.Indexer; import org.jboss.logging.Logger; @@ -19,6 +27,49 @@ public class IndexingUtil { public static final DotName OBJECT = DotName.createSimple(Object.class.getName()); + public static final String JANDEX_INDEX = "META-INF/jandex.idx"; + + // At least Jandex 2.1 is needed + private static final int REQUIRED_INDEX_VERSION = 8; + + public static Index indexJar(Path path) throws IOException { + return indexJar(path.toFile()); + } + + public static Index indexJar(File file) throws IOException { + try (JarFile jarFile = new JarFile(file)) { + ZipEntry existing = jarFile.getEntry(JANDEX_INDEX); + if (existing != null) { + try (InputStream in = jarFile.getInputStream(existing)) { + IndexReader reader = new IndexReader(in); + if (reader.getIndexVersion() < REQUIRED_INDEX_VERSION) { + log.warnf( + "Re-indexing %s - at least Jandex 2.1 must be used to index an application dependency", + file); + return indexJar(jarFile); + } else { + return reader.read(); + } + } + } + return indexJar(jarFile); + } + } + + private static Index indexJar(JarFile file) throws IOException { + Indexer indexer = new Indexer(); + Enumeration e = file.entries(); + while (e.hasMoreElements()) { + JarEntry entry = e.nextElement(); + if (entry.getName().endsWith(".class")) { + try (InputStream inputStream = file.getInputStream(entry)) { + indexer.index(inputStream); + } + } + } + return indexer.complete(); + } + public static void indexClass(String beanClass, Indexer indexer, IndexView quarkusIndex, Set additionalIndex, ClassLoader classLoader) { DotName beanClassName = DotName.createSimple(beanClass); From bfc5da6dfdd223d35d31dba1a1b601b9489a9c9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Tue, 8 Dec 2020 18:21:20 +0100 Subject: [PATCH 15/62] Test that hardcoded lists of Hibernate ORM annotations stay up-to-date MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yoann Rodière --- .../orm/HibernateOrmAnnotationsTest.java | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/HibernateOrmAnnotationsTest.java diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/HibernateOrmAnnotationsTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/HibernateOrmAnnotationsTest.java new file mode 100644 index 0000000000000..cade2c7e2eeee --- /dev/null +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/HibernateOrmAnnotationsTest.java @@ -0,0 +1,80 @@ +package io.quarkus.hibernate.orm; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.File; +import java.io.IOException; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.net.URL; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.hibernate.Hibernate; +import org.jboss.jandex.AnnotationInstance; +import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.DotName; +import org.jboss.jandex.Index; +import org.junit.jupiter.api.Test; + +import io.quarkus.deployment.index.IndexingUtil; +import io.quarkus.hibernate.orm.deployment.HibernateOrmAnnotations; + +/** + * Test that hardcoded lists of Hibernate ORM annotations stay up-to-date. + */ +public class HibernateOrmAnnotationsTest { + + private static final DotName RETENTION = DotName.createSimple(Retention.class.getName()); + private static final DotName TARGET = DotName.createSimple(Target.class.getName()); + + @Test + public void testNoMissingPackageLevelAnnotation() throws IOException { + Index index = indexHibernateJar(); + + Set packageLevelHibernateAnnotations = findRuntimeAnnotationsByTargetType(index, ElementType.PACKAGE); + packageLevelHibernateAnnotations.removeIf(name -> name.toString().contains(".internal.")); + + assertThat(HibernateOrmAnnotations.PACKAGE_ANNOTATIONS) + .containsExactlyInAnyOrderElementsOf(packageLevelHibernateAnnotations); + } + + private Set findRuntimeAnnotationsByTargetType(Index index, ElementType targetType) { + Set annotations = new HashSet<>(); + for (AnnotationInstance retentionAnnotation : index.getAnnotations(RETENTION)) { + ClassInfo annotation = retentionAnnotation.target().asClass(); + if (RetentionPolicy.RUNTIME.name().equals(retentionAnnotation.value().asEnum()) + && allowsTargetType(annotation, targetType)) { + annotations.add(annotation.name()); + } + } + return annotations; + } + + private boolean allowsTargetType(ClassInfo annotation, ElementType targetType) { + AnnotationInstance targetAnnotation = annotation.classAnnotation(TARGET); + if (targetAnnotation == null) { + // Can target anything + return true; + } + + List allowedTargetTypes = Arrays.asList(targetAnnotation.value().asEnumArray()); + return allowedTargetTypes.contains(targetType.name()); + } + + private Index indexHibernateJar() throws IOException { + return IndexingUtil.indexJar(determineHibernateJarLocation()); + } + + private File determineHibernateJarLocation() { + URL url = Hibernate.class.getProtectionDomain().getCodeSource().getLocation(); + if (!url.getProtocol().equals("file")) { + throw new IllegalStateException("Hibernate JAR is not a local file? " + url); + } + return new File(url.getPath()); + } +} From 8ac4edbdcff8bdc04cb9c5b719de6ae17c5d03cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Tue, 8 Dec 2020 17:53:39 +0100 Subject: [PATCH 16/62] Fix NPE in BytecodeRecorderImpl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yoann Rodière --- .../io/quarkus/deployment/recording/BytecodeRecorderImpl.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/recording/BytecodeRecorderImpl.java b/core/deployment/src/main/java/io/quarkus/deployment/recording/BytecodeRecorderImpl.java index 7132600260109..179a88e065d63 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/recording/BytecodeRecorderImpl.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/recording/BytecodeRecorderImpl.java @@ -1107,7 +1107,9 @@ private DeferredParameter loadComplexObject(Object param, Map Date: Tue, 8 Dec 2020 22:04:23 +0000 Subject: [PATCH 17/62] Bump flyway-core from 7.3.0 to 7.3.1 Bumps [flyway-core](https://github.com/flyway/flyway) from 7.3.0 to 7.3.1. - [Release notes](https://github.com/flyway/flyway/releases) - [Commits](https://github.com/flyway/flyway/compare/flyway-7.3.0...flyway-7.3.1) Signed-off-by: dependabot[bot] --- bom/application/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 89774fc3ea5d7..5325ec71928ef 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -160,7 +160,7 @@ 1.6.5 1.0.6 5.8.0.202006091008-r - 7.3.0 + 7.3.1 1.0.8 4.2.0 1.27 From 257f4a40216ad8557cb6244cd79ab873ac84c3db Mon Sep 17 00:00:00 2001 From: Guillaume Le Floch Date: Mon, 7 Dec 2020 14:28:28 +0100 Subject: [PATCH 18/62] Handle null gradle resource directory in dev mode --- .../io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java | 2 +- .../src/main/java/io/quarkus/bootstrap/IDELauncherImpl.java | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java index 5bd60db5765cd..37b665f75867c 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java @@ -445,7 +445,7 @@ Set checkForFileChange() { outputPath = rootPath; doCopy = false; } - if (rootPath == null) { + if (rootPath == null || outputPath == null) { continue; } Path root = Paths.get(rootPath); diff --git a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/IDELauncherImpl.java b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/IDELauncherImpl.java index f479fec1cd2e6..3bbce1bbf38f0 100644 --- a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/IDELauncherImpl.java +++ b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/IDELauncherImpl.java @@ -12,10 +12,9 @@ /** * IDE entry point. - * + *

* This is launched from the core/launcher module. To avoid any shading issues core/launcher unpacks all its dependencies * into the jar file, then uses a custom class loader load them. - * */ public class IDELauncherImpl { From 793cc5fb6b6bcaedd9d51b45b2d8690ebdf66bcb Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Wed, 9 Dec 2020 12:03:23 +0100 Subject: [PATCH 19/62] Simplify the form of CDI doc Per discussion with Martin. --- docs/src/main/asciidoc/cdi.adoc | 52 ++++++++++++++++----------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/docs/src/main/asciidoc/cdi.adoc b/docs/src/main/asciidoc/cdi.adoc index 9d5ce952cf044..b0da08e5aea12 100644 --- a/docs/src/main/asciidoc/cdi.adoc +++ b/docs/src/main/asciidoc/cdi.adoc @@ -13,26 +13,26 @@ include::./attributes.adoc[] In this guide we're going to describe the basic principles of the Quarkus programming model that is based on the http://docs.jboss.org/cdi/spec/2.0/cdi-spec.html[Contexts and Dependency Injection for Java 2.0, window="_blank"] specification. -== _Q: OK. Let's start simple. What is a bean?_ +== OK. Let's start simple. What is a bean? -A: Well, a bean is a _container-managed_ object that supports a set of basic services, such as injection of dependencies, lifecycle callbacks and interceptors. +Well, a bean is a _container-managed_ object that supports a set of basic services, such as injection of dependencies, lifecycle callbacks and interceptors. -== _Q: Wait a minute. What does "container-managed" mean?_ +== Wait a minute. What does "container-managed" mean? -A: Simply put, you don't control the lifecycle of the object instance directly. +Simply put, you don't control the lifecycle of the object instance directly. Instead, you can affect the lifecycle through declarative means, such as annotations, configuration, etc. The container is the _environment_ where your application runs. It creates and destroys the instances of beans, associates the instances with a designated context, and injects them into other beans. -== _Q: What is it good for?_ +== What is it good for? -A: An application developer can focus on the business logic rather than finding out "where and how" to obtain a fully initialized component with all of its dependencies. +An application developer can focus on the business logic rather than finding out "where and how" to obtain a fully initialized component with all of its dependencies. NOTE: You've probably heard of the _inversion of control_ (IoC) programming principle. Dependency injection is one of the implementation techniques of IoC. -== _Q: What does a bean look like?_ +== What does a bean look like? -A: There are several kinds of beans. +There are several kinds of beans. The most common ones are class-based beans: .Simple Bean Example @@ -58,9 +58,9 @@ public class Translator { <2> This is a field injection point. It tells the container that `Translator` depends on the `Dictionary` bean. If there is no matching bean the build fails. <3> This is an interceptor binding annotation. In this case, the annotation comes from the MicroProfile Metrics. The relevant interceptor intercepts the invocation and updates the relevant metrics. We will talk about <> later. -== _Q: Nice. How does the dependency resolution work? I see no names or identifiers._ +== Nice. How does the dependency resolution work? I see no names or identifiers. -A: That's a good question. +That's a good question. In CDI the process of matching a bean to an injection point is *type-safe*. Each bean declares a set of bean types. In our example above, the `Translator` bean has two bean types: `Translator` and `java.lang.Object`. @@ -68,9 +68,9 @@ Subsequently, a bean is assignable to an injection point if the bean has a bean We'll talk about qualifiers later. For now, it's enough to know that the bean above is assignable to an injection point of type `Translator` and `java.lang.Object`. -== _Q: Hm, wait a minute. What happens if multiple beans declare the same type?_ +== Hm, wait a minute. What happens if multiple beans declare the same type? -A: There is a simple rule: *exactly one bean must be assignable to an injection point, otherwise the build fails*. +There is a simple rule: *exactly one bean must be assignable to an injection point, otherwise the build fails*. If none is assignable the build fails with `UnsatisfiedResolutionException`. If multiple are assignable the build fails with `AmbiguousResolutionException`. This is very useful because your application fails fast whenever the container is not able to find an unambiguous dependency for any injection point. @@ -97,9 +97,9 @@ public class Translator { <2> `javax.enterprise.inject.Instance` extends `Iterable`. ==== -== _Q: Can I use setter and constructor injection?_ +== Can I use setter and constructor injection? -A: Yes, you can. +Yes, you can. In fact, in CDI the "setter injection" is superseded by more powerful https://docs.jboss.org/cdi/spec/2.0/cdi-spec.html#initializer_methods[initializer methods, window="_blank"]. Initializers may accept multiple parameters and don't have to follow the JavaBean naming conventions. @@ -128,9 +128,9 @@ It's also not necessary to add `@Inject` if there is only one constructor presen <2> An initializer method must be annotated with `@Inject`. <3> An initializer may accept multiple parameters - each one is an injection point. -== _Q: You talked about some qualifiers?_ +== You talked about some qualifiers? -A: https://docs.jboss.org/cdi/spec/2.0/cdi-spec.html#qualifiers[Qualifiers, window="_blank"] are annotations that help the container to distinguish beans that implement the same type. +https://docs.jboss.org/cdi/spec/2.0/cdi-spec.html#qualifiers[Qualifiers, window="_blank"] are annotations that help the container to distinguish beans that implement the same type. As we already said a bean is assignable to an injection point if it has all the required qualifiers. If you declare no qualifier at an injection point the `@Default` qualifier is assumed. @@ -165,15 +165,15 @@ This bean would be assignable to `@Inject @Superior Translator` and `@Inject @Su The reason is that `@Inject Translator` is automatically transformed to `@Inject @Default Translator` during typesafe resolution. And since our `SuperiorTranslator` does not declare `@Default` only the original `Translator` bean is assignable. -== _Q: Looks good. What is the bean scope?_ +== Looks good. What is the bean scope? The scope of a bean determines the lifecycle of its instances, i.e. when and where an instance should be created and destroyed. NOTE: Every bean has exactly one scope. -== _Q: What scopes can I actually use in my Quarkus application?_ +== What scopes can I actually use in my Quarkus application? -A: You can use all the built-in scopes mentioned by the specification except for `javax.enterprise.context.ConversationScoped`. +You can use all the built-in scopes mentioned by the specification except for `javax.enterprise.context.ConversationScoped`. [options="header",cols="1,1"] |=== @@ -188,9 +188,9 @@ A: You can use all the built-in scopes mentioned by the specification except for NOTE: There can be other custom scopes provided by Quarkus extensions. For example, `quarkus-narayana-jta` provides `javax.transaction.TransactionScoped`. -== _Q: `@ApplicationScoped` and `@Singleton` look very similar. Which one should I choose for my Quarkus application?_ +== `@ApplicationScoped` and `@Singleton` look very similar. Which one should I choose for my Quarkus application? -A: It depends ;-). +It depends ;-). A `@Singleton` bean has no <> and hence an instance is _created eagerly_ when the bean is injected. By contrast, an instance of an `@ApplicationScoped` bean is _created lazily_, i.e. when a method is invoked upon an injected instance for the first time. @@ -208,9 +208,9 @@ Existing injection points just work because the injected proxy delegates to the Therefore, we recommend to stick with `@ApplicationScoped` by default unless there's a good reason to use `@Singleton`. [[client_proxies]] -== _Q: I don't understand the concept of client proxies._ +== I don't understand the concept of client proxies. -A: Indeed, the https://docs.jboss.org/cdi/spec/2.0/cdi-spec.html#client_proxies[client proxies, window="_blank"] could be hard to grasp but they provide some useful functionality. +Indeed, the https://docs.jboss.org/cdi/spec/2.0/cdi-spec.html#client_proxies[client proxies, window="_blank"] could be hard to grasp but they provide some useful functionality. A client proxy is basically an object that delegates all method invocations to a target bean instance. It's a container construct that implements `io.quarkus.arc.ClientProxy` and extends the bean class. @@ -248,9 +248,9 @@ Client proxies allow for: * In rare cases it's practical to destroy the beans manually. A direct injected reference would lead to a stale bean instance. -== _Q: OK. You said that there are several kinds of beans?_ +== OK. You said that there are several kinds of beans? -A: Yes. In general, we distinguish: +Yes. In general, we distinguish: 1. Class beans 2. Producer methods @@ -308,7 +308,7 @@ There's more about producers. You can declare qualifiers, inject dependencies into the producer methods parameters, etc. You can read more about producers for example in the https://docs.jboss.org/weld/reference/latest/en-US/html/producermethods.html[Weld docs, window="_blank"]. -== _Q: OK, injection looks cool. What other services are provided?_ +== OK, injection looks cool. What other services are provided? === Lifecycle Callbacks From e9c51a77ed3348b0ab9ccced91675039b863508d Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Tue, 8 Dec 2020 15:13:50 +0000 Subject: [PATCH 20/62] Update DefaultTokenStateManager to remove all session cookies when tokens are split --- .../runtime/CodeAuthenticationMechanism.java | 7 ++++++- .../runtime/DefaultTokenStateManager.java | 17 +++++++++++++-- .../io/quarkus/it/keycloak/CodeFlowTest.java | 21 +++++++++++++++++++ 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CodeAuthenticationMechanism.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CodeAuthenticationMechanism.java index eab1988037801..5fa03936d0d49 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CodeAuthenticationMechanism.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CodeAuthenticationMechanism.java @@ -484,10 +484,15 @@ private void removeCookie(RoutingContext context, TenantConfigContext configCont if (SESSION_COOKIE_NAME.equals(cookieName)) { resolver.getTokenStateManager().deleteTokens(context, configContext.oidcConfig, cookie.getValue()); } + removeCookie(cookie, configContext.oidcConfig); + } + } + static void removeCookie(ServerCookie cookie, OidcTenantConfig oidcConfig) { + if (cookie != null) { cookie.setValue(""); cookie.setMaxAge(0); - Authentication auth = configContext.oidcConfig.getAuthentication(); + Authentication auth = oidcConfig.getAuthentication(); if (auth.cookiePath.isPresent()) { cookie.setPath(auth.cookiePath.get()); } diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/DefaultTokenStateManager.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/DefaultTokenStateManager.java index 3d8b325dd4884..5828c17e3402b 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/DefaultTokenStateManager.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/DefaultTokenStateManager.java @@ -6,6 +6,7 @@ import io.quarkus.oidc.OidcTenantConfig; import io.quarkus.oidc.TokenStateManager; import io.vertx.core.http.Cookie; +import io.vertx.core.http.impl.ServerCookie; import io.vertx.ext.web.RoutingContext; @ApplicationScoped @@ -55,11 +56,11 @@ public AuthorizationCodeTokens getTokens(RoutingContext routingContext, OidcTena accessToken = tokens[1]; refreshToken = tokens[2]; } else { - Cookie atCookie = routingContext.request().getCookie(getAccessTokenCookieName(oidcConfig.getTenantId().get())); + Cookie atCookie = getAccessTokenCookie(routingContext, oidcConfig); if (atCookie != null) { accessToken = atCookie.getValue(); } - Cookie rtCookie = routingContext.request().getCookie(getRefreshTokenCookieName(oidcConfig.getTenantId().get())); + Cookie rtCookie = getRefreshTokenCookie(routingContext, oidcConfig); if (rtCookie != null) { refreshToken = rtCookie.getValue(); } @@ -71,6 +72,18 @@ public AuthorizationCodeTokens getTokens(RoutingContext routingContext, OidcTena @Override public void deleteTokens(RoutingContext routingContext, OidcTenantConfig oidcConfig, String tokenState) { + if (oidcConfig.tokenStateManager.splitTokens) { + CodeAuthenticationMechanism.removeCookie(getAccessTokenCookie(routingContext, oidcConfig), oidcConfig); + CodeAuthenticationMechanism.removeCookie(getRefreshTokenCookie(routingContext, oidcConfig), oidcConfig); + } + } + + private static ServerCookie getAccessTokenCookie(RoutingContext routingContext, OidcTenantConfig oidcConfig) { + return (ServerCookie) routingContext.request().getCookie(getAccessTokenCookieName(oidcConfig.getTenantId().get())); + } + + private static ServerCookie getRefreshTokenCookie(RoutingContext routingContext, OidcTenantConfig oidcConfig) { + return (ServerCookie) routingContext.request().getCookie(getRefreshTokenCookieName(oidcConfig.getTenantId().get())); } private static String getAccessTokenCookieName(String tenantId) { diff --git a/integration-tests/oidc-code-flow/src/test/java/io/quarkus/it/keycloak/CodeFlowTest.java b/integration-tests/oidc-code-flow/src/test/java/io/quarkus/it/keycloak/CodeFlowTest.java index d78977d7c61d9..350f27a389c43 100644 --- a/integration-tests/oidc-code-flow/src/test/java/io/quarkus/it/keycloak/CodeFlowTest.java +++ b/integration-tests/oidc-code-flow/src/test/java/io/quarkus/it/keycloak/CodeFlowTest.java @@ -619,6 +619,27 @@ public void testDefaultSessionManagerSplitTokens() throws IOException, Interrupt Cookie rtTokenCookie = getSessionRtCookie(page.getWebClient(), "tenant-split-tokens"); checkSingleTokenCookie(rtTokenCookie, "Refresh"); + // verify all the cookies are cleared after the session timeout + webClient.getOptions().setRedirectEnabled(false); + webClient.getCache().clear(); + + await().atLeast(6, TimeUnit.SECONDS) + .pollDelay(Duration.ofSeconds(6)) + .until(new Callable() { + @Override + public Boolean call() throws Exception { + WebResponse webResponse = webClient + .loadWebResponse(new WebRequest(URI.create("http://localhost:8081/index.html").toURL())); + assertEquals(302, webResponse.getStatusCode()); + assertNull(getSessionCookie(webClient, null)); + return true; + } + }); + + assertNull(getSessionCookie(page.getWebClient(), "tenant-split-tokens")); + assertNull(getSessionAtCookie(page.getWebClient(), "tenant-split-tokens")); + assertNull(getSessionRtCookie(page.getWebClient(), "tenant-split-tokens")); + webClient.getCookieManager().clearCookies(); } } From 2fc6833b20a7efe6bdd80c448ac565480d3ef499 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Wed, 9 Dec 2020 13:06:45 +0100 Subject: [PATCH 21/62] Remove a few things handled by the bot from COMMITTERS.adoc --- COMMITTERS.adoc | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/COMMITTERS.adoc b/COMMITTERS.adoc index 2b29a5c0112be..18ad66ea8af44 100644 --- a/COMMITTERS.adoc +++ b/COMMITTERS.adoc @@ -54,10 +54,7 @@ While not absolute, here is some advice: you should affect this milestone to it so that we can be sure it stays on the radar. * If you think a pull request is worth mentioning in the release notes and/or the announcement blog post, add the `release/noteworthy-feature` label. -* When merging a pull request, please affect the corresponding milestone to the pull request - and the resolved issues. - The current master branch is marked with ` - master` on GitHub. - Do NOT affect a PR to an old branch, see the <> for more details. +* Do NOT affect a PR to an old branch, see the <> for more details. Obviously, each situation is different so use your own judgement, and if in doubt, just ask for advice, the other committers are here to help. @@ -182,9 +179,6 @@ some of the "excluding" labels. Thus: -* When you merge a pull request, please affect it with the current milestone marked with "master". -* If the pull request has issues associated (i.e. if the pull request fixes some issues), - you should also affect the milestone assigned to the issues. * If you close a pull request because the committers have decided to not merge it, please add the appropriate `triage/` label: `triage/invalid`, `triage/out-of-date`, `triage/wontfix` are usually in order. From 1b9c68694693d2e20d5ecedbea57554aed477768 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Mon, 7 Dec 2020 15:56:30 +0200 Subject: [PATCH 22/62] Create basic structure for k8s service binding handling --- bom/application/pom.xml | 10 ++ .../io/quarkus/deployment/Capability.java | 1 + .../deployment/pom.xml | 43 ++++++ .../KubernetesServiceBindingProcessor.java | 24 +++ extensions/kubernetes-service-binding/pom.xml | 20 +++ .../runtime/pom.xml | 61 ++++++++ .../KubernetesServiceBindingConfig.java | 22 +++ ...tesServiceBindingConfigSourceProvider.java | 94 ++++++++++++ .../KubernetesServiceBindingRecorder.java | 43 ++++++ .../binding/runtime/ServiceBinding.java | 141 ++++++++++++++++++ .../runtime/ServiceBindingConfigSource.java | 12 ++ .../runtime/ServiceBindingConverter.java | 9 ++ .../resources/META-INF/quarkus-extension.yaml | 8 + ...erviceBindingConfigSourceProviderTest.java | 71 +++++++++ .../binding/runtime/ServiceBindingTest.java | 43 ++++++ .../k8s/test-k8s/.hidden-dir/provider | 1 + .../k8s/test-k8s/.hidden-dir/test-secret-key | 1 + .../resources/k8s/test-k8s/.hidden-dir/type | 1 + .../k8s/test-k8s/.hidden-symlink-dir | 1 + .../src/test/resources/k8s/test-k8s/provider | 1 + .../resources/k8s/test-k8s/test-secret-key | 1 + .../src/test/resources/k8s/test-k8s/type | 1 + .../src/test/resources/k8s/test-name/provider | 1 + .../k8s/test-name/test-other-secret-key | 1 + .../resources/k8s/test-name/test-secret-key | 1 + .../src/test/resources/k8s/test-name/type | 1 + extensions/pom.xml | 1 + 27 files changed, 614 insertions(+) create mode 100644 extensions/kubernetes-service-binding/deployment/pom.xml create mode 100644 extensions/kubernetes-service-binding/deployment/src/main/java/io/quarkus/kubernetes/service/binding/runtime/KubernetesServiceBindingProcessor.java create mode 100644 extensions/kubernetes-service-binding/pom.xml create mode 100644 extensions/kubernetes-service-binding/runtime/pom.xml create mode 100644 extensions/kubernetes-service-binding/runtime/src/main/java/io/quarkus/kubernetes/service/binding/runtime/KubernetesServiceBindingConfig.java create mode 100644 extensions/kubernetes-service-binding/runtime/src/main/java/io/quarkus/kubernetes/service/binding/runtime/KubernetesServiceBindingConfigSourceProvider.java create mode 100644 extensions/kubernetes-service-binding/runtime/src/main/java/io/quarkus/kubernetes/service/binding/runtime/KubernetesServiceBindingRecorder.java create mode 100644 extensions/kubernetes-service-binding/runtime/src/main/java/io/quarkus/kubernetes/service/binding/runtime/ServiceBinding.java create mode 100644 extensions/kubernetes-service-binding/runtime/src/main/java/io/quarkus/kubernetes/service/binding/runtime/ServiceBindingConfigSource.java create mode 100644 extensions/kubernetes-service-binding/runtime/src/main/java/io/quarkus/kubernetes/service/binding/runtime/ServiceBindingConverter.java create mode 100644 extensions/kubernetes-service-binding/runtime/src/main/resources/META-INF/quarkus-extension.yaml create mode 100644 extensions/kubernetes-service-binding/runtime/src/test/java/io/quarkus/kubernetes/service/binding/runtime/KubernetesServiceBindingConfigSourceProviderTest.java create mode 100644 extensions/kubernetes-service-binding/runtime/src/test/java/io/quarkus/kubernetes/service/binding/runtime/ServiceBindingTest.java create mode 100644 extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-k8s/.hidden-dir/provider create mode 100644 extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-k8s/.hidden-dir/test-secret-key create mode 100644 extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-k8s/.hidden-dir/type create mode 120000 extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-k8s/.hidden-symlink-dir create mode 120000 extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-k8s/provider create mode 120000 extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-k8s/test-secret-key create mode 120000 extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-k8s/type create mode 100644 extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-name/provider create mode 100644 extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-name/test-other-secret-key create mode 100644 extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-name/test-secret-key create mode 100644 extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-name/type diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 5325ec71928ef..e942430fdd71e 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -1943,6 +1943,16 @@ quarkus-kubernetes-client-deployment-internal ${project.version} + + io.quarkus + quarkus-kubernetes-service-binding + ${project.version} + + + io.quarkus + quarkus-kubernetes-service-binding-deployment + ${project.version} + io.quarkus quarkus-openshift-client 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 d190ad8c571e3..96fcff3cc51c8 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/Capability.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/Capability.java @@ -41,6 +41,7 @@ public enum Capability { SECURITY_ELYTRON_LDAP, SECURITY_JPA, QUARTZ, + KUBERNETES_SERVICE_BINDING, /** * @deprecated * @see io.quarkus.deployment.metrics.MetricsCapabilityBuildItem diff --git a/extensions/kubernetes-service-binding/deployment/pom.xml b/extensions/kubernetes-service-binding/deployment/pom.xml new file mode 100644 index 0000000000000..4a9c91b41925f --- /dev/null +++ b/extensions/kubernetes-service-binding/deployment/pom.xml @@ -0,0 +1,43 @@ + + + + quarkus-kubernetes-service-binding-parent + io.quarkus + 999-SNAPSHOT + + 4.0.0 + + quarkus-kubernetes-service-binding-deployment + Quarkus - Kubernetes Service Binding - Deployment + + + + io.quarkus + quarkus-core-deployment + + + + io.quarkus + quarkus-kubernetes-service-binding + + + + + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${project.version} + + + + + + + diff --git a/extensions/kubernetes-service-binding/deployment/src/main/java/io/quarkus/kubernetes/service/binding/runtime/KubernetesServiceBindingProcessor.java b/extensions/kubernetes-service-binding/deployment/src/main/java/io/quarkus/kubernetes/service/binding/runtime/KubernetesServiceBindingProcessor.java new file mode 100644 index 0000000000000..43d3cdcecfa63 --- /dev/null +++ b/extensions/kubernetes-service-binding/deployment/src/main/java/io/quarkus/kubernetes/service/binding/runtime/KubernetesServiceBindingProcessor.java @@ -0,0 +1,24 @@ +package io.quarkus.kubernetes.service.binding.runtime; + +import io.quarkus.deployment.Capability; +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.ExecutionTime; +import io.quarkus.deployment.annotations.Record; +import io.quarkus.deployment.builditem.CapabilityBuildItem; +import io.quarkus.deployment.builditem.RunTimeConfigurationSourceValueBuildItem; + +public class KubernetesServiceBindingProcessor { + + @BuildStep + void capabilities(BuildProducer capability) { + capability.produce(new CapabilityBuildItem(Capability.KUBERNETES_SERVICE_BINDING)); + } + + @BuildStep + @Record(ExecutionTime.RUNTIME_INIT) + public RunTimeConfigurationSourceValueBuildItem configure(KubernetesServiceBindingRecorder recorder, + KubernetesServiceBindingConfig config) { + return new RunTimeConfigurationSourceValueBuildItem(recorder.configSources(config)); + } +} diff --git a/extensions/kubernetes-service-binding/pom.xml b/extensions/kubernetes-service-binding/pom.xml new file mode 100644 index 0000000000000..853422d7adf9a --- /dev/null +++ b/extensions/kubernetes-service-binding/pom.xml @@ -0,0 +1,20 @@ + + + + quarkus-extensions-parent + io.quarkus + 999-SNAPSHOT + ../pom.xml + + 4.0.0 + + quarkus-kubernetes-service-binding-parent + Quarkus - Kubernetes Service Binding + pom + + deployment + runtime + + diff --git a/extensions/kubernetes-service-binding/runtime/pom.xml b/extensions/kubernetes-service-binding/runtime/pom.xml new file mode 100644 index 0000000000000..1ac221790927b --- /dev/null +++ b/extensions/kubernetes-service-binding/runtime/pom.xml @@ -0,0 +1,61 @@ + + + 4.0.0 + + + io.quarkus + quarkus-kubernetes-service-binding-parent + 999-SNAPSHOT + + + quarkus-kubernetes-service-binding + Quarkus - Kubernetes Service Binding - Runtime + Read runtime configuration based on the Kubernetes Service Binding Specification + + + io.quarkus + quarkus-core + + + + org.junit.jupiter + junit-jupiter + test + + + org.assertj + assertj-core + test + + + org.mockito + mockito-core + test + + + + + + + io.quarkus + quarkus-bootstrap-maven-plugin + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${project.version} + + + + + + + + + diff --git a/extensions/kubernetes-service-binding/runtime/src/main/java/io/quarkus/kubernetes/service/binding/runtime/KubernetesServiceBindingConfig.java b/extensions/kubernetes-service-binding/runtime/src/main/java/io/quarkus/kubernetes/service/binding/runtime/KubernetesServiceBindingConfig.java new file mode 100644 index 0000000000000..37866722c7870 --- /dev/null +++ b/extensions/kubernetes-service-binding/runtime/src/main/java/io/quarkus/kubernetes/service/binding/runtime/KubernetesServiceBindingConfig.java @@ -0,0 +1,22 @@ +package io.quarkus.kubernetes.service.binding.runtime; + +import java.util.Optional; + +import io.quarkus.runtime.annotations.ConfigItem; +import io.quarkus.runtime.annotations.ConfigPhase; +import io.quarkus.runtime.annotations.ConfigRoot; + +@ConfigRoot(name = "kubernetes-service-binding", phase = ConfigPhase.BOOTSTRAP) +public class KubernetesServiceBindingConfig { + + /** + * If enabled, Service Bindings will be looked in the file system + */ + public boolean enabled; + + /** + * The bindings file system root. Specified by the Kubernetes Service ServiceBinding Specification. + */ + @ConfigItem(defaultValue = "${SERVICE_BINDING_ROOT:}") + public Optional root; +} diff --git a/extensions/kubernetes-service-binding/runtime/src/main/java/io/quarkus/kubernetes/service/binding/runtime/KubernetesServiceBindingConfigSourceProvider.java b/extensions/kubernetes-service-binding/runtime/src/main/java/io/quarkus/kubernetes/service/binding/runtime/KubernetesServiceBindingConfigSourceProvider.java new file mode 100644 index 0000000000000..3bd5a62909dd5 --- /dev/null +++ b/extensions/kubernetes-service-binding/runtime/src/main/java/io/quarkus/kubernetes/service/binding/runtime/KubernetesServiceBindingConfigSourceProvider.java @@ -0,0 +1,94 @@ +package io.quarkus.kubernetes.service.binding.runtime; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.ServiceLoader; + +import org.eclipse.microprofile.config.spi.ConfigSource; +import org.eclipse.microprofile.config.spi.ConfigSourceProvider; + +public class KubernetesServiceBindingConfigSourceProvider implements ConfigSourceProvider { + + private final List serviceBindings; + private final List serviceBindingConverters; + + public KubernetesServiceBindingConfigSourceProvider(String bindingRoot) { + this(bindingRoot, determineConverters()); + } + + //visible for testing + KubernetesServiceBindingConfigSourceProvider(String bindingRoot, List serviceBindingConverters) { + this.serviceBindingConverters = serviceBindingConverters; + Path p = Paths.get(bindingRoot); + if (!Files.exists(p)) { + serviceBindings = Collections.emptyList(); + return; + } + if (!Files.isDirectory(p)) { + throw new IllegalArgumentException("Service Binding root '" + p + "' is not a directory"); + } + + File[] files = p.toFile().listFiles(); + if (files == null) { + serviceBindings = Collections.emptyList(); + } else { + serviceBindings = new ArrayList<>(files.length); + for (File f : files) { + serviceBindings.add(new ServiceBinding(f.toPath())); + } + serviceBindings.sort(new Comparator() { + @Override + public int compare(ServiceBinding o1, ServiceBinding o2) { + if (!o1.getName().equals(o2.getName())) { + return o1.getName().compareTo(o2.getName()); + } + return o1.getProvider().compareTo(o2.getProvider()); + } + }); + } + } + + private static List determineConverters() { + List result = new ArrayList<>(); + ServiceLoader loader = ServiceLoader.load(ServiceBindingConverter.class, + Thread.currentThread().getContextClassLoader()); + for (ServiceBindingConverter c : loader) { + result.add(c); + } + return result; + } + + @Override + public Iterable getConfigSources(ClassLoader forClassLoader) { + if (serviceBindings.isEmpty()) { + return Collections.emptyList(); + } + + List result = new ArrayList<>(); + for (ServiceBindingConverter converter : serviceBindingConverters) { + Optional optional = converter.convert(serviceBindings); + if (optional.isPresent()) { + result.add(optional.get()); + } + } + for (ServiceBinding serviceBinding : serviceBindings) { + Map serviceBindingProperties = serviceBinding.getProperties(); + Map rawConfigSourceProperties = new HashMap<>(); + for (Map.Entry entry : serviceBindingProperties.entrySet()) { + rawConfigSourceProperties.put("quarkus." + serviceBinding.getName() + "." + entry.getKey(), entry.getValue()); + } + result.add(new ServiceBindingConfigSource("service-binding-" + serviceBinding.getName() + "-raw", + rawConfigSourceProperties)); + } + return result; + } +} diff --git a/extensions/kubernetes-service-binding/runtime/src/main/java/io/quarkus/kubernetes/service/binding/runtime/KubernetesServiceBindingRecorder.java b/extensions/kubernetes-service-binding/runtime/src/main/java/io/quarkus/kubernetes/service/binding/runtime/KubernetesServiceBindingRecorder.java new file mode 100644 index 0000000000000..2bf23b933f3d7 --- /dev/null +++ b/extensions/kubernetes-service-binding/runtime/src/main/java/io/quarkus/kubernetes/service/binding/runtime/KubernetesServiceBindingRecorder.java @@ -0,0 +1,43 @@ +package io.quarkus.kubernetes.service.binding.runtime; + +import java.util.Collections; + +import org.eclipse.microprofile.config.spi.ConfigSource; +import org.eclipse.microprofile.config.spi.ConfigSourceProvider; +import org.jboss.logging.Logger; + +import io.quarkus.runtime.RuntimeValue; +import io.quarkus.runtime.annotations.Recorder; + +@Recorder +public class KubernetesServiceBindingRecorder { + + private static final Logger log = Logger.getLogger(KubernetesServiceBindingRecorder.class); + + public RuntimeValue configSources(KubernetesServiceBindingConfig kubernetesServiceBindingConfig) { + if (!kubernetesServiceBindingConfig.enabled) { + log.debug( + "No attempt will be made to bind configuration based on Kubernetes ServiceBinding because the feature was not enabled."); + return emptyRuntimeValue(); + } + if (!kubernetesServiceBindingConfig.root.isPresent()) { + log.debug( + "No attempt will be made to bind configuration based on Kubernetes Service Binding because the binding root was not specified."); + return emptyRuntimeValue(); + } + + return new RuntimeValue<>(new KubernetesServiceBindingConfigSourceProvider(kubernetesServiceBindingConfig.root.get())); + } + + private RuntimeValue emptyRuntimeValue() { + return new RuntimeValue<>(new EmptyConfigSourceProvider()); + } + + private static class EmptyConfigSourceProvider implements ConfigSourceProvider { + + @Override + public Iterable getConfigSources(ClassLoader forClassLoader) { + return Collections.emptyList(); + } + } +} diff --git a/extensions/kubernetes-service-binding/runtime/src/main/java/io/quarkus/kubernetes/service/binding/runtime/ServiceBinding.java b/extensions/kubernetes-service-binding/runtime/src/main/java/io/quarkus/kubernetes/service/binding/runtime/ServiceBinding.java new file mode 100644 index 0000000000000..7e0c7cb6b2cbf --- /dev/null +++ b/extensions/kubernetes-service-binding/runtime/src/main/java/io/quarkus/kubernetes/service/binding/runtime/ServiceBinding.java @@ -0,0 +1,141 @@ +package io.quarkus.kubernetes.service.binding.runtime; + +import java.io.File; +import java.io.FileFilter; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +import org.jboss.logging.Logger; + +/** + * Contains all the necessary information about a service binding + */ +public final class ServiceBinding { + + private static final Logger log = Logger.getLogger(ServiceBinding.class); + + private static final String PROVIDER = "provider"; + private static final String TYPE = "type"; + + private final String name; + private final String provider; + private final Map properties; + private final String type; + + public ServiceBinding(Path bindingDirectory) { + this(bindingDirectory.getFileName().toString(), getFilenameToContentMap(bindingDirectory), bindingDirectory); + } + + // visible for testing + ServiceBinding(String name, Map filenameToContentMap, Path bindingDirectory) { + Map properties = new HashMap<>(); + String type = null; + String provider = null; + for (Map.Entry entry : filenameToContentMap.entrySet()) { + if (TYPE.equals(entry.getKey())) { + type = entry.getValue(); + } else if (PROVIDER.equals(entry.getKey())) { + provider = entry.getValue(); + } else { + properties.put(entry.getKey(), entry.getValue()); + } + } + + if (type == null) { + throw new IllegalArgumentException("Directory '" + bindingDirectory + + "' does not represent a valid Service ServiceBinding directory as it does specify a type"); + } + + this.name = name; + this.type = type; + this.provider = provider; + this.properties = Collections.unmodifiableMap(properties); + } + + private static Map getFilenameToContentMap(Path directory) { + if (!Files.exists(directory) || !Files.isDirectory(directory)) { + return Collections.emptyMap(); + } + + File[] files = directory.toFile().listFiles(new FileFilter() { + @Override + public boolean accept(File f) { + try { + return !Files.isHidden(f.toPath()) && !Files.isDirectory(f.toPath()); + } catch (IOException e) { + throw new IllegalStateException("Unable to determine if file '" + f + "' is a regular file", e); + } + } + }); + + Map result = new HashMap<>(); + if (files != null) { + for (File f : files) { + try { + result.put(f.toPath().getFileName().toString(), + new String(Files.readAllBytes(f.toPath()), StandardCharsets.UTF_8).trim()); + } catch (IOException e) { + throw new IllegalStateException("Unable to read file '" + f + "'", e); + } + } + } + return result; + } + + public String getName() { + return name; + } + + public Map getProperties() { + return properties; + } + + public String getType() { + return type; + } + + public String getProvider() { + return provider; + } + + @Override + public String toString() { + return "ServiceBinding{" + + "name='" + name + '\'' + + ", provider='" + provider + '\'' + + ", type='" + type + '\'' + + '}'; + } + + public static List matchingByType(String type, List all) { + Objects.requireNonNull(type, "Type must not be null"); + List result = new ArrayList<>(); + for (ServiceBinding binding : all) { + if (type.equals(binding.getType())) { + result.add(binding); + } + } + return result; + } + + public static Optional singleMatchingByType(String type, List all) { + List allMatching = matchingByType(type, all); + if (allMatching.isEmpty()) { + return Optional.empty(); + } + ServiceBinding first = allMatching.get(0); + if (allMatching.size() > 1) { + log.warn("More than one ServiceBinding matches type '" + type + "', but only " + first + " will be used"); + } + return Optional.of(first); + } +} diff --git a/extensions/kubernetes-service-binding/runtime/src/main/java/io/quarkus/kubernetes/service/binding/runtime/ServiceBindingConfigSource.java b/extensions/kubernetes-service-binding/runtime/src/main/java/io/quarkus/kubernetes/service/binding/runtime/ServiceBindingConfigSource.java new file mode 100644 index 0000000000000..de0bd14dabeaf --- /dev/null +++ b/extensions/kubernetes-service-binding/runtime/src/main/java/io/quarkus/kubernetes/service/binding/runtime/ServiceBindingConfigSource.java @@ -0,0 +1,12 @@ +package io.quarkus.kubernetes.service.binding.runtime; + +import java.util.Map; + +import io.smallrye.config.common.MapBackedConfigSource; + +public class ServiceBindingConfigSource extends MapBackedConfigSource { + + public ServiceBindingConfigSource(String name, Map propertyMap) { + super(name, propertyMap, 270, false); + } +} diff --git a/extensions/kubernetes-service-binding/runtime/src/main/java/io/quarkus/kubernetes/service/binding/runtime/ServiceBindingConverter.java b/extensions/kubernetes-service-binding/runtime/src/main/java/io/quarkus/kubernetes/service/binding/runtime/ServiceBindingConverter.java new file mode 100644 index 0000000000000..861c3bcbd76cf --- /dev/null +++ b/extensions/kubernetes-service-binding/runtime/src/main/java/io/quarkus/kubernetes/service/binding/runtime/ServiceBindingConverter.java @@ -0,0 +1,9 @@ +package io.quarkus.kubernetes.service.binding.runtime; + +import java.util.List; +import java.util.Optional; + +public interface ServiceBindingConverter { + + Optional convert(List serviceBindings); +} diff --git a/extensions/kubernetes-service-binding/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/kubernetes-service-binding/runtime/src/main/resources/META-INF/quarkus-extension.yaml new file mode 100644 index 0000000000000..ec28e4874c408 --- /dev/null +++ b/extensions/kubernetes-service-binding/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -0,0 +1,8 @@ +--- +name: "Kubernetes Service Binding" +metadata: + keywords: + - "kubernetes-service-binding" + categories: + - "cloud" + status: "preview" diff --git a/extensions/kubernetes-service-binding/runtime/src/test/java/io/quarkus/kubernetes/service/binding/runtime/KubernetesServiceBindingConfigSourceProviderTest.java b/extensions/kubernetes-service-binding/runtime/src/test/java/io/quarkus/kubernetes/service/binding/runtime/KubernetesServiceBindingConfigSourceProviderTest.java new file mode 100644 index 0000000000000..b2980516d4129 --- /dev/null +++ b/extensions/kubernetes-service-binding/runtime/src/test/java/io/quarkus/kubernetes/service/binding/runtime/KubernetesServiceBindingConfigSourceProviderTest.java @@ -0,0 +1,71 @@ +package io.quarkus.kubernetes.service.binding.runtime; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.entry; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import org.eclipse.microprofile.config.spi.ConfigSource; +import org.junit.jupiter.api.Test; + +class KubernetesServiceBindingConfigSourceProviderTest { + + @Test + void testNonExistentDir() throws IOException { + Path path = Files.createTempDirectory("tmp"); + KubernetesServiceBindingConfigSourceProvider configSourceProvider = new KubernetesServiceBindingConfigSourceProvider( + path.resolve("non-existent").toString(), Collections.emptyList()); + Iterable configSources = configSourceProvider + .getConfigSources(Thread.currentThread().getContextClassLoader()); + assertThat(configSources).isEmpty(); + } + + @Test + void testNotDir() throws IOException { + Path path = Files.createTempDirectory("tmp"); + assertThatIllegalArgumentException().isThrownBy(() -> { + new KubernetesServiceBindingConfigSourceProvider( + Files.createFile(path.resolve("file")).toString(), Collections.emptyList()); + }); + } + + @Test + void test() { + Path path = Paths.get("src/test/resources/k8s"); + KubernetesServiceBindingConfigSourceProvider configSourceProvider = new KubernetesServiceBindingConfigSourceProvider( + path.toString(), Collections.singletonList(new ServiceBindingConverter() { + @Override + public Optional convert(List serviceBindings) { + for (ServiceBinding serviceBinding : serviceBindings) { + if ("test-type-1".equals(serviceBinding.getType())) { + return Optional.of(new ServiceBindingConfigSource("test", Collections.singletonMap("key", + serviceBinding.getProperties().get("test-secret-key")))); + } + } + return Optional.of(new ServiceBindingConfigSource("test", Collections.singletonMap("key", + ServiceBinding.matchingByType("test-type-1", serviceBindings).get(0).getProperties() + .get("test-secret-key")))); + } + })); + Iterable configSources = configSourceProvider + .getConfigSources(Thread.currentThread().getContextClassLoader()); + assertThat(configSources).hasSize(3).extracting("name").containsOnly("test", "service-binding-test-name-raw", + "service-binding-test-k8s-raw"); + assertThat(configSources).filteredOn(c -> c.getName().equals("test")).singleElement().satisfies(c -> { + assertThat(c.getProperties()).containsExactly(entry("key", "test-secret-value")); + }); + assertThat(configSources).filteredOn(c -> c.getName().equals("service-binding-test-name-raw")).singleElement() + .satisfies(c -> { + assertThat(c.getProperties()).containsOnly( + entry("quarkus.test-name.test-secret-key", "test-secret-value-2"), + entry("quarkus.test-name.test-other-secret-key", "test-other-secret-value-2")); + }); + } +} diff --git a/extensions/kubernetes-service-binding/runtime/src/test/java/io/quarkus/kubernetes/service/binding/runtime/ServiceBindingTest.java b/extensions/kubernetes-service-binding/runtime/src/test/java/io/quarkus/kubernetes/service/binding/runtime/ServiceBindingTest.java new file mode 100644 index 0000000000000..32b2576979dd5 --- /dev/null +++ b/extensions/kubernetes-service-binding/runtime/src/test/java/io/quarkus/kubernetes/service/binding/runtime/ServiceBindingTest.java @@ -0,0 +1,43 @@ +package io.quarkus.kubernetes.service.binding.runtime; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.entry; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.junit.jupiter.api.Test; + +class ServiceBindingTest { + + private final Path root = Paths.get("src/test/resources/k8s"); + + @Test + void testInvalid() throws IOException { + Path path = Files.createTempDirectory("invalid-binding"); + assertThatIllegalArgumentException().isThrownBy(() -> new ServiceBinding(path)); + } + + @Test + void test() { + ServiceBinding binding = new ServiceBinding(root.resolve("test-name")); + assertThat(binding.getType()).isEqualTo("test-type-2"); + assertThat(binding.getProvider()).isEqualTo("test-provider-2"); + assertThat(binding.getProperties()).containsOnly( + entry("test-secret-key", "test-secret-value-2"), + entry("test-other-secret-key", "test-other-secret-value-2")); + } + + @Test + void testK8s() { + //When bindings are provided as a k8s configmap secret pairs data files will be symlinks to hidden directories + ServiceBinding binding = new ServiceBinding(root.resolve("test-k8s")); + assertThat(binding.getType()).isEqualTo("test-type-1"); + assertThat(binding.getProvider()).isEqualTo("test-provider-1"); + assertThat(binding.getProperties()).containsExactly(entry("test-secret-key", "test-secret-value")); + } + +} diff --git a/extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-k8s/.hidden-dir/provider b/extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-k8s/.hidden-dir/provider new file mode 100644 index 0000000000000..0036389c3b873 --- /dev/null +++ b/extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-k8s/.hidden-dir/provider @@ -0,0 +1 @@ +test-provider-1 diff --git a/extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-k8s/.hidden-dir/test-secret-key b/extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-k8s/.hidden-dir/test-secret-key new file mode 100644 index 0000000000000..fffe74ef7102e --- /dev/null +++ b/extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-k8s/.hidden-dir/test-secret-key @@ -0,0 +1 @@ +test-secret-value diff --git a/extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-k8s/.hidden-dir/type b/extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-k8s/.hidden-dir/type new file mode 100644 index 0000000000000..b3f16a9424d28 --- /dev/null +++ b/extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-k8s/.hidden-dir/type @@ -0,0 +1 @@ +test-type-1 diff --git a/extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-k8s/.hidden-symlink-dir b/extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-k8s/.hidden-symlink-dir new file mode 120000 index 0000000000000..bc3e42406cdad --- /dev/null +++ b/extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-k8s/.hidden-symlink-dir @@ -0,0 +1 @@ +.hidden-dir/ \ No newline at end of file diff --git a/extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-k8s/provider b/extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-k8s/provider new file mode 120000 index 0000000000000..d093f1987eb5c --- /dev/null +++ b/extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-k8s/provider @@ -0,0 +1 @@ +.hidden-symlink-dir/provider \ No newline at end of file diff --git a/extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-k8s/test-secret-key b/extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-k8s/test-secret-key new file mode 120000 index 0000000000000..49eda851c572d --- /dev/null +++ b/extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-k8s/test-secret-key @@ -0,0 +1 @@ +.hidden-symlink-dir/test-secret-key \ No newline at end of file diff --git a/extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-k8s/type b/extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-k8s/type new file mode 120000 index 0000000000000..2df1652a93900 --- /dev/null +++ b/extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-k8s/type @@ -0,0 +1 @@ +.hidden-symlink-dir/type \ No newline at end of file diff --git a/extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-name/provider b/extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-name/provider new file mode 100644 index 0000000000000..a4f50455cb4c3 --- /dev/null +++ b/extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-name/provider @@ -0,0 +1 @@ +test-provider-2 diff --git a/extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-name/test-other-secret-key b/extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-name/test-other-secret-key new file mode 100644 index 0000000000000..501d67d9c1b15 --- /dev/null +++ b/extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-name/test-other-secret-key @@ -0,0 +1 @@ +test-other-secret-value-2 diff --git a/extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-name/test-secret-key b/extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-name/test-secret-key new file mode 100644 index 0000000000000..97dfe840baa14 --- /dev/null +++ b/extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-name/test-secret-key @@ -0,0 +1 @@ +test-secret-value-2 diff --git a/extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-name/type b/extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-name/type new file mode 100644 index 0000000000000..b0c27fbe342da --- /dev/null +++ b/extensions/kubernetes-service-binding/runtime/src/test/resources/k8s/test-name/type @@ -0,0 +1 @@ +test-type-2 diff --git a/extensions/pom.xml b/extensions/pom.xml index 9d50c2000a1c3..a21dc1a5af51f 100644 --- a/extensions/pom.xml +++ b/extensions/pom.xml @@ -164,6 +164,7 @@ kubernetes-client kubernetes-config openshift-client + kubernetes-service-binding consul-config From 84008d2aaefcaaec12ff6133583c0a19a634c3d8 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Mon, 7 Dec 2020 17:39:46 +0200 Subject: [PATCH 23/62] Add bare-bones example of PostgreSQL k8s service binding handling --- .../deployment/JDBCPostgreSQLProcessor.java | 11 ++++ .../jdbc/jdbc-postgresql/runtime/pom.xml | 5 ++ .../PostgreSqlServiceBindingConverter.java | 54 +++++++++++++++++++ ...ce.binding.runtime.ServiceBindingConverter | 1 + 4 files changed, 71 insertions(+) create mode 100644 extensions/jdbc/jdbc-postgresql/runtime/src/main/java/io/quarkus/jdbc/postgresql/runtime/PostgreSqlServiceBindingConverter.java create mode 100644 extensions/jdbc/jdbc-postgresql/runtime/src/main/resources/META-INF/services/io.quarkus.kubernetes.service.binding.runtime.ServiceBindingConverter diff --git a/extensions/jdbc/jdbc-postgresql/deployment/src/main/java/io/quarkus/jdbc/postgresql/deployment/JDBCPostgreSQLProcessor.java b/extensions/jdbc/jdbc-postgresql/deployment/src/main/java/io/quarkus/jdbc/postgresql/deployment/JDBCPostgreSQLProcessor.java index 161354341531b..1680495160c9a 100644 --- a/extensions/jdbc/jdbc-postgresql/deployment/src/main/java/io/quarkus/jdbc/postgresql/deployment/JDBCPostgreSQLProcessor.java +++ b/extensions/jdbc/jdbc-postgresql/deployment/src/main/java/io/quarkus/jdbc/postgresql/deployment/JDBCPostgreSQLProcessor.java @@ -11,7 +11,9 @@ import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.builditem.FeatureBuildItem; import io.quarkus.deployment.builditem.SslNativeConfigBuildItem; +import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem; import io.quarkus.jdbc.postgresql.runtime.PostgreSQLAgroalConnectionConfigurer; +import io.quarkus.jdbc.postgresql.runtime.PostgreSqlServiceBindingConverter; public class JDBCPostgreSQLProcessor { @@ -38,4 +40,13 @@ void configureAgroalConnection(BuildProducer additional .build()); } } + + @BuildStep + void registerServiceBinding(Capabilities capabilities, BuildProducer serviceProvider) { + if (capabilities.isPresent(Capability.KUBERNETES_SERVICE_BINDING)) { + serviceProvider.produce( + new ServiceProviderBuildItem("io.quarkus.kubernetes.service.binding.runtime.ServiceBindingConverter", + PostgreSqlServiceBindingConverter.class.getName())); + } + } } diff --git a/extensions/jdbc/jdbc-postgresql/runtime/pom.xml b/extensions/jdbc/jdbc-postgresql/runtime/pom.xml index bc2703757a609..0d6ae6b92f953 100644 --- a/extensions/jdbc/jdbc-postgresql/runtime/pom.xml +++ b/extensions/jdbc/jdbc-postgresql/runtime/pom.xml @@ -27,6 +27,11 @@ svm provided + + io.quarkus + quarkus-kubernetes-service-binding + true + diff --git a/extensions/jdbc/jdbc-postgresql/runtime/src/main/java/io/quarkus/jdbc/postgresql/runtime/PostgreSqlServiceBindingConverter.java b/extensions/jdbc/jdbc-postgresql/runtime/src/main/java/io/quarkus/jdbc/postgresql/runtime/PostgreSqlServiceBindingConverter.java new file mode 100644 index 0000000000000..2dfb46734c2e6 --- /dev/null +++ b/extensions/jdbc/jdbc-postgresql/runtime/src/main/java/io/quarkus/jdbc/postgresql/runtime/PostgreSqlServiceBindingConverter.java @@ -0,0 +1,54 @@ +package io.quarkus.jdbc.postgresql.runtime; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.jboss.logging.Logger; + +import io.quarkus.kubernetes.service.binding.runtime.ServiceBinding; +import io.quarkus.kubernetes.service.binding.runtime.ServiceBindingConfigSource; +import io.quarkus.kubernetes.service.binding.runtime.ServiceBindingConverter; + +public class PostgreSqlServiceBindingConverter implements ServiceBindingConverter { + + private static final Logger log = Logger.getLogger(ServiceBinding.class); + + @Override + public Optional convert(List serviceBindings) { + Optional matchingByType = ServiceBinding.singleMatchingByType("postgresql", serviceBindings); + if (!matchingByType.isPresent()) { + return Optional.empty(); + } + + Map properties = new HashMap<>(); + ServiceBinding binding = matchingByType.get(); + + String username = binding.getProperties().get("username"); + if (username != null) { + properties.put("quarkus.datasource.username", username); + } else { + log.debug("Property 'username' was not found"); + } + String password = binding.getProperties().get("password"); + if (password != null) { + properties.put("quarkus.datasource.password", password); + } else { + log.debug("Property 'password' was not found"); + } + String host = binding.getProperties().get("host"); + String port = binding.getProperties().get("port"); + String database = binding.getProperties().get("database"); + if ((host != null) && (database != null)) { + String portPart = ""; + if (port != null) { + portPart = ":" + port; + } + properties.put("quarkus.datasource.jdbc.url", String.format("jdbc:postgresql://%s%s/%s", host, portPart, database)); + } else { + log.debug("One or more of 'host' or 'database' properties were not found"); + } + return Optional.of(new ServiceBindingConfigSource("postgresql-k8s-service-binding-source", properties)); + } +} diff --git a/extensions/jdbc/jdbc-postgresql/runtime/src/main/resources/META-INF/services/io.quarkus.kubernetes.service.binding.runtime.ServiceBindingConverter b/extensions/jdbc/jdbc-postgresql/runtime/src/main/resources/META-INF/services/io.quarkus.kubernetes.service.binding.runtime.ServiceBindingConverter new file mode 100644 index 0000000000000..777d262cb4fac --- /dev/null +++ b/extensions/jdbc/jdbc-postgresql/runtime/src/main/resources/META-INF/services/io.quarkus.kubernetes.service.binding.runtime.ServiceBindingConverter @@ -0,0 +1 @@ +io.quarkus.jdbc.postgresql.runtime.PostgreSqlServiceBindingConverter From d208f564813f31e8e6493844272f2eb4fb8028df Mon Sep 17 00:00:00 2001 From: Erin Schnabel Date: Fri, 4 Dec 2020 16:53:06 -0500 Subject: [PATCH 24/62] micrometer export class member visibility --- .../deployment/export/AzureMonitorRegistryProcessor.java | 4 ++-- .../deployment/export/DatadogRegistryProcessor.java | 4 ++-- .../micrometer/deployment/export/JmxRegistryProcessor.java | 6 +++--- .../deployment/export/PrometheusRegistryProcessor.java | 4 ++-- .../deployment/export/SignalFxRegistryProcessor.java | 4 ++-- .../deployment/export/StackdriverRegistryProcessor.java | 6 +++--- .../deployment/export/StatsdRegistryProcessor.java | 4 ++-- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/AzureMonitorRegistryProcessor.java b/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/AzureMonitorRegistryProcessor.java index da56582668f26..60865f6657bde 100644 --- a/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/AzureMonitorRegistryProcessor.java +++ b/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/AzureMonitorRegistryProcessor.java @@ -34,13 +34,13 @@ public boolean getAsBoolean() { } @BuildStep(onlyIf = { NativeBuild.class, AzureMonitorEnabled.class }) - MicrometerRegistryProviderBuildItem nativeModeNotSupported() { + protected MicrometerRegistryProviderBuildItem nativeModeNotSupported() { log.info("The Azure Monitor meter registry does not support running in native mode."); return null; } @BuildStep(onlyIf = AzureMonitorEnabled.class, onlyIfNot = NativeBuild.class) - MicrometerRegistryProviderBuildItem createAzureMonitorRegistry(BuildProducer additionalBeans) { + protected MicrometerRegistryProviderBuildItem createAzureMonitorRegistry(BuildProducer additionalBeans) { // Add the AzureMonitor Registry Producer additionalBeans.produce( diff --git a/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/DatadogRegistryProcessor.java b/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/DatadogRegistryProcessor.java index e1eac78babb43..d4aba5f9a7870 100644 --- a/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/DatadogRegistryProcessor.java +++ b/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/DatadogRegistryProcessor.java @@ -20,7 +20,7 @@ public class DatadogRegistryProcessor { static final String REGISTRY_CLASS_NAME = "io.micrometer.datadog.DatadogMeterRegistry"; static final Class REGISTRY_CLASS = MicrometerRecorder.getClassForName(REGISTRY_CLASS_NAME); - static class DatadogEnabled implements BooleanSupplier { + public static class DatadogEnabled implements BooleanSupplier { MicrometerConfig mConfig; public boolean getAsBoolean() { @@ -29,7 +29,7 @@ public boolean getAsBoolean() { } @BuildStep(onlyIf = DatadogEnabled.class) - MicrometerRegistryProviderBuildItem createDatadogRegistry(CombinedIndexBuildItem index, + protected MicrometerRegistryProviderBuildItem createDatadogRegistry(CombinedIndexBuildItem index, BuildProducer additionalBeans) { // Add the Datadog Registry Producer diff --git a/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/JmxRegistryProcessor.java b/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/JmxRegistryProcessor.java index b59e2c7a36822..ecd91016765fa 100644 --- a/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/JmxRegistryProcessor.java +++ b/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/JmxRegistryProcessor.java @@ -25,7 +25,7 @@ public class JmxRegistryProcessor { static final String REGISTRY_CLASS_NAME = "io.micrometer.jmx.JmxMeterRegistry"; static final Class REGISTRY_CLASS = MicrometerRecorder.getClassForName(REGISTRY_CLASS_NAME); - static class JmxEnabled implements BooleanSupplier { + public static class JmxEnabled implements BooleanSupplier { MicrometerConfig mConfig; public boolean getAsBoolean() { @@ -34,14 +34,14 @@ public boolean getAsBoolean() { } @BuildStep(onlyIf = { NativeBuild.class, JmxEnabled.class }) - MicrometerRegistryProviderBuildItem createJmxRegistry(CombinedIndexBuildItem index) { + protected MicrometerRegistryProviderBuildItem createJmxRegistry(CombinedIndexBuildItem index) { log.info("JMX Meter Registry does not support running in native mode."); return null; } /** Jmx does not work with GraalVM */ @BuildStep(onlyIf = JmxEnabled.class, onlyIfNot = NativeBuild.class) - MicrometerRegistryProviderBuildItem createJmxRegistry(CombinedIndexBuildItem index, + protected MicrometerRegistryProviderBuildItem createJmxRegistry(CombinedIndexBuildItem index, BuildProducer additionalBeans) { // Add the Jmx Registry Producer diff --git a/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/PrometheusRegistryProcessor.java b/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/PrometheusRegistryProcessor.java index 9f41d40177dad..1cc04b143e2cb 100644 --- a/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/PrometheusRegistryProcessor.java +++ b/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/PrometheusRegistryProcessor.java @@ -37,7 +37,7 @@ public boolean getAsBoolean() { } @BuildStep(onlyIf = PrometheusEnabled.class) - MicrometerRegistryProviderBuildItem createPrometheusRegistry(BuildProducer additionalBeans) { + protected MicrometerRegistryProviderBuildItem createPrometheusRegistry(BuildProducer additionalBeans) { // Add the Prometheus Registry Producer additionalBeans.produce(AdditionalBeanBuildItem.builder() @@ -50,7 +50,7 @@ MicrometerRegistryProviderBuildItem createPrometheusRegistry(BuildProducer routes, + protected void createPrometheusRoute(BuildProducer routes, MicrometerConfig mConfig, PrometheusRecorder recorder) { diff --git a/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/SignalFxRegistryProcessor.java b/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/SignalFxRegistryProcessor.java index e650ab614ab87..45a3ec04d0861 100644 --- a/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/SignalFxRegistryProcessor.java +++ b/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/SignalFxRegistryProcessor.java @@ -34,13 +34,13 @@ public boolean getAsBoolean() { } @BuildStep(onlyIf = { NativeBuild.class, SignalFxRegistryEnabled.class }) - MicrometerRegistryProviderBuildItem nativeModeNotSupported() { + protected MicrometerRegistryProviderBuildItem nativeModeNotSupported() { log.info("The SignalFx meter registry does not support running in native mode."); return null; } @BuildStep(onlyIf = SignalFxRegistryEnabled.class, onlyIfNot = NativeBuild.class) - MicrometerRegistryProviderBuildItem createSignalFxRegistry(BuildProducer additionalBeans) { + protected MicrometerRegistryProviderBuildItem createSignalFxRegistry(BuildProducer additionalBeans) { // Add the SignalFx Registry Producer additionalBeans.produce( diff --git a/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/StackdriverRegistryProcessor.java b/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/StackdriverRegistryProcessor.java index 6cbf6ba4dab96..e801161f42005 100644 --- a/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/StackdriverRegistryProcessor.java +++ b/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/StackdriverRegistryProcessor.java @@ -25,7 +25,7 @@ public class StackdriverRegistryProcessor { static final String REGISTRY_CLASS_NAME = "io.micrometer.stackdriver.StackdriverMeterRegistry"; static final Class REGISTRY_CLASS = MicrometerRecorder.getClassForName(REGISTRY_CLASS_NAME); - static class StackdriverEnabled implements BooleanSupplier { + public static class StackdriverEnabled implements BooleanSupplier { MicrometerConfig mConfig; public boolean getAsBoolean() { @@ -34,14 +34,14 @@ public boolean getAsBoolean() { } @BuildStep(onlyIf = { NativeBuild.class, StackdriverEnabled.class }) - MicrometerRegistryProviderBuildItem nativeModeNotSupported() { + protected MicrometerRegistryProviderBuildItem nativeModeNotSupported() { log.info("The Stackdriver meter registry does not support running in native mode."); return null; } /** Stackdriver does not work with GraalVM */ @BuildStep(onlyIf = StackdriverEnabled.class, onlyIfNot = NativeBuild.class) - MicrometerRegistryProviderBuildItem createStackdriverRegistry(CombinedIndexBuildItem index, + protected MicrometerRegistryProviderBuildItem createStackdriverRegistry(CombinedIndexBuildItem index, BuildProducer additionalBeans) { // Add the Stackdriver Registry Producer diff --git a/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/StatsdRegistryProcessor.java b/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/StatsdRegistryProcessor.java index a5fdbbaa16d58..8df0d60a8664b 100644 --- a/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/StatsdRegistryProcessor.java +++ b/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/StatsdRegistryProcessor.java @@ -34,13 +34,13 @@ public boolean getAsBoolean() { } @BuildStep(onlyIf = { NativeBuild.class, StatsdRegistryEnabled.class }) - MicrometerRegistryProviderBuildItem nativeModeNotSupported() { + protected MicrometerRegistryProviderBuildItem nativeModeNotSupported() { log.info("The StatsD meter registry does not support running in native mode."); return null; } @BuildStep(onlyIf = StatsdRegistryEnabled.class, onlyIfNot = NativeBuild.class) - MicrometerRegistryProviderBuildItem createStatsdRegistry(BuildProducer additionalBeans) { + protected MicrometerRegistryProviderBuildItem createStatsdRegistry(BuildProducer additionalBeans) { // Add the Statsd Registry Producer additionalBeans.produce( From 9f46e4481b02609757342d924d1a94916f419611 Mon Sep 17 00:00:00 2001 From: Erin Schnabel Date: Tue, 8 Dec 2020 11:51:43 -0500 Subject: [PATCH 25/62] Move optional micrometer registries to quarkiverse extensions --- .github/dependabot.yml | 1 - .github/native-tests.json | 2 +- bom/application/pom.xml | 52 ----- extensions/micrometer/deployment/pom.xml | 30 --- .../export/AzureMonitorRegistryProcessor.java | 54 ----- .../export/DatadogRegistryProcessor.java | 43 ---- .../export/JmxRegistryProcessor.java | 55 ----- .../export/PrometheusRegistryProcessor.java | 5 +- .../export/SignalFxRegistryProcessor.java | 54 ----- .../export/StackdriverRegistryProcessor.java | 55 ----- .../export/StatsdRegistryProcessor.java | 55 ----- .../export/AllRegistriesDisabledTest.java | 7 +- .../AzureMonitorEnabledInvalidTest.java | 32 --- .../export/AzureMonitorEnabledTest.java | 44 ---- .../export/DatadogEnabledInvalidTest.java | 32 --- .../deployment/export/DatadogEnabledTest.java | 43 ---- .../deployment/export/JmxEnabledTest.java | 41 ---- .../export/SignalFxEnabledInvalidTest.java | 34 ---- .../export/SignalFxEnabledInvalidUriTest.java | 34 ---- .../export/SignalFxEnabledTest.java | 43 ---- .../export/StackdriverEnabledInvalidTest.java | 32 --- .../export/StackdriverEnabledTest.java | 44 ---- .../deployment/export/StatsdEnabledTest.java | 43 ---- .../{deployment/export => test}/Util.java | 2 +- extensions/micrometer/runtime/pom.xml | 30 --- .../runtime/config/AzureMonitorConfig.java | 32 --- .../runtime/config/DatadogConfig.java | 32 --- .../micrometer/runtime/config/JmxConfig.java | 32 --- .../runtime/config/MicrometerConfig.java | 6 - .../runtime/config/SignalFxConfig.java | 32 --- .../runtime/config/StackdriverConfig.java | 43 ---- .../runtime/config/StatsdConfig.java | 32 --- .../runtime/config/runtime/ExportConfig.java | 192 ------------------ .../AzureMonitorMeterRegistryProvider.java | 57 ------ .../export/DatadogMeterRegistryProvider.java | 61 ------ .../export/JmxMeterRegistryProvider.java | 49 ----- .../export/SignalFxMeterRegistryProvider.java | 50 ----- .../StackdriverMeterRegistryProvider.java | 59 ------ .../export/StatsdMeterRegistryProvider.java | 52 ----- integration-tests/micrometer-jmx/pom.xml | 158 -------------- .../micrometer/jmx/CustomConfiguration.java | 41 ---- .../it/micrometer/jmx/MessageResource.java | 53 ----- .../src/main/resources/application.properties | 8 - .../jmx/JmxMetricsRegistryTest.java | 89 -------- integration-tests/micrometer-native/pom.xml | 175 ---------------- .../native_mode/MessageResource.java | 36 ---- .../src/main/resources/application.properties | 15 -- .../native_mode/NativeMeterRegistriesIT.java | 38 ---- .../NativeMeterRegistriesTest.java | 40 ---- integration-tests/pom.xml | 2 - 50 files changed, 6 insertions(+), 2245 deletions(-) delete mode 100644 extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/AzureMonitorRegistryProcessor.java delete mode 100644 extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/DatadogRegistryProcessor.java delete mode 100644 extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/JmxRegistryProcessor.java delete mode 100644 extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/SignalFxRegistryProcessor.java delete mode 100644 extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/StackdriverRegistryProcessor.java delete mode 100644 extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/StatsdRegistryProcessor.java delete mode 100644 extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/AzureMonitorEnabledInvalidTest.java delete mode 100644 extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/AzureMonitorEnabledTest.java delete mode 100644 extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/DatadogEnabledInvalidTest.java delete mode 100644 extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/DatadogEnabledTest.java delete mode 100644 extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/JmxEnabledTest.java delete mode 100644 extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/SignalFxEnabledInvalidTest.java delete mode 100644 extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/SignalFxEnabledInvalidUriTest.java delete mode 100644 extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/SignalFxEnabledTest.java delete mode 100644 extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/StackdriverEnabledInvalidTest.java delete mode 100644 extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/StackdriverEnabledTest.java delete mode 100644 extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/StatsdEnabledTest.java rename extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/{deployment/export => test}/Util.java (95%) delete mode 100644 extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/config/AzureMonitorConfig.java delete mode 100644 extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/config/DatadogConfig.java delete mode 100644 extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/config/JmxConfig.java delete mode 100644 extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/config/SignalFxConfig.java delete mode 100644 extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/config/StackdriverConfig.java delete mode 100644 extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/config/StatsdConfig.java delete mode 100644 extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/export/AzureMonitorMeterRegistryProvider.java delete mode 100644 extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/export/DatadogMeterRegistryProvider.java delete mode 100644 extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/export/JmxMeterRegistryProvider.java delete mode 100644 extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/export/SignalFxMeterRegistryProvider.java delete mode 100644 extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/export/StackdriverMeterRegistryProvider.java delete mode 100644 extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/export/StatsdMeterRegistryProvider.java delete mode 100644 integration-tests/micrometer-jmx/pom.xml delete mode 100644 integration-tests/micrometer-jmx/src/main/java/io/quarkus/it/micrometer/jmx/CustomConfiguration.java delete mode 100644 integration-tests/micrometer-jmx/src/main/java/io/quarkus/it/micrometer/jmx/MessageResource.java delete mode 100644 integration-tests/micrometer-jmx/src/main/resources/application.properties delete mode 100644 integration-tests/micrometer-jmx/src/test/java/io/quarkus/it/micrometer/jmx/JmxMetricsRegistryTest.java delete mode 100644 integration-tests/micrometer-native/pom.xml delete mode 100644 integration-tests/micrometer-native/src/main/java/io/quarkus/it/micrometer/native_mode/MessageResource.java delete mode 100644 integration-tests/micrometer-native/src/main/resources/application.properties delete mode 100644 integration-tests/micrometer-native/src/test/java/io/quarkus/it/micrometer/native_mode/NativeMeterRegistriesIT.java delete mode 100644 integration-tests/micrometer-native/src/test/java/io/quarkus/it/micrometer/native_mode/NativeMeterRegistriesTest.java diff --git a/.github/dependabot.yml b/.github/dependabot.yml index f279eb1773112..37070f9b73738 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -119,7 +119,6 @@ updates: - dependency-name: commons-io:commons-io # Micrometer - dependency-name: io.micrometer:micrometer-bom - - dependency-name: io.micrometer:micrometer-registry-stackdriver # BouncyCastle - dependency-name: org.bouncycastle:bcprov-jdk15on - dependency-name: org.bouncycastle:bctls-jdk15on diff --git a/.github/native-tests.json b/.github/native-tests.json index d894a45cc660e..e049d6ecbe552 100644 --- a/.github/native-tests.json +++ b/.github/native-tests.json @@ -110,7 +110,7 @@ { "category": "Misc4", "timeout": 65, - "test-modules": "smallrye-config smallrye-graphql picocli-native gradle micrometer-mp-metrics micrometer-native micrometer-prometheus smallrye-metrics logging-json jaxp" + "test-modules": "smallrye-config smallrye-graphql picocli-native gradle micrometer-mp-metrics micrometer-prometheus smallrye-metrics logging-json jaxp" }, { "category": "Spring", diff --git a/bom/application/pom.xml b/bom/application/pom.xml index e942430fdd71e..8e4eee62d9cd4 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -4811,58 +4811,6 @@ ${grpc.version} - - - org.threeten - threetenbp - ${threetenbp.version} - - - io.micrometer - micrometer-registry-stackdriver - ${micrometer.version} - - - commons-logging - commons-logging - - - javax.annotation - javax.annotation-api - - - org.checkerframework - checker-qual - - - com.google.j2objc - j2objc-annotations - - - com.google.android - annotations - - - org.codehaus.mojo - animal-sniffer-annotations - - - com.google.auto.value - auto-value-annotations - - - - - com.google.auth - google-auth-library-oauth2-http - ${google-auth.version} - - - com.google.auth - google-auth-library-credentials - ${google-auth.version} - - info.picocli diff --git a/extensions/micrometer/deployment/pom.xml b/extensions/micrometer/deployment/pom.xml index 805bf651ebaee..15f71b81880e1 100644 --- a/extensions/micrometer/deployment/pom.xml +++ b/extensions/micrometer/deployment/pom.xml @@ -48,41 +48,11 @@ - - io.micrometer - micrometer-registry-azure-monitor - test - - - io.micrometer - micrometer-registry-datadog - test - - - io.micrometer - micrometer-registry-jmx - test - io.micrometer micrometer-registry-prometheus test - - io.micrometer - micrometer-registry-signalfx - test - - - io.micrometer - micrometer-registry-stackdriver - test - - - io.micrometer - micrometer-registry-statsd - test - io.quarkus diff --git a/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/AzureMonitorRegistryProcessor.java b/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/AzureMonitorRegistryProcessor.java deleted file mode 100644 index 60865f6657bde..0000000000000 --- a/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/AzureMonitorRegistryProcessor.java +++ /dev/null @@ -1,54 +0,0 @@ -package io.quarkus.micrometer.deployment.export; - -import java.util.function.BooleanSupplier; - -import org.jboss.logging.Logger; - -import io.quarkus.arc.deployment.AdditionalBeanBuildItem; -import io.quarkus.deployment.annotations.BuildProducer; -import io.quarkus.deployment.annotations.BuildStep; -import io.quarkus.deployment.pkg.steps.NativeBuild; -import io.quarkus.micrometer.deployment.MicrometerRegistryProviderBuildItem; -import io.quarkus.micrometer.runtime.MicrometerRecorder; -import io.quarkus.micrometer.runtime.config.MicrometerConfig; -import io.quarkus.micrometer.runtime.export.AzureMonitorMeterRegistryProvider; - -/** - * Add support for the Azure Monitor Meter Registry. Note that the registry may not - * be available at deployment time for some projects: Avoid direct class - * references. - */ -public class AzureMonitorRegistryProcessor { - private static final Logger log = Logger.getLogger(AzureMonitorRegistryProcessor.class); - - static final String REGISTRY_CLASS_NAME = "io.micrometer.azuremonitor.AzureMonitorMeterRegistry"; - static final Class REGISTRY_CLASS = MicrometerRecorder.getClassForName(REGISTRY_CLASS_NAME); - - public static class AzureMonitorEnabled implements BooleanSupplier { - MicrometerConfig mConfig; - - @Override - public boolean getAsBoolean() { - return REGISTRY_CLASS != null && mConfig.checkRegistryEnabledWithDefault(mConfig.export.azuremonitor); - } - } - - @BuildStep(onlyIf = { NativeBuild.class, AzureMonitorEnabled.class }) - protected MicrometerRegistryProviderBuildItem nativeModeNotSupported() { - log.info("The Azure Monitor meter registry does not support running in native mode."); - return null; - } - - @BuildStep(onlyIf = AzureMonitorEnabled.class, onlyIfNot = NativeBuild.class) - protected MicrometerRegistryProviderBuildItem createAzureMonitorRegistry(BuildProducer additionalBeans) { - - // Add the AzureMonitor Registry Producer - additionalBeans.produce( - AdditionalBeanBuildItem.builder() - .addBeanClass(AzureMonitorMeterRegistryProvider.class) - .setUnremovable().build()); - - // Include the AzureMonitorMeterRegistry in a possible CompositeMeterRegistry - return new MicrometerRegistryProviderBuildItem(REGISTRY_CLASS); - } -} diff --git a/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/DatadogRegistryProcessor.java b/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/DatadogRegistryProcessor.java deleted file mode 100644 index d4aba5f9a7870..0000000000000 --- a/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/DatadogRegistryProcessor.java +++ /dev/null @@ -1,43 +0,0 @@ -package io.quarkus.micrometer.deployment.export; - -import java.util.function.BooleanSupplier; - -import io.quarkus.arc.deployment.AdditionalBeanBuildItem; -import io.quarkus.deployment.annotations.BuildProducer; -import io.quarkus.deployment.annotations.BuildStep; -import io.quarkus.deployment.builditem.CombinedIndexBuildItem; -import io.quarkus.micrometer.deployment.MicrometerRegistryProviderBuildItem; -import io.quarkus.micrometer.runtime.MicrometerRecorder; -import io.quarkus.micrometer.runtime.config.MicrometerConfig; -import io.quarkus.micrometer.runtime.export.DatadogMeterRegistryProvider; - -/** - * Add support for the Datadog Meter Registry. Note that the registry may not - * be available at deployment time for some projects: Avoid direct class - * references. - */ -public class DatadogRegistryProcessor { - static final String REGISTRY_CLASS_NAME = "io.micrometer.datadog.DatadogMeterRegistry"; - static final Class REGISTRY_CLASS = MicrometerRecorder.getClassForName(REGISTRY_CLASS_NAME); - - public static class DatadogEnabled implements BooleanSupplier { - MicrometerConfig mConfig; - - public boolean getAsBoolean() { - return REGISTRY_CLASS != null && mConfig.checkRegistryEnabledWithDefault(mConfig.export.datadog); - } - } - - @BuildStep(onlyIf = DatadogEnabled.class) - protected MicrometerRegistryProviderBuildItem createDatadogRegistry(CombinedIndexBuildItem index, - BuildProducer additionalBeans) { - - // Add the Datadog Registry Producer - additionalBeans.produce(AdditionalBeanBuildItem.builder() - .addBeanClass(DatadogMeterRegistryProvider.class) - .setUnremovable().build()); - - // Include the DatadogMeterRegistry in a possible CompositeMeterRegistry - return new MicrometerRegistryProviderBuildItem(REGISTRY_CLASS); - } -} diff --git a/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/JmxRegistryProcessor.java b/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/JmxRegistryProcessor.java deleted file mode 100644 index ecd91016765fa..0000000000000 --- a/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/JmxRegistryProcessor.java +++ /dev/null @@ -1,55 +0,0 @@ -package io.quarkus.micrometer.deployment.export; - -import java.util.function.BooleanSupplier; - -import org.jboss.logging.Logger; - -import io.quarkus.arc.deployment.AdditionalBeanBuildItem; -import io.quarkus.deployment.annotations.BuildProducer; -import io.quarkus.deployment.annotations.BuildStep; -import io.quarkus.deployment.builditem.CombinedIndexBuildItem; -import io.quarkus.deployment.pkg.steps.NativeBuild; -import io.quarkus.micrometer.deployment.MicrometerRegistryProviderBuildItem; -import io.quarkus.micrometer.runtime.MicrometerRecorder; -import io.quarkus.micrometer.runtime.config.MicrometerConfig; -import io.quarkus.micrometer.runtime.export.JmxMeterRegistryProvider; - -/** - * Add support for the Jmx Meter Registry. Note that the registry may not - * be available at deployment time for some projects: Avoid direct class - * references. - */ -public class JmxRegistryProcessor { - private static final Logger log = Logger.getLogger(JmxRegistryProcessor.class); - - static final String REGISTRY_CLASS_NAME = "io.micrometer.jmx.JmxMeterRegistry"; - static final Class REGISTRY_CLASS = MicrometerRecorder.getClassForName(REGISTRY_CLASS_NAME); - - public static class JmxEnabled implements BooleanSupplier { - MicrometerConfig mConfig; - - public boolean getAsBoolean() { - return REGISTRY_CLASS != null && mConfig.checkRegistryEnabledWithDefault(mConfig.export.jmx); - } - } - - @BuildStep(onlyIf = { NativeBuild.class, JmxEnabled.class }) - protected MicrometerRegistryProviderBuildItem createJmxRegistry(CombinedIndexBuildItem index) { - log.info("JMX Meter Registry does not support running in native mode."); - return null; - } - - /** Jmx does not work with GraalVM */ - @BuildStep(onlyIf = JmxEnabled.class, onlyIfNot = NativeBuild.class) - protected MicrometerRegistryProviderBuildItem createJmxRegistry(CombinedIndexBuildItem index, - BuildProducer additionalBeans) { - - // Add the Jmx Registry Producer - additionalBeans.produce(AdditionalBeanBuildItem.builder() - .addBeanClass(JmxMeterRegistryProvider.class) - .setUnremovable().build()); - - // Include the JmxMeterRegistry in a possible CompositeMeterRegistry - return new MicrometerRegistryProviderBuildItem(REGISTRY_CLASS); - } -} diff --git a/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/PrometheusRegistryProcessor.java b/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/PrometheusRegistryProcessor.java index 1cc04b143e2cb..87fd0474d22bc 100644 --- a/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/PrometheusRegistryProcessor.java +++ b/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/PrometheusRegistryProcessor.java @@ -37,7 +37,8 @@ public boolean getAsBoolean() { } @BuildStep(onlyIf = PrometheusEnabled.class) - protected MicrometerRegistryProviderBuildItem createPrometheusRegistry(BuildProducer additionalBeans) { + MicrometerRegistryProviderBuildItem createPrometheusRegistry( + BuildProducer additionalBeans) { // Add the Prometheus Registry Producer additionalBeans.produce(AdditionalBeanBuildItem.builder() @@ -50,7 +51,7 @@ protected MicrometerRegistryProviderBuildItem createPrometheusRegistry(BuildProd @BuildStep(onlyIf = PrometheusEnabled.class) @Record(value = ExecutionTime.STATIC_INIT) - protected void createPrometheusRoute(BuildProducer routes, + void createPrometheusRoute(BuildProducer routes, MicrometerConfig mConfig, PrometheusRecorder recorder) { diff --git a/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/SignalFxRegistryProcessor.java b/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/SignalFxRegistryProcessor.java deleted file mode 100644 index 45a3ec04d0861..0000000000000 --- a/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/SignalFxRegistryProcessor.java +++ /dev/null @@ -1,54 +0,0 @@ -package io.quarkus.micrometer.deployment.export; - -import java.util.function.BooleanSupplier; - -import org.jboss.logging.Logger; - -import io.quarkus.arc.deployment.AdditionalBeanBuildItem; -import io.quarkus.deployment.annotations.BuildProducer; -import io.quarkus.deployment.annotations.BuildStep; -import io.quarkus.deployment.pkg.steps.NativeBuild; -import io.quarkus.micrometer.deployment.MicrometerRegistryProviderBuildItem; -import io.quarkus.micrometer.runtime.MicrometerRecorder; -import io.quarkus.micrometer.runtime.config.MicrometerConfig; -import io.quarkus.micrometer.runtime.export.SignalFxMeterRegistryProvider; - -/** - * Add support for the SignalFx Meter Registry. Note that the registry may not - * be available at deployment time for some projects: Avoid direct class - * references. - */ -public class SignalFxRegistryProcessor { - private static final Logger log = Logger.getLogger(SignalFxRegistryProcessor.class); - - static final String REGISTRY_CLASS_NAME = "io.micrometer.signalfx.SignalFxMeterRegistry"; - static final Class REGISTRY_CLASS = MicrometerRecorder.getClassForName(REGISTRY_CLASS_NAME); - - public static class SignalFxRegistryEnabled implements BooleanSupplier { - MicrometerConfig mConfig; - - @Override - public boolean getAsBoolean() { - return REGISTRY_CLASS != null && mConfig.checkRegistryEnabledWithDefault(mConfig.export.signalfx); - } - } - - @BuildStep(onlyIf = { NativeBuild.class, SignalFxRegistryEnabled.class }) - protected MicrometerRegistryProviderBuildItem nativeModeNotSupported() { - log.info("The SignalFx meter registry does not support running in native mode."); - return null; - } - - @BuildStep(onlyIf = SignalFxRegistryEnabled.class, onlyIfNot = NativeBuild.class) - protected MicrometerRegistryProviderBuildItem createSignalFxRegistry(BuildProducer additionalBeans) { - - // Add the SignalFx Registry Producer - additionalBeans.produce( - AdditionalBeanBuildItem.builder() - .addBeanClass(SignalFxMeterRegistryProvider.class) - .setUnremovable().build()); - - // Include the SignalFxMeterRegistryProvider in a possible CompositeMeterRegistry - return new MicrometerRegistryProviderBuildItem(REGISTRY_CLASS); - } -} diff --git a/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/StackdriverRegistryProcessor.java b/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/StackdriverRegistryProcessor.java deleted file mode 100644 index e801161f42005..0000000000000 --- a/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/StackdriverRegistryProcessor.java +++ /dev/null @@ -1,55 +0,0 @@ -package io.quarkus.micrometer.deployment.export; - -import java.util.function.BooleanSupplier; - -import org.jboss.logging.Logger; - -import io.quarkus.arc.deployment.AdditionalBeanBuildItem; -import io.quarkus.deployment.annotations.BuildProducer; -import io.quarkus.deployment.annotations.BuildStep; -import io.quarkus.deployment.builditem.CombinedIndexBuildItem; -import io.quarkus.deployment.pkg.steps.NativeBuild; -import io.quarkus.micrometer.deployment.MicrometerRegistryProviderBuildItem; -import io.quarkus.micrometer.runtime.MicrometerRecorder; -import io.quarkus.micrometer.runtime.config.MicrometerConfig; -import io.quarkus.micrometer.runtime.export.StackdriverMeterRegistryProvider; - -/** - * Add support for the Stackdriver Meter Registry. Note that the registry may not - * be available at deployment time for some projects: Avoid direct class - * references. - */ -public class StackdriverRegistryProcessor { - private static final Logger log = Logger.getLogger(StackdriverRegistryProcessor.class); - - static final String REGISTRY_CLASS_NAME = "io.micrometer.stackdriver.StackdriverMeterRegistry"; - static final Class REGISTRY_CLASS = MicrometerRecorder.getClassForName(REGISTRY_CLASS_NAME); - - public static class StackdriverEnabled implements BooleanSupplier { - MicrometerConfig mConfig; - - public boolean getAsBoolean() { - return REGISTRY_CLASS != null && mConfig.checkRegistryEnabledWithDefault(mConfig.export.stackdriver); - } - } - - @BuildStep(onlyIf = { NativeBuild.class, StackdriverEnabled.class }) - protected MicrometerRegistryProviderBuildItem nativeModeNotSupported() { - log.info("The Stackdriver meter registry does not support running in native mode."); - return null; - } - - /** Stackdriver does not work with GraalVM */ - @BuildStep(onlyIf = StackdriverEnabled.class, onlyIfNot = NativeBuild.class) - protected MicrometerRegistryProviderBuildItem createStackdriverRegistry(CombinedIndexBuildItem index, - BuildProducer additionalBeans) { - - // Add the Stackdriver Registry Producer - additionalBeans.produce(AdditionalBeanBuildItem.builder() - .addBeanClass(StackdriverMeterRegistryProvider.class) - .setUnremovable().build()); - - // Include the StackdriverMeterRegistry in a possible CompositeMeterRegistry - return new MicrometerRegistryProviderBuildItem(REGISTRY_CLASS); - } -} diff --git a/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/StatsdRegistryProcessor.java b/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/StatsdRegistryProcessor.java deleted file mode 100644 index 8df0d60a8664b..0000000000000 --- a/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/export/StatsdRegistryProcessor.java +++ /dev/null @@ -1,55 +0,0 @@ -package io.quarkus.micrometer.deployment.export; - -import java.util.function.BooleanSupplier; - -import org.jboss.logging.Logger; - -import io.quarkus.arc.deployment.AdditionalBeanBuildItem; -import io.quarkus.deployment.annotations.BuildProducer; -import io.quarkus.deployment.annotations.BuildStep; -import io.quarkus.deployment.pkg.steps.NativeBuild; -import io.quarkus.micrometer.deployment.MicrometerRegistryProviderBuildItem; -import io.quarkus.micrometer.runtime.MicrometerRecorder; -import io.quarkus.micrometer.runtime.config.MicrometerConfig; -import io.quarkus.micrometer.runtime.export.StatsdMeterRegistryProvider; - -/** - * Add support for the StatsD Meter Registry. Note that the registry may not - * be available at deployment time for some projects: Avoid direct class - * references. - */ -public class StatsdRegistryProcessor { - private static final Logger log = Logger.getLogger(StatsdRegistryProcessor.class); - - static final String REGISTRY_CLASS_NAME = "io.micrometer.statsd.StatsdMeterRegistry"; - static final Class REGISTRY_CLASS = MicrometerRecorder.getClassForName(REGISTRY_CLASS_NAME); - - public static class StatsdRegistryEnabled implements BooleanSupplier { - MicrometerConfig mConfig; - - @Override - public boolean getAsBoolean() { - return REGISTRY_CLASS != null && mConfig.checkRegistryEnabledWithDefault(mConfig.export.statsd); - } - } - - @BuildStep(onlyIf = { NativeBuild.class, StatsdRegistryEnabled.class }) - protected MicrometerRegistryProviderBuildItem nativeModeNotSupported() { - log.info("The StatsD meter registry does not support running in native mode."); - return null; - } - - @BuildStep(onlyIf = StatsdRegistryEnabled.class, onlyIfNot = NativeBuild.class) - protected MicrometerRegistryProviderBuildItem createStatsdRegistry(BuildProducer additionalBeans) { - - // Add the Statsd Registry Producer - additionalBeans.produce( - AdditionalBeanBuildItem.builder() - .addBeanClass(StatsdMeterRegistryProvider.class) - .setUnremovable().build()); - - // Include the StatsdMeterRegistryProvider in a possible CompositeMeterRegistry - return new MicrometerRegistryProviderBuildItem(REGISTRY_CLASS); - } - -} diff --git a/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/AllRegistriesDisabledTest.java b/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/AllRegistriesDisabledTest.java index bbb027447a0cf..a90eb24b235cc 100644 --- a/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/AllRegistriesDisabledTest.java +++ b/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/AllRegistriesDisabledTest.java @@ -22,13 +22,8 @@ public class AllRegistriesDisabledTest { static final QuarkusUnitTest config = new QuarkusUnitTest() .withConfigurationResource("test-logging.properties") .overrideConfigKey("quarkus.micrometer.binder-enabled-default", "false") - .overrideConfigKey("quarkus.micrometer.export.azuremonitor.enabled", "false") - .overrideConfigKey("quarkus.micrometer.export.datadog.enabled", "false") - .overrideConfigKey("quarkus.micrometer.export.jmx.enabled", "false") + .overrideConfigKey("quarkus.micrometer.export.json.enabled", "false") .overrideConfigKey("quarkus.micrometer.export.prometheus.enabled", "false") - .overrideConfigKey("quarkus.micrometer.export.signalfx.enabled", "false") - .overrideConfigKey("quarkus.micrometer.export.stackdriver.enabled", "false") - .overrideConfigKey("quarkus.micrometer.export.statsd.enabled", "false") .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)); @Inject diff --git a/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/AzureMonitorEnabledInvalidTest.java b/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/AzureMonitorEnabledInvalidTest.java deleted file mode 100644 index 5e358bf37a0bf..0000000000000 --- a/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/AzureMonitorEnabledInvalidTest.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.quarkus.micrometer.deployment.export; - -import org.jboss.shrinkwrap.api.ShrinkWrap; -import org.jboss.shrinkwrap.api.spec.JavaArchive; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; - -import io.micrometer.core.instrument.config.validate.ValidationException; -import io.quarkus.test.QuarkusUnitTest; - -public class AzureMonitorEnabledInvalidTest { - final static String testedAttribute = "quarkus.micrometer.export.azuremonitor.instrumentation-key"; - - @RegisterExtension - static final QuarkusUnitTest config = new QuarkusUnitTest() - .withConfigurationResource("test-logging.properties") - .overrideConfigKey("quarkus.micrometer.binder-enabled-default", "false") - .overrideConfigKey("quarkus.micrometer.export.azuremonitor.enabled", "true") - .overrideConfigKey("quarkus.micrometer.registry-enabled-default", "false") - .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) - .addClass(AzureMonitorRegistryProcessor.REGISTRY_CLASS)) - .setLogRecordPredicate(r -> "io.quarkus.micrometer.runtime.export.ConfigAdapter".equals(r.getLoggerName())) - .assertLogRecords(r -> Util.assertMessage(testedAttribute, r)) - .assertException(t -> Assertions.assertEquals(ValidationException.class.getName(), t.getClass().getName(), - "Unexpected exception in test: " + Util.stackToString(t))); - - @Test - public void testMeterRegistryPresent() { - Assertions.fail("Runtime should not have initialized with missing " + testedAttribute); - } -} diff --git a/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/AzureMonitorEnabledTest.java b/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/AzureMonitorEnabledTest.java deleted file mode 100644 index d25fc06c6d4f6..0000000000000 --- a/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/AzureMonitorEnabledTest.java +++ /dev/null @@ -1,44 +0,0 @@ -package io.quarkus.micrometer.deployment.export; - -import java.util.Set; - -import javax.inject.Inject; - -import org.jboss.shrinkwrap.api.ShrinkWrap; -import org.jboss.shrinkwrap.api.spec.JavaArchive; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; - -import io.micrometer.core.instrument.MeterRegistry; -import io.micrometer.core.instrument.composite.CompositeMeterRegistry; -import io.quarkus.micrometer.runtime.MicrometerRecorder; -import io.quarkus.test.QuarkusUnitTest; - -public class AzureMonitorEnabledTest { - static final String REGISTRY_CLASS_NAME = "io.micrometer.azuremonitor.AzureMonitorMeterRegistry"; - static final Class REGISTRY_CLASS = MicrometerRecorder.getClassForName(REGISTRY_CLASS_NAME); - - @RegisterExtension - static final QuarkusUnitTest config = new QuarkusUnitTest() - .withConfigurationResource("test-logging.properties") - .overrideConfigKey("quarkus.micrometer.binder-enabled-default", "false") - .overrideConfigKey("quarkus.micrometer.export.azuremonitor.enabled", "true") - .overrideConfigKey("quarkus.micrometer.export.azuremonitor.instrumentation-key", "TEST") - .overrideConfigKey("quarkus.micrometer.registry-enabled-default", "false") - .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) - .addClass(AzureMonitorRegistryProcessor.REGISTRY_CLASS)); - - @Inject - MeterRegistry registry; - - @Test - public void testMeterRegistryPresent() { - // AzureMonitor is enabled (alone, all others disabled) - Assertions.assertNotNull(registry, "A registry should be configured"); - Set subRegistries = ((CompositeMeterRegistry) registry).getRegistries(); - Assertions.assertEquals( - REGISTRY_CLASS, subRegistries.iterator().next().getClass(), - "Should be AzureMonitorMeterRegistry"); - } -} diff --git a/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/DatadogEnabledInvalidTest.java b/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/DatadogEnabledInvalidTest.java deleted file mode 100644 index 84b4e39075adb..0000000000000 --- a/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/DatadogEnabledInvalidTest.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.quarkus.micrometer.deployment.export; - -import org.jboss.shrinkwrap.api.ShrinkWrap; -import org.jboss.shrinkwrap.api.spec.JavaArchive; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; - -import io.micrometer.core.instrument.config.validate.ValidationException; -import io.quarkus.test.QuarkusUnitTest; - -public class DatadogEnabledInvalidTest { - final static String testedAttribute = "quarkus.micrometer.export.datadog.api-key"; - - @RegisterExtension - static final QuarkusUnitTest config = new QuarkusUnitTest() - .withConfigurationResource("test-logging.properties") - .overrideConfigKey("quarkus.micrometer.binder-enabled-default", "false") - .overrideConfigKey("quarkus.micrometer.export.datadog.enabled", "true") - .overrideConfigKey("quarkus.micrometer.registry-enabled-default", "false") - .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) - .addClass(DatadogRegistryProcessor.REGISTRY_CLASS)) - .setLogRecordPredicate(r -> "io.quarkus.micrometer.runtime.export.ConfigAdapter".equals(r.getLoggerName())) - .assertLogRecords(r -> Util.assertMessage(testedAttribute, r)) - .assertException(t -> Assertions.assertEquals(ValidationException.class.getName(), t.getClass().getName(), - "Unexpected exception in test: " + Util.stackToString(t))); - - @Test - public void testMeterRegistryPresent() { - Assertions.fail("Runtime should not have initialized with missing " + testedAttribute); - } -} diff --git a/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/DatadogEnabledTest.java b/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/DatadogEnabledTest.java deleted file mode 100644 index c935cf9cdc9ee..0000000000000 --- a/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/DatadogEnabledTest.java +++ /dev/null @@ -1,43 +0,0 @@ -package io.quarkus.micrometer.deployment.export; - -import java.util.Set; - -import javax.inject.Inject; - -import org.jboss.shrinkwrap.api.ShrinkWrap; -import org.jboss.shrinkwrap.api.spec.JavaArchive; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; - -import io.micrometer.core.instrument.MeterRegistry; -import io.micrometer.core.instrument.composite.CompositeMeterRegistry; -import io.quarkus.micrometer.runtime.MicrometerRecorder; -import io.quarkus.test.QuarkusUnitTest; - -public class DatadogEnabledTest { - static final String REGISTRY_CLASS_NAME = "io.micrometer.datadog.DatadogMeterRegistry"; - static final Class REGISTRY_CLASS = MicrometerRecorder.getClassForName(REGISTRY_CLASS_NAME); - - @RegisterExtension - static final QuarkusUnitTest config = new QuarkusUnitTest() - .withConfigurationResource("test-logging.properties") - .overrideConfigKey("quarkus.micrometer.binder-enabled-default", "false") - .overrideConfigKey("quarkus.micrometer.export.datadog.enabled", "true") - .overrideConfigKey("quarkus.micrometer.export.datadog.publish", "false") - .overrideConfigKey("quarkus.micrometer.export.datadog.apiKey", "dummy") - .overrideConfigKey("quarkus.micrometer.registry-enabled-default", "false") - .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) - .addClass(DatadogRegistryProcessor.REGISTRY_CLASS)); - - @Inject - MeterRegistry registry; - - @Test - public void testMeterRegistryPresent() { - // Datadog is enabled (alone, all others disabled) - Assertions.assertNotNull(registry, "A registry should be configured"); - Set subRegistries = ((CompositeMeterRegistry) registry).getRegistries(); - Assertions.assertEquals(REGISTRY_CLASS, subRegistries.iterator().next().getClass(), "Should be DatadogMeterRegistry"); - } -} diff --git a/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/JmxEnabledTest.java b/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/JmxEnabledTest.java deleted file mode 100644 index 49801f061e4ba..0000000000000 --- a/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/JmxEnabledTest.java +++ /dev/null @@ -1,41 +0,0 @@ -package io.quarkus.micrometer.deployment.export; - -import java.util.Set; - -import javax.inject.Inject; - -import org.jboss.shrinkwrap.api.ShrinkWrap; -import org.jboss.shrinkwrap.api.spec.JavaArchive; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; - -import io.micrometer.core.instrument.MeterRegistry; -import io.micrometer.core.instrument.composite.CompositeMeterRegistry; -import io.quarkus.micrometer.runtime.MicrometerRecorder; -import io.quarkus.test.QuarkusUnitTest; - -public class JmxEnabledTest { - static final String REGISTRY_CLASS_NAME = "io.micrometer.jmx.JmxMeterRegistry"; - static final Class REGISTRY_CLASS = MicrometerRecorder.getClassForName(REGISTRY_CLASS_NAME); - - @RegisterExtension - static final QuarkusUnitTest config = new QuarkusUnitTest() - .withConfigurationResource("test-logging.properties") - .overrideConfigKey("quarkus.micrometer.binder-enabled-default", "false") - .overrideConfigKey("quarkus.micrometer.export.jmx.enabled", "true") - .overrideConfigKey("quarkus.micrometer.registry-enabled-default", "false") - .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) - .addClass(JmxRegistryProcessor.REGISTRY_CLASS)); - - @Inject - MeterRegistry registry; - - @Test - public void testMeterRegistryPresent() { - // Jmx is enabled (alone, all others disabled) - Assertions.assertNotNull(registry, "A registry should be configured"); - Set subRegistries = ((CompositeMeterRegistry) registry).getRegistries(); - Assertions.assertEquals(REGISTRY_CLASS, subRegistries.iterator().next().getClass(), "Should be JmxMeterRegistry"); - } -} diff --git a/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/SignalFxEnabledInvalidTest.java b/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/SignalFxEnabledInvalidTest.java deleted file mode 100644 index e17e788d77d66..0000000000000 --- a/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/SignalFxEnabledInvalidTest.java +++ /dev/null @@ -1,34 +0,0 @@ -package io.quarkus.micrometer.deployment.export; - -import org.jboss.shrinkwrap.api.ShrinkWrap; -import org.jboss.shrinkwrap.api.spec.JavaArchive; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; - -import io.micrometer.core.instrument.config.validate.ValidationException; -import io.quarkus.test.QuarkusUnitTest; - -public class SignalFxEnabledInvalidTest { - final static String testedAttribute = "quarkus.micrometer.export.signalfx.access-token"; - - @RegisterExtension - static final QuarkusUnitTest config = new QuarkusUnitTest() - .withConfigurationResource("test-logging.properties") - .overrideConfigKey("quarkus.micrometer.binder-enabled-default", "false") - .overrideConfigKey("quarkus.micrometer.export.signalfx.enabled", "true") - .overrideConfigKey("quarkus.micrometer.registry-enabled-default", "false") - .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) - .addClass(SignalFxRegistryProcessor.REGISTRY_CLASS)) - .setLogRecordPredicate(r -> "io.quarkus.micrometer.runtime.export.ConfigAdapter".equals(r.getLoggerName())) - .assertLogRecords(r -> Util.assertMessage(testedAttribute, r)) - .assertException(t -> { - Assertions.assertEquals(ValidationException.class.getName(), t.getClass().getName(), - "Unexpected exception in test: " + Util.stackToString(t)); - }); - - @Test - public void testMeterRegistryPresent() { - Assertions.fail("Runtime should not have initialized with missing " + testedAttribute); - } -} diff --git a/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/SignalFxEnabledInvalidUriTest.java b/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/SignalFxEnabledInvalidUriTest.java deleted file mode 100644 index 40f65d1d15ff1..0000000000000 --- a/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/SignalFxEnabledInvalidUriTest.java +++ /dev/null @@ -1,34 +0,0 @@ -package io.quarkus.micrometer.deployment.export; - -import org.jboss.shrinkwrap.api.ShrinkWrap; -import org.jboss.shrinkwrap.api.spec.JavaArchive; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; - -import io.micrometer.core.instrument.config.validate.ValidationException; -import io.quarkus.test.QuarkusUnitTest; - -public class SignalFxEnabledInvalidUriTest { - final static String testedAttribute = "quarkus.micrometer.export.signalfx.uri"; - - @RegisterExtension - static final QuarkusUnitTest config = new QuarkusUnitTest() - .withConfigurationResource("test-logging.properties") - .overrideConfigKey("quarkus.micrometer.binder-enabled-default", "false") - .overrideConfigKey("quarkus.micrometer.export.signalfx.enabled", "true") - .overrideConfigKey("quarkus.micrometer.export.signalfx.access-token", "required") - .overrideConfigKey("quarkus.micrometer.export.signalfx.uri", "intentionally-bad-uri") - .overrideConfigKey("quarkus.micrometer.registry-enabled-default", "false") - .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) - .addClass(SignalFxRegistryProcessor.REGISTRY_CLASS)) - .setLogRecordPredicate(r -> "io.quarkus.micrometer.runtime.export.ConfigAdapter".equals(r.getLoggerName())) - .assertLogRecords(r -> Util.assertMessage(testedAttribute, r)) - .assertException(t -> Assertions.assertEquals(ValidationException.class.getName(), t.getClass().getName(), - "Unexpected exception in test: " + Util.stackToString(t))); - - @Test - public void testMeterRegistryPresent() { - Assertions.fail("Runtime should not have initialized with malformed " + testedAttribute); - } -} diff --git a/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/SignalFxEnabledTest.java b/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/SignalFxEnabledTest.java deleted file mode 100644 index 82472d628bf69..0000000000000 --- a/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/SignalFxEnabledTest.java +++ /dev/null @@ -1,43 +0,0 @@ -package io.quarkus.micrometer.deployment.export; - -import java.util.Set; - -import javax.inject.Inject; - -import org.jboss.shrinkwrap.api.ShrinkWrap; -import org.jboss.shrinkwrap.api.spec.JavaArchive; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; - -import io.micrometer.core.instrument.MeterRegistry; -import io.micrometer.core.instrument.composite.CompositeMeterRegistry; -import io.quarkus.micrometer.runtime.MicrometerRecorder; -import io.quarkus.test.QuarkusUnitTest; - -public class SignalFxEnabledTest { - static final String REGISTRY_CLASS_NAME = "io.micrometer.signalfx.SignalFxMeterRegistry"; - static final Class REGISTRY_CLASS = MicrometerRecorder.getClassForName(REGISTRY_CLASS_NAME); - - @RegisterExtension - static final QuarkusUnitTest config = new QuarkusUnitTest() - .withConfigurationResource("test-logging.properties") - .overrideConfigKey("quarkus.micrometer.binder-enabled-default", "false") - .overrideConfigKey("quarkus.micrometer.export.signalfx.enabled", "true") - .overrideConfigKey("quarkus.micrometer.export.signalfx.access-token", "required") - .overrideConfigKey("quarkus.micrometer.registry-enabled-default", "false") - .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) - .addClass(SignalFxRegistryProcessor.REGISTRY_CLASS)); - - @Inject - MeterRegistry registry; - - @Test - public void testMeterRegistryPresent() { - // SignalFx is enabled (alone, all others disabled) - Assertions.assertNotNull(registry, "A registry should be configured"); - Set subRegistries = ((CompositeMeterRegistry) registry).getRegistries(); - Assertions.assertEquals(REGISTRY_CLASS, subRegistries.iterator().next().getClass(), - "Should be SignalFxMeterRegistry"); - } -} diff --git a/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/StackdriverEnabledInvalidTest.java b/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/StackdriverEnabledInvalidTest.java deleted file mode 100644 index d05b33c12aafe..0000000000000 --- a/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/StackdriverEnabledInvalidTest.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.quarkus.micrometer.deployment.export; - -import org.jboss.shrinkwrap.api.ShrinkWrap; -import org.jboss.shrinkwrap.api.spec.JavaArchive; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; - -import io.micrometer.core.instrument.config.validate.ValidationException; -import io.quarkus.test.QuarkusUnitTest; - -public class StackdriverEnabledInvalidTest { - final static String testedAttribute = "quarkus.micrometer.export.stackdriver.project-id"; - - @RegisterExtension - static final QuarkusUnitTest config = new QuarkusUnitTest() - .withConfigurationResource("test-logging.properties") - .overrideConfigKey("quarkus.micrometer.binder-enabled-default", "false") - .overrideConfigKey("quarkus.micrometer.export.stackdriver.enabled", "true") - .overrideConfigKey("quarkus.micrometer.registry-enabled-default", "false") - .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) - .addClass(StackdriverRegistryProcessor.REGISTRY_CLASS)) - .setLogRecordPredicate(r -> "io.quarkus.micrometer.runtime.export.ConfigAdapter".equals(r.getLoggerName())) - .assertLogRecords(r -> Util.assertMessage(testedAttribute, r)) - .assertException(t -> Assertions.assertEquals(ValidationException.class.getName(), t.getClass().getName(), - "Unexpected exception in test: " + Util.stackToString(t))); - - @Test - public void testMeterRegistryPresent() { - Assertions.fail("Runtime should not have initialized with missing " + testedAttribute); - } -} diff --git a/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/StackdriverEnabledTest.java b/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/StackdriverEnabledTest.java deleted file mode 100644 index 9bec78f914531..0000000000000 --- a/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/StackdriverEnabledTest.java +++ /dev/null @@ -1,44 +0,0 @@ -package io.quarkus.micrometer.deployment.export; - -import java.util.Set; - -import javax.inject.Inject; - -import org.jboss.shrinkwrap.api.ShrinkWrap; -import org.jboss.shrinkwrap.api.spec.JavaArchive; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; - -import io.micrometer.core.instrument.MeterRegistry; -import io.micrometer.core.instrument.composite.CompositeMeterRegistry; -import io.quarkus.micrometer.runtime.MicrometerRecorder; -import io.quarkus.test.QuarkusUnitTest; - -public class StackdriverEnabledTest { - static final String REGISTRY_CLASS_NAME = "io.micrometer.stackdriver.StackdriverMeterRegistry"; - static final Class REGISTRY_CLASS = MicrometerRecorder.getClassForName(REGISTRY_CLASS_NAME); - - @RegisterExtension - static final QuarkusUnitTest config = new QuarkusUnitTest() - .withConfigurationResource("test-logging.properties") - .overrideConfigKey("quarkus.micrometer.binder-enabled-default", "false") - .overrideConfigKey("quarkus.micrometer.export.stackdriver.enabled", "true") - .overrideConfigKey("quarkus.micrometer.export.stackdriver.publish", "false") - .overrideConfigKey("quarkus.micrometer.export.stackdriver.project-id", "required") - .overrideConfigKey("quarkus.micrometer.registry-enabled-default", "false") - .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) - .addClass(StackdriverRegistryProcessor.REGISTRY_CLASS)); - - @Inject - MeterRegistry registry; - - @Test - public void testMeterRegistryPresent() { - // Stackdriver is enabled (alone, all others disabled) - Assertions.assertNotNull(registry, "A registry should be configured"); - Set subRegistries = ((CompositeMeterRegistry) registry).getRegistries(); - Assertions.assertEquals(REGISTRY_CLASS, subRegistries.iterator().next().getClass(), - "Should be StackdriverMeterRegistry"); - } -} diff --git a/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/StatsdEnabledTest.java b/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/StatsdEnabledTest.java deleted file mode 100644 index b3d7727af741a..0000000000000 --- a/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/StatsdEnabledTest.java +++ /dev/null @@ -1,43 +0,0 @@ -package io.quarkus.micrometer.deployment.export; - -import java.util.Set; - -import javax.inject.Inject; - -import org.jboss.shrinkwrap.api.ShrinkWrap; -import org.jboss.shrinkwrap.api.spec.JavaArchive; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; - -import io.micrometer.core.instrument.MeterRegistry; -import io.micrometer.core.instrument.composite.CompositeMeterRegistry; -import io.quarkus.micrometer.runtime.MicrometerRecorder; -import io.quarkus.test.QuarkusUnitTest; - -public class StatsdEnabledTest { - static final String REGISTRY_CLASS_NAME = "io.micrometer.statsd.StatsdMeterRegistry"; - static final Class REGISTRY_CLASS = MicrometerRecorder.getClassForName(REGISTRY_CLASS_NAME); - - @RegisterExtension - static final QuarkusUnitTest config = new QuarkusUnitTest() - .withConfigurationResource("test-logging.properties") - .overrideConfigKey("quarkus.micrometer.binder-enabled-default", "false") - .overrideConfigKey("quarkus.micrometer.export.statsd.enabled", "true") - .overrideConfigKey("quarkus.micrometer.export.statsd.publish", "false") - .overrideConfigKey("quarkus.micrometer.registry-enabled-default", "false") - .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) - .addClass(StatsdRegistryProcessor.REGISTRY_CLASS)); - - @Inject - MeterRegistry registry; - - @Test - public void testMeterRegistryPresent() { - // Stackdriver is enabled (alone, all others disabled) - Assertions.assertNotNull(registry, "A registry should be configured"); - Set subRegistries = ((CompositeMeterRegistry) registry).getRegistries(); - Assertions.assertEquals(REGISTRY_CLASS, subRegistries.iterator().next().getClass(), - "Should be StatsdMeterRegistry"); - } -} diff --git a/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/Util.java b/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/test/Util.java similarity index 95% rename from extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/Util.java rename to extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/test/Util.java index f7f67ad613c6b..5d646d37c8b87 100644 --- a/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/deployment/export/Util.java +++ b/extensions/micrometer/deployment/src/test/java/io/quarkus/micrometer/test/Util.java @@ -1,4 +1,4 @@ -package io.quarkus.micrometer.deployment.export; +package io.quarkus.micrometer.test; import java.util.Arrays; import java.util.List; diff --git a/extensions/micrometer/runtime/pom.xml b/extensions/micrometer/runtime/pom.xml index a13f0636faae1..c9568c72527ca 100644 --- a/extensions/micrometer/runtime/pom.xml +++ b/extensions/micrometer/runtime/pom.xml @@ -44,41 +44,11 @@ - - io.micrometer - micrometer-registry-azure-monitor - true - - - io.micrometer - micrometer-registry-datadog - true - - - io.micrometer - micrometer-registry-jmx - true - io.micrometer micrometer-registry-prometheus true - - io.micrometer - micrometer-registry-signalfx - true - - - io.micrometer - micrometer-registry-stackdriver - true - - - io.micrometer - micrometer-registry-statsd - true - diff --git a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/config/AzureMonitorConfig.java b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/config/AzureMonitorConfig.java deleted file mode 100644 index 1f0edabb26475..0000000000000 --- a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/config/AzureMonitorConfig.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.quarkus.micrometer.runtime.config; - -import java.util.Optional; - -import io.quarkus.runtime.annotations.ConfigGroup; -import io.quarkus.runtime.annotations.ConfigItem; - -@ConfigGroup -public class AzureMonitorConfig implements MicrometerConfig.CapabilityEnabled { - /** - * Support for export to AzureMonitor. - *

- * Support for AzureMonitor will be enabled if Micrometer - * support is enabled, the AzureMonitorMeterRegistry is on the classpath - * and either this value is true, or this value is unset and - * {@code quarkus.micrometer.registry-enabled-default} is true. - */ - @ConfigItem - public Optional enabled; - - @Override - public Optional getEnabled() { - return enabled; - } - - @Override - public String toString() { - return this.getClass().getSimpleName() - + "{enabled=" + enabled - + '}'; - } -} diff --git a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/config/DatadogConfig.java b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/config/DatadogConfig.java deleted file mode 100644 index 7ebf8d7ac1092..0000000000000 --- a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/config/DatadogConfig.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.quarkus.micrometer.runtime.config; - -import java.util.Optional; - -import io.quarkus.runtime.annotations.ConfigGroup; -import io.quarkus.runtime.annotations.ConfigItem; - -@ConfigGroup -public class DatadogConfig implements MicrometerConfig.CapabilityEnabled { - /** - * Support for export to Datadog - *

- * Support for Datadog will be enabled if Micrometer - * support is enabled, the DatadogMeterRegistry is on the classpath - * and either this value is true, or this value is unset and - * {@code quarkus.micrometer.registry-enabled-default} is true. - */ - @ConfigItem - public Optional enabled; - - @Override - public Optional getEnabled() { - return enabled; - } - - @Override - public String toString() { - return this.getClass().getSimpleName() - + "{enabled=" + enabled - + '}'; - } -} diff --git a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/config/JmxConfig.java b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/config/JmxConfig.java deleted file mode 100644 index cf7f7e32edfde..0000000000000 --- a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/config/JmxConfig.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.quarkus.micrometer.runtime.config; - -import java.util.Optional; - -import io.quarkus.runtime.annotations.ConfigGroup; -import io.quarkus.runtime.annotations.ConfigItem; - -@ConfigGroup -public class JmxConfig implements MicrometerConfig.CapabilityEnabled { - /** - * Support for export to JMX - *

- * Support for JMX will be enabled if Micrometer - * support is enabled, the JmxMeterRegistry is on the classpath - * and either this value is true, or this value is unset and - * {@code quarkus.micrometer.registry-enabled-default} is true. - */ - @ConfigItem - public Optional enabled; - - @Override - public Optional getEnabled() { - return enabled; - } - - @Override - public String toString() { - return this.getClass().getSimpleName() - + "{enabled=" + enabled - + '}'; - } -} diff --git a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/config/MicrometerConfig.java b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/config/MicrometerConfig.java index b5e5df56359d0..d4537f08ae4f6 100644 --- a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/config/MicrometerConfig.java +++ b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/config/MicrometerConfig.java @@ -116,14 +116,8 @@ public static class BinderConfig { /** Build / static runtime config for exporters */ @ConfigGroup public static class ExportConfig { - public AzureMonitorConfig azuremonitor; - public DatadogConfig datadog; - public JmxConfig jmx; public JsonConfig json; public PrometheusConfig prometheus; - public SignalFxConfig signalfx; - public StackdriverConfig stackdriver; - public StatsdConfig statsd; } public static interface CapabilityEnabled { diff --git a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/config/SignalFxConfig.java b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/config/SignalFxConfig.java deleted file mode 100644 index cb48b9da76735..0000000000000 --- a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/config/SignalFxConfig.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.quarkus.micrometer.runtime.config; - -import java.util.Optional; - -import io.quarkus.runtime.annotations.ConfigGroup; -import io.quarkus.runtime.annotations.ConfigItem; - -@ConfigGroup -public class SignalFxConfig implements MicrometerConfig.CapabilityEnabled { - /** - * Support for export to SignalFx. - *

- * Support for SignalFx will be enabled if Micrometer - * support is enabled, the SignalFxMeterRegistry is on the classpath - * and either this value is true, or this value is unset and - * {@code quarkus.micrometer.registry-enabled-default} is true. - */ - @ConfigItem - public Optional enabled; - - @Override - public Optional getEnabled() { - return enabled; - } - - @Override - public String toString() { - return this.getClass().getSimpleName() - + "{enabled=" + enabled - + '}'; - } -} diff --git a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/config/StackdriverConfig.java b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/config/StackdriverConfig.java deleted file mode 100644 index cb9966980008e..0000000000000 --- a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/config/StackdriverConfig.java +++ /dev/null @@ -1,43 +0,0 @@ -package io.quarkus.micrometer.runtime.config; - -import java.util.Optional; - -import io.quarkus.runtime.annotations.ConfigGroup; -import io.quarkus.runtime.annotations.ConfigItem; - -@ConfigGroup -public class StackdriverConfig implements MicrometerConfig.CapabilityEnabled { - /** - * Support for export to Stackdriver. - * - * Support for Stackdriver will be enabled if Micrometer - * support is enabled, the StackdriverMeterRegistry is on the classpath - * and either this value is true, or this value is unset and - * `quarkus.micrometer.registry-enabled-default` is true. - * - * [NOTE] - * ==== - * Stackdriver libraries do not yet support running in native mode. - * The Stackdriver MeterRegistry will be automatically disabled - * for native builds. - * - * See https://github.com/grpc/grpc-java/issues/5460 - * ==== - * - * @asciidoclet - */ - @ConfigItem - public Optional enabled; - - @Override - public Optional getEnabled() { - return enabled; - } - - @Override - public String toString() { - return this.getClass().getSimpleName() - + "{enabled=" + enabled - + '}'; - } -} diff --git a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/config/StatsdConfig.java b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/config/StatsdConfig.java deleted file mode 100644 index 785397c7c9d2b..0000000000000 --- a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/config/StatsdConfig.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.quarkus.micrometer.runtime.config; - -import java.util.Optional; - -import io.quarkus.runtime.annotations.ConfigGroup; -import io.quarkus.runtime.annotations.ConfigItem; - -@ConfigGroup -public class StatsdConfig implements MicrometerConfig.CapabilityEnabled { - /** - * Support for export to StatsD. - *

- * Support for StatsD will be enabled if Micrometer - * support is enabled, the StatsFxMeterRegistry is on the classpath - * and either this value is true, or this value is unset and - * {@code quarkus.micrometer.registry-enabled-default} is true. - */ - @ConfigItem - public Optional enabled; - - @Override - public Optional getEnabled() { - return enabled; - } - - @Override - public String toString() { - return this.getClass().getSimpleName() - + "{enabled=" + enabled - + '}'; - } -} diff --git a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/config/runtime/ExportConfig.java b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/config/runtime/ExportConfig.java index d72c492ece830..376d7558a6659 100644 --- a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/config/runtime/ExportConfig.java +++ b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/config/runtime/ExportConfig.java @@ -12,78 +12,6 @@ @SuppressWarnings("unused") @ConfigRoot(name = "micrometer.export", phase = ConfigPhase.RUN_TIME) public class ExportConfig { - // @formatter:off - /** - * Azure Monitor registry configuration properties. - * - * A property source for configuration of the AzureMonitor MeterRegistry. - * - * Available values: - * - * [cols=2] - * !=== - * h!Property=Default - * h!Description - * - * !`instrumentation-key` - * !Define the instrumentation key used to push data to Azure Insights Monitor - * - * !=== - * - * Other Micrometer configuration attributes can also be specified. - * - * @asciidoclet - */ - // @formatter:on - @ConfigItem - Map azuremonitor; - - // @formatter:off - /** - * Datadog MeterRegistry configuration properties. - * - * A property source for configuration of the Datadog MeterRegistry to push - * metrics using the Datadog API, see https://micrometer.io/docs/registry/datadog. - * - * Available values: - * - * [cols=2] - * !=== - * h!Property=Default - * h!Description - * - * !`apiKey=YOUR_KEY` - * !Define the key used to push data using the Datadog API - * - * !`publish=true` - * !By default, gathered metrics will be published to Datadog when the MeterRegistry is enabled. - * Use this attribute to selectively disable publication of metrics in some environments. - * - * !`step=1m` - * !The interval at which metrics are sent to Datadog. The default is 1 minute. - * !=== - * - * Other Micrometer configuration attributes can also be specified. - * - * @asciidoclet - */ - // @formatter:on - @ConfigItem - Map datadog; - - // @formatter:off - /** - * JMX registry configuration properties. - * - * A property source for configuration of the JMX MeterRegistry, - * see https://micrometer.io/docs/registry/jmx. - * - * @asciidoclet - */ - // @formatter:on - @ConfigItem - Map jmx; - // @formatter:off /** * Prometheus registry configuration properties. @@ -96,124 +24,4 @@ public class ExportConfig { // @formatter:on @ConfigItem Map prometheus; - - // @formatter:off - /** - * SignalFx registry configuration properties. - * - * A property source for configuration of the SignalFx MeterRegistry, - * see https://micrometer.io/docs/registry/signalFx. - * - * Available values: - * - * [cols=2] - * !=== - * h!Property=Default - * h!Description - * - * !`access-token=MY_ACCESS_TOKEN` - * !Define the access token required to push data to SignalFx - * - * !`source=identifier` - * !Unique identifier for the app instance that is publishing metrics to SignalFx. - * Defaults to the local host name. - * - * !`uri=https://ingest.signalfx.com` - * !Define the the URI to ship metrics to. Use this attribute to specify - * the location of an internal proxy, if necessary. - * - * !`step=1m` - * !The interval at which metrics are sent to SignalFx Monitoring. The default is 1 minute. - * !=== - * - * Other Micrometer configuration attributes can also be specified. - * - * @asciidoclet - */ - // @formatter:on - @ConfigItem - Map signalfx; - - // @formatter:off - /** - * Stackdriver registry configuration properties. - * - * A property source for configuration of the Stackdriver MeterRegistry, - * see https://micrometer.io/docs/registry/stackdriver. - * - * Available values: - * - * [cols=2] - * !=== - * h!Property=Default - * h!Description - * - * !`project-id=MY_PROJECT_ID` - * !Define the project id used to push data to Stackdriver Monitoring - * - * !`publish=true` - * !By default, gathered metrics will be published to Stackdriver when the MeterRegistry is enabled. - * Use this attribute to selectively disable publication of metrics in some environments. - * - * !`step=1m` - * !The interval at which metrics are sent to Stackdriver Monitoring. The default is 1 minute. - * !=== - * - * Other Micrometer configuration attributes can also be specified. - * - * @asciidoclet - */ - // @formatter:on - @ConfigItem - Map stackdriver; - - // @formatter:off - /** - * StatsD registry configuration properties. - * - * A property source for configuration of the StatsD MeterRegistry, - * see https://micrometer.io/docs/registry/statsD. - * - * Available values: - * - * [cols=2] - * !=== - * h!Property=Default - * h!Description - * - * !`flavor=datadog` - * !Specify the flavor of the StatsD line protocol. The original StatsD line protocol - * specification is `etsy`. The default value is `datadog`. - * - * !`host=localhost` - * !The host name of the StatsD agent. - * - * !`maxPacketLength=1400` - * !Adjust the packet length to keep the payload within your network's MTU. - * - * !`port=8125` - * !The port of the StatsD agent`. - * - * !`protocol=UDP` - * !The protocol of the connection to the agent (UDP or TCP). - * - * !`publish=true` - * !By default, gathered metrics will be published to StatsD when the MeterRegistry is enabled. - * Use this attribute to selectively disable publication of metrics in some environments. - * - * !`step=1m` - * !The interval at which metrics are sent to StatsD Monitoring. The default is 1 minute. - * !=== - * - * Other Micrometer configuration attributes can also be specified. - * - * As mentioned in the Micrometer StatsD documentation, if you want to customize the metrics - * sink, do so by providing your own `StatsDMeterRegistry` instance using a CDI `@Produces` - * method. - * - * @asciidoclet - */ - // @formatter:on - @ConfigItem - Map statsd; } diff --git a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/export/AzureMonitorMeterRegistryProvider.java b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/export/AzureMonitorMeterRegistryProvider.java deleted file mode 100644 index b970963d4813d..0000000000000 --- a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/export/AzureMonitorMeterRegistryProvider.java +++ /dev/null @@ -1,57 +0,0 @@ -package io.quarkus.micrometer.runtime.export; - -import java.util.Map; - -import javax.enterprise.inject.Produces; -import javax.inject.Singleton; - -import org.eclipse.microprofile.config.Config; - -import io.micrometer.azuremonitor.AzureMonitorConfig; -import io.micrometer.azuremonitor.AzureMonitorMeterRegistry; -import io.micrometer.azuremonitor.AzureMonitorNamingConvention; -import io.micrometer.core.instrument.Clock; -import io.micrometer.core.instrument.config.MeterRegistryConfigValidator; -import io.micrometer.core.instrument.config.validate.Validated; -import io.micrometer.core.instrument.step.StepRegistryConfig; -import io.quarkus.arc.DefaultBean; - -@Singleton -public class AzureMonitorMeterRegistryProvider { - static final String PREFIX = "quarkus.micrometer.export.azuremonitor."; - - @Produces - @Singleton - @DefaultBean - public AzureMonitorConfig configure(Config config) { - final Map properties = ConfigAdapter.captureProperties(config, PREFIX); - - return ConfigAdapter.validate(new AzureMonitorConfig() { - @Override - public String get(String key) { - return properties.get(key); - } - - @Override - // Bug in micrometer: instrumentationKey is required - public Validated validate() { - return MeterRegistryConfigValidator.checkAll(this, - c -> StepRegistryConfig.validate(c), - MeterRegistryConfigValidator.checkRequired("instrumentationKey", - AzureMonitorConfig::instrumentationKey)); - } - }); - } - - @Produces - @DefaultBean - public AzureMonitorNamingConvention namingConvention() { - return new AzureMonitorNamingConvention(); - } - - @Produces - @Singleton - public AzureMonitorMeterRegistry registry(AzureMonitorConfig config, Clock clock) { - return new AzureMonitorMeterRegistry(config, clock); - } -} diff --git a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/export/DatadogMeterRegistryProvider.java b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/export/DatadogMeterRegistryProvider.java deleted file mode 100644 index 8be8095b128a4..0000000000000 --- a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/export/DatadogMeterRegistryProvider.java +++ /dev/null @@ -1,61 +0,0 @@ -package io.quarkus.micrometer.runtime.export; - -import java.util.Map; - -import javax.enterprise.inject.Produces; -import javax.inject.Singleton; - -import org.eclipse.microprofile.config.Config; -import org.jboss.logging.Logger; - -import io.micrometer.core.instrument.Clock; -import io.micrometer.datadog.DatadogConfig; -import io.micrometer.datadog.DatadogMeterRegistry; -import io.micrometer.datadog.DatadogNamingConvention; -import io.quarkus.arc.DefaultBean; - -@Singleton -public class DatadogMeterRegistryProvider { - private static final Logger log = Logger.getLogger(DatadogMeterRegistryProvider.class); - - static final String PREFIX = "quarkus.micrometer.export.datadog."; - static final String PUBLISH = "datadog.publish"; - static final String ENABLED = "datadog.enabled"; - - @Produces - @Singleton - @DefaultBean - public DatadogConfig configure(Config config) { - final Map properties = ConfigAdapter.captureProperties(config, PREFIX); - - // Special check: if publish is set, override the value of enabled - // Specifically, The datadog registry must be enabled for this - // Provider to even be present. If this instance (at runtime) wants - // to prevent metrics from being published, then it would set - // quarkus.micrometer.export.datadog.publish=false - if (properties.containsKey(PUBLISH)) { - properties.put(ENABLED, properties.get(PUBLISH)); - } - - return ConfigAdapter.validate(new DatadogConfig() { - @Override - public String get(String key) { - return properties.get(key); - } - }); - } - - @Produces - @DefaultBean - public DatadogNamingConvention namingConvention() { - return new DatadogNamingConvention(); - } - - @Produces - @Singleton - public DatadogMeterRegistry registry(DatadogConfig config, Clock clock) { - return DatadogMeterRegistry.builder(config) - .clock(clock) - .build(); - } -} diff --git a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/export/JmxMeterRegistryProvider.java b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/export/JmxMeterRegistryProvider.java deleted file mode 100644 index c4bc116b7f3cb..0000000000000 --- a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/export/JmxMeterRegistryProvider.java +++ /dev/null @@ -1,49 +0,0 @@ -package io.quarkus.micrometer.runtime.export; - -import java.util.Map; - -import javax.enterprise.inject.Produces; -import javax.inject.Singleton; - -import org.eclipse.microprofile.config.Config; -import org.jboss.logging.Logger; - -import io.micrometer.core.instrument.Clock; -import io.micrometer.core.instrument.util.HierarchicalNameMapper; -import io.micrometer.jmx.JmxConfig; -import io.micrometer.jmx.JmxMeterRegistry; -import io.quarkus.arc.DefaultBean; - -@Singleton -public class JmxMeterRegistryProvider { - private static final Logger log = Logger.getLogger(JmxMeterRegistryProvider.class); - - static final String PREFIX = "quarkus.micrometer.export.jmx."; - - @Produces - @Singleton - @DefaultBean - public HierarchicalNameMapper config() { - return HierarchicalNameMapper.DEFAULT; - } - - @Produces - @Singleton - @DefaultBean - public JmxConfig configure(Config config) { - final Map properties = ConfigAdapter.captureProperties(config, PREFIX); - - return ConfigAdapter.validate(new JmxConfig() { - @Override - public String get(String key) { - return properties.get(key); - } - }); - } - - @Produces - @Singleton - public JmxMeterRegistry registry(JmxConfig config, Clock clock, HierarchicalNameMapper nameMapper) { - return new JmxMeterRegistry(config, clock, nameMapper); - } -} diff --git a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/export/SignalFxMeterRegistryProvider.java b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/export/SignalFxMeterRegistryProvider.java deleted file mode 100644 index 2d708879df82b..0000000000000 --- a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/export/SignalFxMeterRegistryProvider.java +++ /dev/null @@ -1,50 +0,0 @@ -package io.quarkus.micrometer.runtime.export; - -import java.util.Map; - -import javax.enterprise.inject.Produces; -import javax.inject.Singleton; - -import org.eclipse.microprofile.config.Config; -import org.jboss.logging.Logger; - -import io.micrometer.core.instrument.Clock; -import io.micrometer.signalfx.SignalFxConfig; -import io.micrometer.signalfx.SignalFxMeterRegistry; -import io.micrometer.signalfx.SignalFxNamingConvention; -import io.quarkus.arc.DefaultBean; - -@Singleton -public class SignalFxMeterRegistryProvider { - private static final Logger log = Logger.getLogger(SignalFxMeterRegistryProvider.class); - - static final String PREFIX = "quarkus.micrometer.export.signalfx."; - static final String PUBLISH = "signalfx.publish"; - static final String ENABLED = "signalfx.enabled"; - - @Produces - @Singleton - @DefaultBean - public SignalFxConfig configure(Config config) { - final Map properties = ConfigAdapter.captureProperties(config, PREFIX); - - return ConfigAdapter.validate(new SignalFxConfig() { - @Override - public String get(String key) { - return properties.get(key); - } - }); - } - - @Produces - @DefaultBean - public SignalFxNamingConvention namingConvention() { - return new SignalFxNamingConvention(); - } - - @Produces - @Singleton - public SignalFxMeterRegistry registry(SignalFxConfig config, Clock clock) { - return new SignalFxMeterRegistry(config, clock); - } -} diff --git a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/export/StackdriverMeterRegistryProvider.java b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/export/StackdriverMeterRegistryProvider.java deleted file mode 100644 index 4864f11571365..0000000000000 --- a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/export/StackdriverMeterRegistryProvider.java +++ /dev/null @@ -1,59 +0,0 @@ -package io.quarkus.micrometer.runtime.export; - -import java.util.Map; - -import javax.enterprise.inject.Produces; -import javax.inject.Singleton; - -import org.eclipse.microprofile.config.Config; -import org.jboss.logging.Logger; - -import io.micrometer.core.instrument.Clock; -import io.micrometer.stackdriver.StackdriverConfig; -import io.micrometer.stackdriver.StackdriverMeterRegistry; -import io.micrometer.stackdriver.StackdriverNamingConvention; -import io.quarkus.arc.DefaultBean; - -@Singleton -public class StackdriverMeterRegistryProvider { - private static final Logger log = Logger.getLogger(StackdriverMeterRegistryProvider.class); - - static final String PREFIX = "quarkus.micrometer.export.stackdriver."; - static final String PUBLISH = "stackdriver.publish"; - static final String ENABLED = "stackdriver.enabled"; - - @Produces - @Singleton - @DefaultBean - public StackdriverConfig configure(Config config) throws Throwable { - final Map properties = ConfigAdapter.captureProperties(config, PREFIX); - - // Special check: if publish is set, override the value of enabled - // Specifically, the StackDriver registry must be enabled for this - // Provider to even be present. If this instance (at runtime) wants - // to prevent metrics from being published, then it would set - // quarkus.micrometer.export.stackdriver.publish=false - if (properties.containsKey(PUBLISH)) { - properties.put(ENABLED, properties.get(PUBLISH)); - } - - return ConfigAdapter.validate(new StackdriverConfig() { - @Override - public String get(String key) { - return properties.get(key); - } - }); - } - - @Produces - @DefaultBean - public StackdriverNamingConvention namingConvention() { - return new StackdriverNamingConvention(); - } - - @Produces - @Singleton - public StackdriverMeterRegistry registry(StackdriverConfig config, Clock clock) { - return StackdriverMeterRegistry.builder(config).clock(clock).build(); - } -} diff --git a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/export/StatsdMeterRegistryProvider.java b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/export/StatsdMeterRegistryProvider.java deleted file mode 100644 index 6b5b43ef42659..0000000000000 --- a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/export/StatsdMeterRegistryProvider.java +++ /dev/null @@ -1,52 +0,0 @@ -package io.quarkus.micrometer.runtime.export; - -import java.util.Map; - -import javax.enterprise.inject.Produces; -import javax.inject.Singleton; - -import org.eclipse.microprofile.config.Config; -import org.jboss.logging.Logger; - -import io.micrometer.core.instrument.Clock; -import io.micrometer.statsd.StatsdConfig; -import io.micrometer.statsd.StatsdMeterRegistry; -import io.quarkus.arc.DefaultBean; - -@Singleton -public class StatsdMeterRegistryProvider { - private static final Logger log = Logger.getLogger(StatsdMeterRegistryProvider.class); - - static final String PREFIX = "quarkus.micrometer.export.statsd."; - static final String PUBLISH = "statsd.publish"; - static final String ENABLED = "statsd.enabled"; - - @Produces - @Singleton - @DefaultBean - public StatsdConfig configure(Config config) { - final Map properties = ConfigAdapter.captureProperties(config, PREFIX); - - // Special check: if publish is set, override the value of enabled - // Specifically, the StatsD registry must be enabled for this - // Provider to even be present. If this instance (at runtime) wants - // to prevent metrics from being published, then it would set - // quarkus.micrometer.export.statsd.publish=false - if (properties.containsKey(PUBLISH)) { - properties.put(ENABLED, properties.get(PUBLISH)); - } - - return ConfigAdapter.validate(new StatsdConfig() { - @Override - public String get(String key) { - return properties.get(key); - } - }); - } - - @Produces - @Singleton - public StatsdMeterRegistry registry(StatsdConfig config, Clock clock) { - return new StatsdMeterRegistry(config, clock); - } -} diff --git a/integration-tests/micrometer-jmx/pom.xml b/integration-tests/micrometer-jmx/pom.xml deleted file mode 100644 index 3d7ed501331ae..0000000000000 --- a/integration-tests/micrometer-jmx/pom.xml +++ /dev/null @@ -1,158 +0,0 @@ - - - 4.0.0 - - - io.quarkus - quarkus-integration-tests-parent - 999-SNAPSHOT - - - quarkus-integration-test-micrometer-jmx - Quarkus - Integration Tests - Micrometer JMX - - - - io.quarkus - quarkus-micrometer - ${project.version} - - - - io.micrometer - micrometer-registry-jmx - - - - - io.quarkus - quarkus-vertx-web - - - - io.quarkus - quarkus-smallrye-reactive-messaging-kafka - - - - - io.quarkus - quarkus-junit5 - test - - - io.rest-assured - rest-assured - test - - - org.jboss.logging - commons-logging-jboss-logging - test - - - - - io.quarkus - quarkus-micrometer-deployment - ${project.version} - pom - test - - - * - * - - - - - io.quarkus - quarkus-vertx-web-deployment - ${project.version} - pom - test - - - * - * - - - - - io.quarkus - quarkus-smallrye-reactive-messaging-kafka-deployment - ${project.version} - pom - test - - - * - * - - - - - - - - - io.quarkus - quarkus-maven-plugin - - - - build - - - - - - - - - - native-image - - - native - - - - - native - - - - - - org.apache.maven.plugins - maven-surefire-plugin - - ${native.surefire.skip} - - - - - maven-failsafe-plugin - - - - integration-test - verify - - - - - ${project.build.directory}/${project.build.finalName}-runner - - - - - - - - - - - diff --git a/integration-tests/micrometer-jmx/src/main/java/io/quarkus/it/micrometer/jmx/CustomConfiguration.java b/integration-tests/micrometer-jmx/src/main/java/io/quarkus/it/micrometer/jmx/CustomConfiguration.java deleted file mode 100644 index 75eae4b27e261..0000000000000 --- a/integration-tests/micrometer-jmx/src/main/java/io/quarkus/it/micrometer/jmx/CustomConfiguration.java +++ /dev/null @@ -1,41 +0,0 @@ -package io.quarkus.it.micrometer.jmx; - -import java.util.Arrays; - -import javax.annotation.Priority; -import javax.enterprise.inject.Produces; -import javax.inject.Singleton; -import javax.interceptor.Interceptor; - -import io.micrometer.core.instrument.Tag; -import io.micrometer.core.instrument.config.MeterFilter; -import io.micrometer.jmx.JmxMeterRegistry; -import io.quarkus.micrometer.runtime.MeterFilterConstraint; - -@Singleton -@Priority(Interceptor.Priority.APPLICATION - 100) -public class CustomConfiguration { - - @Produces - @Singleton - @MeterFilterConstraint(applyTo = JmxMeterRegistry.class) - public MeterFilter configurePrometheusRegistries() { - return MeterFilter.commonTags(Arrays.asList( - Tag.of("registry", "jmx"))); - } - - @Produces - @Singleton - @MeterFilterConstraint(applyTo = CustomConfiguration.class) - public MeterFilter configureNonexistantRegistries() { - return MeterFilter.commonTags(Arrays.asList( - Tag.of("tag", "class-should-not-match"))); - } - - @Produces - @Singleton - public MeterFilter configureAllRegistries() { - return MeterFilter.commonTags(Arrays.asList( - Tag.of("env", "test"))); - } -} diff --git a/integration-tests/micrometer-jmx/src/main/java/io/quarkus/it/micrometer/jmx/MessageResource.java b/integration-tests/micrometer-jmx/src/main/java/io/quarkus/it/micrometer/jmx/MessageResource.java deleted file mode 100644 index 6ca2e4ed7df83..0000000000000 --- a/integration-tests/micrometer-jmx/src/main/java/io/quarkus/it/micrometer/jmx/MessageResource.java +++ /dev/null @@ -1,53 +0,0 @@ -package io.quarkus.it.micrometer.jmx; - -import java.lang.management.ManagementFactory; -import java.util.Set; - -import javax.management.*; - -import io.micrometer.core.instrument.MeterRegistry; -import io.micrometer.core.instrument.composite.CompositeMeterRegistry; -import io.quarkus.vertx.web.Param; -import io.quarkus.vertx.web.Route; -import io.quarkus.vertx.web.RouteBase; -import io.vertx.core.http.HttpMethod; - -@RouteBase(path = "/message") -public class MessageResource { - - private final MeterRegistry registry; - private final MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer(); - - public MessageResource(MeterRegistry registry) { - this.registry = registry; - } - - @Route(path = "ping", methods = HttpMethod.GET) - public String message() { - CompositeMeterRegistry compositeMeterRegistry = (CompositeMeterRegistry) registry; - Set subRegistries = compositeMeterRegistry.getRegistries(); - return subRegistries.iterator().next().getClass().getName(); - } - - @Route(path = "fail", methods = HttpMethod.GET) - public String fail() { - throw new RuntimeException("Failed on purpose"); - } - - @Route(path = "item/:id", methods = HttpMethod.GET) - public String item(@Param("id") String id) { - return "return message with id " + id; - } - - @Route(path = "mbeans", methods = HttpMethod.GET) - public String metrics() throws IntrospectionException, InstanceNotFoundException, ReflectionException { - Set mbeans = mBeanServer.queryNames(null, null); - StringBuilder sb = new StringBuilder(); - for (ObjectName mbean : mbeans) { - if (mbean.getCanonicalName().startsWith("metrics:name=http")) { - sb.append(mbean.getCanonicalName()).append("\n"); - } - } - return sb.toString(); - } -} diff --git a/integration-tests/micrometer-jmx/src/main/resources/application.properties b/integration-tests/micrometer-jmx/src/main/resources/application.properties deleted file mode 100644 index 53d0f3f1277dd..0000000000000 --- a/integration-tests/micrometer-jmx/src/main/resources/application.properties +++ /dev/null @@ -1,8 +0,0 @@ - -#quarkus.log.category."io.quarkus.micrometer".level=DEBUG -quarkus.log.category."io.quarkus.bootstrap".level=INFO -quarkus.log.category."io.quarkus.netty".level=INFO -quarkus.log.category."io.quarkus.resteasy.runtime".level=INFO - -quarkus.log.category."io.netty".level=INFO -quarkus.log.category."org.apache".level=INFO diff --git a/integration-tests/micrometer-jmx/src/test/java/io/quarkus/it/micrometer/jmx/JmxMetricsRegistryTest.java b/integration-tests/micrometer-jmx/src/test/java/io/quarkus/it/micrometer/jmx/JmxMetricsRegistryTest.java deleted file mode 100644 index 889f9950f20bf..0000000000000 --- a/integration-tests/micrometer-jmx/src/test/java/io/quarkus/it/micrometer/jmx/JmxMetricsRegistryTest.java +++ /dev/null @@ -1,89 +0,0 @@ -package io.quarkus.it.micrometer.jmx; - -import static io.restassured.RestAssured.given; -import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.CoreMatchers.not; - -import org.junit.jupiter.api.MethodOrderer; -import org.junit.jupiter.api.Order; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestMethodOrder; - -import io.quarkus.test.junit.QuarkusTest; - -/** - * Test functioning prometheus endpoint. - * Use test execution order to ensure one http server request measurement - * is present when the endpoint is scraped. - */ -@QuarkusTest -@TestMethodOrder(MethodOrderer.OrderAnnotation.class) -class JmxMetricsRegistryTest { - - @Test - @Order(1) - void testRegistryInjection() { - given() - .when().get("/message/ping") - .then() - .statusCode(200) - .body(containsString("io.micrometer.jmx.JmxMeterRegistry")); - } - - @Test - @Order(2) - void testUnknownUrl() { - given() - .when().get("/messsage/notfound") - .then() - .statusCode(404); - } - - @Test - @Order(3) - void testServerError() { - given() - .when().get("/message/fail") - .then() - .statusCode(500); - } - - @Test - @Order(4) - void testPathParameter() { - given() - .when().get("/message/item/123") - .then() - .statusCode(200); - } - - @Test - @Order(10) - void testJmxReporter() { - - given() - .when().get("/message/mbeans") - .then() - .log().all() - .statusCode(200) - - // JMX endpoint is returning a subset of mbean objects to inspect - // hierarchical naming means all tags are present: registry=jmx, and env=test - - // Generic connection statistic - .body(containsString("metrics:name=httpServerConnections.env.test.registry.jmx.statistic")) - - .body(containsString( - "metrics:name=httpServerRequests.env.test.method.GET.outcome.CLIENT_ERROR.registry.jmx.status.404.uri.NOT_FOUND")) - .body(containsString( - "metrics:name=httpServerRequests.env.test.method.GET.outcome.SERVER_ERROR.registry.jmx.status.500.uri./message/fail")) - - .body(containsString( - "metrics:name=httpServerRequests.env.test.method.GET.outcome.SUCCESS.registry.jmx.status.200.uri./message/ping")) - .body(containsString( - "metrics:name=httpServerRequests.env.test.method.GET.outcome.SUCCESS.registry.jmx.status.200.uri./message/item/{id}")) - - // this was defined by a tag to a non-matching registry, and should not be found - .body(not(containsString("class-should-not-match"))); - } -} diff --git a/integration-tests/micrometer-native/pom.xml b/integration-tests/micrometer-native/pom.xml deleted file mode 100644 index 8a5c465acf770..0000000000000 --- a/integration-tests/micrometer-native/pom.xml +++ /dev/null @@ -1,175 +0,0 @@ - - - 4.0.0 - - - io.quarkus - quarkus-integration-tests-parent - 999-SNAPSHOT - - - quarkus-integration-test-micrometer-native - Quarkus - Integration Tests - Micrometer Native - - - - io.quarkus - quarkus-micrometer - - - - - io.quarkus - quarkus-vertx-web - - - - io.micrometer - micrometer-registry-azure-monitor - - - io.micrometer - micrometer-registry-datadog - - - io.micrometer - micrometer-registry-jmx - - - io.micrometer - micrometer-registry-signalfx - - - io.micrometer - micrometer-registry-stackdriver - - - io.micrometer - micrometer-registry-statsd - - - - - io.quarkus - quarkus-junit5 - test - - - io.rest-assured - rest-assured - test - - - org.jboss.logging - commons-logging-jboss-logging - test - - - - - io.quarkus - quarkus-micrometer-deployment - ${project.version} - pom - test - - - * - * - - - - - io.quarkus - quarkus-vertx-web-deployment - ${project.version} - pom - test - - - * - * - - - - - - - - - io.quarkus - quarkus-maven-plugin - - - - build - - - - - - - - - - native-image - - - native - - - - - native - - - - - - org.apache.maven.plugins - maven-surefire-plugin - - ${native.surefire.skip} - - - - - maven-failsafe-plugin - - - - integration-test - verify - - - - - ${project.build.directory}/${project.build.finalName}-runner - - - - - - - - - io.quarkus - quarkus-maven-plugin - - - - native-image - - - true - - - - - - - - - diff --git a/integration-tests/micrometer-native/src/main/java/io/quarkus/it/micrometer/native_mode/MessageResource.java b/integration-tests/micrometer-native/src/main/java/io/quarkus/it/micrometer/native_mode/MessageResource.java deleted file mode 100644 index 3b6546095c0a0..0000000000000 --- a/integration-tests/micrometer-native/src/main/java/io/quarkus/it/micrometer/native_mode/MessageResource.java +++ /dev/null @@ -1,36 +0,0 @@ -package io.quarkus.it.micrometer.native_mode; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - -import io.micrometer.core.instrument.MeterRegistry; -import io.micrometer.core.instrument.composite.CompositeMeterRegistry; -import io.quarkus.vertx.web.Route; -import io.quarkus.vertx.web.RouteBase; -import io.vertx.core.http.HttpMethod; - -@RouteBase(path = "/message") -public class MessageResource { - - private final MeterRegistry registry; - - public MessageResource(MeterRegistry registry) { - this.registry = registry; - } - - @Route(path = "ping", methods = HttpMethod.GET) - public List message() { - CompositeMeterRegistry compositeMeterRegistry = (CompositeMeterRegistry) registry; - Set subRegistries = compositeMeterRegistry.getRegistries(); - - List names = new ArrayList(subRegistries.size()); - subRegistries.forEach(x -> names.add(x.getClass().getName())); - return names; - } - - @Route(path = "fail", methods = HttpMethod.GET) - public String fail() { - throw new RuntimeException("Failed on purpose"); - } -} diff --git a/integration-tests/micrometer-native/src/main/resources/application.properties b/integration-tests/micrometer-native/src/main/resources/application.properties deleted file mode 100644 index e5293fb5b7236..0000000000000 --- a/integration-tests/micrometer-native/src/main/resources/application.properties +++ /dev/null @@ -1,15 +0,0 @@ -#quarkus.log.category."io.quarkus.micrometer".level=DEBUG -quarkus.log.category."io.quarkus.bootstrap".level=INFO -quarkus.log.category."io.quarkus.netty".level=INFO -quarkus.log.category."io.quarkus.resteasy.runtime".level=INFO - -quarkus.log.category."io.netty".level=INFO -quarkus.log.category."org.apache".level=INFO - -deployment.env=test - -quarkus.micrometer.export.azuremonitor.instrumentation-key=dummy -quarkus.micrometer.export.datadog.apiKey=dummy -quarkus.micrometer.export.signalfx.access-token=dummy -quarkus.micrometer.export.stackdriver.project-id=dummy -quarkus.micrometer.export.statsd.publish=false \ No newline at end of file diff --git a/integration-tests/micrometer-native/src/test/java/io/quarkus/it/micrometer/native_mode/NativeMeterRegistriesIT.java b/integration-tests/micrometer-native/src/test/java/io/quarkus/it/micrometer/native_mode/NativeMeterRegistriesIT.java deleted file mode 100644 index e4b8dd9464572..0000000000000 --- a/integration-tests/micrometer-native/src/test/java/io/quarkus/it/micrometer/native_mode/NativeMeterRegistriesIT.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.quarkus.it.micrometer.native_mode; - -import static io.restassured.RestAssured.given; - -import java.util.List; - -import org.hamcrest.MatcherAssert; -import org.hamcrest.Matchers; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -import io.quarkus.test.junit.NativeImageTest; -import io.restassured.response.Response; - -@NativeImageTest -class NativeMeterRegistriesIT extends NativeMeterRegistriesTest { - - @Test - @Override - void testRegistryInjection() { - // We expect all 6 registries on the classpath to be initialized - Response response = given() - .when().get("/message/ping"); - - Assertions.assertEquals(200, response.statusCode()); - - List registries = response.jsonPath().getList("$"); - MatcherAssert.assertThat(registries, Matchers.containsInAnyOrder( - "io.micrometer.datadog.DatadogMeterRegistry")); - - MatcherAssert.assertThat(registries, Matchers.not(Matchers.containsInAnyOrder( - "io.micrometer.azuremonitor.AzureMonitorMeterRegistry", - "io.micrometer.jmx.JmxMeterRegistry", - "io.micrometer.signalfx.SignalFxMeterRegistry", - "io.micrometer.stackdriver.StackdriverMeterRegistry", - "io.micrometer.statsd.StatsdMeterRegistry"))); - } -} diff --git a/integration-tests/micrometer-native/src/test/java/io/quarkus/it/micrometer/native_mode/NativeMeterRegistriesTest.java b/integration-tests/micrometer-native/src/test/java/io/quarkus/it/micrometer/native_mode/NativeMeterRegistriesTest.java deleted file mode 100644 index 62cc447be68e6..0000000000000 --- a/integration-tests/micrometer-native/src/test/java/io/quarkus/it/micrometer/native_mode/NativeMeterRegistriesTest.java +++ /dev/null @@ -1,40 +0,0 @@ -package io.quarkus.it.micrometer.native_mode; - -import static io.restassured.RestAssured.given; - -import java.util.List; - -import org.hamcrest.MatcherAssert; -import org.hamcrest.Matchers; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -import io.quarkus.test.junit.QuarkusTest; -import io.restassured.response.Response; - -/** - * Test functioning MeterRegistries - * Use test execution order to ensure one http server request measurement - * is present when the endpoint is scraped. - */ -@QuarkusTest -class NativeMeterRegistriesTest { - - @Test - void testRegistryInjection() { - // We expect all 6 registries on the classpath to be initialized - Response response = given() - .when().get("/message/ping"); - - Assertions.assertEquals(200, response.statusCode()); - - List registries = response.jsonPath().getList("$"); - MatcherAssert.assertThat(registries, Matchers.containsInAnyOrder( - "io.micrometer.azuremonitor.AzureMonitorMeterRegistry", - "io.micrometer.datadog.DatadogMeterRegistry", - "io.micrometer.jmx.JmxMeterRegistry", - "io.micrometer.signalfx.SignalFxMeterRegistry", - "io.micrometer.stackdriver.StackdriverMeterRegistry", - "io.micrometer.statsd.StatsdMeterRegistry")); - } -} diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 34d99043a3815..110ecfa9d7bb5 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -143,9 +143,7 @@ ide-launcher elasticsearch-rest-client elasticsearch-rest-high-level-client - micrometer-jmx micrometer-mp-metrics - micrometer-native micrometer-prometheus logging-json jaxp From 9154e6ee8c5e35ab6f6553ea79e82573636fce90 Mon Sep 17 00:00:00 2001 From: Roberto Cortez Date: Tue, 8 Dec 2020 00:46:04 +0000 Subject: [PATCH 26/62] Support SmallRye Config 1.10.0. --- bom/application/pom.xml | 6 +++- integration-tests/smallrye-config/pom.xml | 29 +++++++++++++++++++ .../src/main/resources/application.properties | 3 ++ .../main/resources/config-common.properties | 1 + .../src/main/resources/config.properties | 1 + .../it/smallrye/config/ConfigLocationsIT.java | 7 +++++ .../smallrye/config/ConfigLocationsTest.java | 27 +++++++++++++++++ 7 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 integration-tests/smallrye-config/src/main/resources/config-common.properties create mode 100644 integration-tests/smallrye-config/src/main/resources/config.properties create mode 100644 integration-tests/smallrye-config/src/test/java/io/quarkus/it/smallrye/config/ConfigLocationsIT.java create mode 100644 integration-tests/smallrye-config/src/test/java/io/quarkus/it/smallrye/config/ConfigLocationsTest.java diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 8e4eee62d9cd4..c2c4e477cfd2a 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -38,7 +38,7 @@ 1.0.1 1.4.1 1.5.0 - 1.9.3 + 1.10.0 2.2.5 2.4.4 2.0.16 @@ -2779,6 +2779,10 @@ org.osgi org.osgi.annotation.versioning + + org.ow2.asm + asm + diff --git a/integration-tests/smallrye-config/pom.xml b/integration-tests/smallrye-config/pom.xml index af6c2fc34df6f..943b791c905fb 100644 --- a/integration-tests/smallrye-config/pom.xml +++ b/integration-tests/smallrye-config/pom.xml @@ -82,6 +82,15 @@ maven-compiler-plugin ${compiler-plugin.version} + + org.apache.maven.plugins + maven-surefire-plugin + + + common,test + + + @@ -95,6 +104,23 @@ + + org.codehaus.mojo + properties-maven-plugin + 1.0.0 + + + + set-system-properties + + + + common,prod + + + + + org.apache.maven.plugins maven-surefire-plugin @@ -111,6 +137,9 @@ verify + + common,prod + ${project.build.directory}/${project.build.finalName}-runner diff --git a/integration-tests/smallrye-config/src/main/resources/application.properties b/integration-tests/smallrye-config/src/main/resources/application.properties index 632d301281328..170574305b7f6 100644 --- a/integration-tests/smallrye-config/src/main/resources/application.properties +++ b/integration-tests/smallrye-config/src/main/resources/application.properties @@ -13,3 +13,6 @@ server.ssl.certificate=certificate # configurable source configurable.source.init=database + +# additional locations +smallrye.config.locations=config.properties diff --git a/integration-tests/smallrye-config/src/main/resources/config-common.properties b/integration-tests/smallrye-config/src/main/resources/config-common.properties new file mode 100644 index 0000000000000..acab6416575cd --- /dev/null +++ b/integration-tests/smallrye-config/src/main/resources/config-common.properties @@ -0,0 +1 @@ +config.properties.common=1234 diff --git a/integration-tests/smallrye-config/src/main/resources/config.properties b/integration-tests/smallrye-config/src/main/resources/config.properties new file mode 100644 index 0000000000000..fc031ecfd4767 --- /dev/null +++ b/integration-tests/smallrye-config/src/main/resources/config.properties @@ -0,0 +1 @@ +config.properties=1234 diff --git a/integration-tests/smallrye-config/src/test/java/io/quarkus/it/smallrye/config/ConfigLocationsIT.java b/integration-tests/smallrye-config/src/test/java/io/quarkus/it/smallrye/config/ConfigLocationsIT.java new file mode 100644 index 0000000000000..7c7ca4710e761 --- /dev/null +++ b/integration-tests/smallrye-config/src/test/java/io/quarkus/it/smallrye/config/ConfigLocationsIT.java @@ -0,0 +1,7 @@ +package io.quarkus.it.smallrye.config; + +import io.quarkus.test.junit.NativeImageTest; + +@NativeImageTest +public class ConfigLocationsIT extends ConfigLocationsTest { +} diff --git a/integration-tests/smallrye-config/src/test/java/io/quarkus/it/smallrye/config/ConfigLocationsTest.java b/integration-tests/smallrye-config/src/test/java/io/quarkus/it/smallrye/config/ConfigLocationsTest.java new file mode 100644 index 0000000000000..62fc7b5d97e93 --- /dev/null +++ b/integration-tests/smallrye-config/src/test/java/io/quarkus/it/smallrye/config/ConfigLocationsTest.java @@ -0,0 +1,27 @@ +package io.quarkus.it.smallrye.config; + +import static io.restassured.RestAssured.given; +import static javax.ws.rs.core.Response.Status.OK; +import static org.hamcrest.Matchers.equalTo; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +public class ConfigLocationsTest { + @Test + void locations() { + given() + .get("/config/{name}", "config.properties") + .then() + .statusCode(OK.getStatusCode()) + .body("value", equalTo("1234")); + + given() + .get("/config/{name}", "config.properties.common") + .then() + .statusCode(OK.getStatusCode()) + .body("value", equalTo("1234")); + } +} From be760d98c11c8ebf9a790a6ade5f9fdfe6b9fb12 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Wed, 9 Dec 2020 11:04:16 +0100 Subject: [PATCH 27/62] Provide M2Eclipse metadata to silence errors in Eclipse When importing a project. --- .../m2e/lifecycle-mapping-metadata.xml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 devtools/maven/src/main/resources/META-INF/m2e/lifecycle-mapping-metadata.xml diff --git a/devtools/maven/src/main/resources/META-INF/m2e/lifecycle-mapping-metadata.xml b/devtools/maven/src/main/resources/META-INF/m2e/lifecycle-mapping-metadata.xml new file mode 100644 index 0000000000000..d174ca348de41 --- /dev/null +++ b/devtools/maven/src/main/resources/META-INF/m2e/lifecycle-mapping-metadata.xml @@ -0,0 +1,18 @@ + + + + + + generate-code + generate-code-tests + + + + + false + true + + + + + \ No newline at end of file From d01d4d473b65be40d3f4bf0de614085aa658eef1 Mon Sep 17 00:00:00 2001 From: Saumya Singh Date: Wed, 9 Dec 2020 15:04:29 +0530 Subject: [PATCH 28/62] Added link to cache quickstart in cache guide --- docs/src/main/asciidoc/cache.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/main/asciidoc/cache.adoc b/docs/src/main/asciidoc/cache.adoc index b045196bffad6..eb2f3a443aab2 100644 --- a/docs/src/main/asciidoc/cache.adoc +++ b/docs/src/main/asciidoc/cache.adoc @@ -36,7 +36,7 @@ However, you can go right to the completed example. Clone the Git repository: `git clone {quickstarts-clone-url}`, or download an {quickstarts-archive-url}[archive]. -The solution is located in the `cache-quickstart` directory. +The solution is located in the `cache-quickstart` {quickstarts-tree-url}/cache-quickstart[directory]. == Creating the Maven project From 6d5ff7f5a0811ff0b3e379fbc60e56d24f82013f Mon Sep 17 00:00:00 2001 From: Saumya Singh Date: Wed, 9 Dec 2020 18:00:44 +0530 Subject: [PATCH 29/62] Added link of gettingstarted quickstart in Getting Started guide --- docs/src/main/asciidoc/getting-started.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/main/asciidoc/getting-started.adoc b/docs/src/main/asciidoc/getting-started.adoc index db45860e963ba..1b43c3e0537ee 100644 --- a/docs/src/main/asciidoc/getting-started.adoc +++ b/docs/src/main/asciidoc/getting-started.adoc @@ -67,7 +67,7 @@ Download an {quickstarts-archive-url}[archive] or clone the git repository: git clone {quickstarts-clone-url} ---- -The solution is located in the `getting-started` directory. +The solution is located in the `getting-started` {quickstarts-tree-url}/getting-started[directory]. == Bootstrapping the project From 1b601fc0dc4e17f2767f4c776fcaf27cc7529b1b Mon Sep 17 00:00:00 2001 From: Saumya Singh Date: Wed, 9 Dec 2020 18:25:56 +0530 Subject: [PATCH 30/62] Added link of gettingstarted testing quickstart in Getting Started Testing guide --- docs/src/main/asciidoc/getting-started-testing.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/main/asciidoc/getting-started-testing.adoc b/docs/src/main/asciidoc/getting-started-testing.adoc index b7bb7d7c1c41a..3849cc63c5a86 100644 --- a/docs/src/main/asciidoc/getting-started-testing.adoc +++ b/docs/src/main/asciidoc/getting-started-testing.adoc @@ -47,7 +47,7 @@ However, you can go right to the completed example. Clone the Git repository: `git clone {quickstarts-clone-url}`, or download an {quickstarts-archive-url}[archive]. -The solution is located in the `getting-started-testing` directory. +The solution is located in the `getting-started-testing` {quickstarts-tree-url}/getting-started-testing[directory]. This guide assumes you already have the completed application from the `getting-started` directory. From a97f37e7f0cac11616a2941ed12a20e5c85d543c Mon Sep 17 00:00:00 2001 From: Saumya Singh Date: Wed, 9 Dec 2020 18:43:40 +0530 Subject: [PATCH 31/62] Added link of JMS quickstart in JMS guide --- docs/src/main/asciidoc/jms.adoc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/src/main/asciidoc/jms.adoc b/docs/src/main/asciidoc/jms.adoc index b266d29ee91ad..6b37c75770932 100644 --- a/docs/src/main/asciidoc/jms.adoc +++ b/docs/src/main/asciidoc/jms.adoc @@ -53,6 +53,9 @@ However, you can go right to the completed example. Clone the Git repository: `git clone https://github.com/amqphub/quarkus-qpid-jms-quickstart.git`, or download an https://github.com/amqphub/quarkus-qpid-jms-quickstart/archive/master.zip[archive]. +The solution is located in the `jms-quickstart` {quickstarts-tree-url}/jms-quickstart[directory]. + + === Creating the Maven Project From 9eff64bdd5c133de686c13f3d5386d552ea64319 Mon Sep 17 00:00:00 2001 From: Alexey Loubyansky Date: Mon, 7 Dec 2020 15:07:22 +0100 Subject: [PATCH 32/62] Properly close the app launched in devmode from the IDE --- .../deployment/dev/IDEDevModeMain.java | 21 +++++++++-- .../RunningQuarkusApplicationImpl.java | 7 +++- .../io/quarkus/launcher/QuarkusLauncher.java | 7 ++-- .../main/java/io/quarkus/runtime/Quarkus.java | 19 +++++++++- .../io/quarkus/bootstrap/IDELauncherImpl.java | 37 ++++++++++++++----- 5 files changed, 72 insertions(+), 19 deletions(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/IDEDevModeMain.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/IDEDevModeMain.java index efb33adb0df30..7f7d583e86dd3 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/IDEDevModeMain.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/IDEDevModeMain.java @@ -1,5 +1,6 @@ package io.quarkus.deployment.dev; +import java.io.Closeable; import java.io.File; import java.nio.file.Path; import java.util.Collections; @@ -20,11 +21,12 @@ import io.quarkus.bootstrap.util.QuarkusModelHelper; import io.quarkus.bootstrap.utils.BuildToolHelper; -@SuppressWarnings("unused") -public class IDEDevModeMain implements BiConsumer> { +public class IDEDevModeMain implements BiConsumer>, Closeable { private static final Logger log = Logger.getLogger(IDEDevModeMain.class.getName()); + private IsolatedDevModeMain delegate; + @Override public void accept(CuratedApplication curatedApplication, Map stringObjectMap) { Path appClasses = (Path) stringObjectMap.get("app-classes"); @@ -58,10 +60,23 @@ public void accept(CuratedApplication curatedApplication, Map st log.error("Failed to load workspace, hot reload will not be available", e); } - new IsolatedDevModeMain().accept(curatedApplication, + terminateIfRunning(); + delegate = new IsolatedDevModeMain(); + delegate.accept(curatedApplication, Collections.singletonMap(DevModeContext.class.getName(), devModeContext)); } + @Override + public void close() { + terminateIfRunning(); + } + + private void terminateIfRunning() { + if (delegate != null) { + delegate.close(); + } + } + private DevModeContext.ModuleInfo toModule(WorkspaceModule module) throws BootstrapGradleException { AppArtifactKey key = new AppArtifactKey(module.getArtifactCoords().getGroupId(), module.getArtifactCoords().getArtifactId(), module.getArtifactCoords().getClassifier()); diff --git a/core/deployment/src/main/java/io/quarkus/runner/bootstrap/RunningQuarkusApplicationImpl.java b/core/deployment/src/main/java/io/quarkus/runner/bootstrap/RunningQuarkusApplicationImpl.java index 948d896301652..609c043335650 100644 --- a/core/deployment/src/main/java/io/quarkus/runner/bootstrap/RunningQuarkusApplicationImpl.java +++ b/core/deployment/src/main/java/io/quarkus/runner/bootstrap/RunningQuarkusApplicationImpl.java @@ -14,6 +14,8 @@ public class RunningQuarkusApplicationImpl implements RunningQuarkusApplication private final Closeable closeTask; private final ClassLoader classLoader; + private boolean closing; + public RunningQuarkusApplicationImpl(Closeable closeTask, ClassLoader classLoader) { this.closeTask = closeTask; this.classLoader = classLoader; @@ -26,7 +28,10 @@ public ClassLoader getClassLoader() { @Override public void close() throws Exception { - closeTask.close(); + if (!closing) { + closing = true; + closeTask.close(); + } } @Override diff --git a/core/launcher/src/main/java/io/quarkus/launcher/QuarkusLauncher.java b/core/launcher/src/main/java/io/quarkus/launcher/QuarkusLauncher.java index 629ed1cd60b41..7393aedcbfabf 100644 --- a/core/launcher/src/main/java/io/quarkus/launcher/QuarkusLauncher.java +++ b/core/launcher/src/main/java/io/quarkus/launcher/QuarkusLauncher.java @@ -1,5 +1,6 @@ package io.quarkus.launcher; +import java.io.Closeable; import java.net.JarURLConnection; import java.net.URI; import java.net.URL; @@ -22,7 +23,7 @@ */ public class QuarkusLauncher { - public static void launch(String callingClass, String quarkusApplication, String... args) { + public static Closeable launch(String callingClass, String quarkusApplication, String... args) { final ClassLoader originalCl = Thread.currentThread().getContextClassLoader(); try { String classResource = callingClass.replace(".", "/") + ".class"; @@ -52,8 +53,7 @@ public static void launch(String callingClass, String quarkusApplication, String Thread.currentThread().setContextClassLoader(loader); Class launcher = loader.loadClass("io.quarkus.bootstrap.IDELauncherImpl"); - launcher.getDeclaredMethod("launch", Path.class, Map.class).invoke(null, appClasses, context); - + return (Closeable) launcher.getDeclaredMethod("launch", Path.class, Map.class).invoke(null, appClasses, context); } catch (Exception e) { throw new RuntimeException(e); } finally { @@ -61,5 +61,4 @@ public static void launch(String callingClass, String quarkusApplication, String Thread.currentThread().setContextClassLoader(originalCl); } } - } diff --git a/core/runtime/src/main/java/io/quarkus/runtime/Quarkus.java b/core/runtime/src/main/java/io/quarkus/runtime/Quarkus.java index 10093ad759683..e6ecf8b5f4d11 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/Quarkus.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/Quarkus.java @@ -1,5 +1,7 @@ package io.quarkus.runtime; +import java.io.Closeable; +import java.io.IOException; import java.util.function.BiConsumer; import org.jboss.logging.Logger; @@ -24,6 +26,8 @@ public class Quarkus { //WARNING: this is too early to inject a logger //private static final Logger log = Logger.getLogger(Quarkus.class); + private static Closeable LAUNCHED_FROM_IDE; + /** * Runs a quarkus application, that will run until the provided {@link QuarkusApplication} has completed. * @@ -89,7 +93,18 @@ private static void launchFromIDE(Class quarkusApp pos++; } String callingClass = stackTrace[pos].getClassName(); - QuarkusLauncher.launch(callingClass, quarkusApplication == null ? null : quarkusApplication.getName(), args); + LAUNCHED_FROM_IDE = QuarkusLauncher.launch(callingClass, + quarkusApplication == null ? null : quarkusApplication.getName(), args); + } + + private static void terminateForIDE() { + if (LAUNCHED_FROM_IDE != null) { + try { + LAUNCHED_FROM_IDE.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } } /** @@ -120,6 +135,7 @@ public static void run(String... args) { * @param code The exit code. This may be overridden if an exception occurs on shutdown */ public static void asyncExit(int code) { + terminateForIDE(); ApplicationLifecycleManager.exit(code); } @@ -136,6 +152,7 @@ public static void asyncExit(int code) { * */ public static void asyncExit() { + terminateForIDE(); ApplicationLifecycleManager.exit(-1); } diff --git a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/IDELauncherImpl.java b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/IDELauncherImpl.java index 3bbce1bbf38f0..0b22fca86a096 100644 --- a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/IDELauncherImpl.java +++ b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/IDELauncherImpl.java @@ -7,6 +7,8 @@ import io.quarkus.bootstrap.resolver.model.WorkspaceModule; import io.quarkus.bootstrap.util.QuarkusModelHelper; import io.quarkus.bootstrap.utils.BuildToolHelper; +import java.io.Closeable; +import java.io.IOException; import java.nio.file.Path; import java.util.Map; @@ -16,11 +18,9 @@ * This is launched from the core/launcher module. To avoid any shading issues core/launcher unpacks all its dependencies * into the jar file, then uses a custom class loader load them. */ -public class IDELauncherImpl { +public class IDELauncherImpl implements Closeable { - public static void launch(Path projectRoot, Map context) { - - CuratedApplication app = null; + public static Closeable launch(Path projectRoot, Map context) { try { //todo : proper support for everything final QuarkusBootstrap.Builder builder = QuarkusBootstrap.builder() @@ -52,16 +52,33 @@ public static void launch(Path projectRoot, Map context) { } } - app = builder.build().bootstrap(); - - app.runInAugmentClassLoader("io.quarkus.deployment.dev.IDEDevModeMain", context); + final CuratedApplication curatedApp = builder.build().bootstrap(); + final Object appInstance = curatedApp.runInAugmentClassLoader("io.quarkus.deployment.dev.IDEDevModeMain", context); + return new IDELauncherImpl(curatedApp, + appInstance == null ? null : appInstance instanceof Closeable ? (Closeable) appInstance : null); } catch (Exception e) { throw new RuntimeException(e); + } + } + + private final CuratedApplication curatedApp; + private final Closeable runningApp; + + private IDELauncherImpl(CuratedApplication curatedApp, Closeable runningApp) { + this.curatedApp = curatedApp; + this.runningApp = runningApp; + } + + @Override + public void close() throws IOException { + try { + if (runningApp != null) { + runningApp.close(); + } } finally { - if (app != null) { - app.close(); + if (curatedApp != null) { + curatedApp.close(); } } } - } From 181f6fd8c1621cabedad191db294c1ad22d3d3ac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Dec 2020 16:38:49 +0000 Subject: [PATCH 33/62] Bump caffeine from 2.8.7 to 2.8.8 Bumps [caffeine](https://github.com/ben-manes/caffeine) from 2.8.7 to 2.8.8. - [Release notes](https://github.com/ben-manes/caffeine/releases) - [Commits](https://github.com/ben-manes/caffeine/compare/v2.8.7...v2.8.8) Signed-off-by: dependabot[bot] --- bom/application/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index c2c4e477cfd2a..06ec653f586c7 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -131,7 +131,7 @@ 2.3 11.0.4.Final 4.3.4.Final - 2.8.7 + 2.8.8 4.1.49.Final 1.0.3 3.4.1.Final From 2114fd7599a5b3e9ac581bff0780680442c8786c Mon Sep 17 00:00:00 2001 From: Martin Kouba Date: Fri, 4 Dec 2020 09:19:52 +0100 Subject: [PATCH 34/62] Quarkus ArC integration refactoring - modify SyntheticBeanBuildItem so that it can be used for "regular" synthetic beans, i.e. no recorder proxy is needed - introduce BeanDiscoveryFinishedBuildItem and SynthesisFinishedBuildItem - introduce the "CDI Integration Guide" and remove the relevant parts from the reference guide - deprecate some build items - code refactoring Co-authored-by: Chris Laprun Co-authored-by: Matej Novotny Co-authored-by: Guillaume Smet --- docs/src/main/asciidoc/cdi-integration.adoc | 532 ++++++++++++++++++ docs/src/main/asciidoc/cdi-reference.adoc | 388 +------------ docs/src/main/asciidoc/cdi.adoc | 1 + .../quarkus/arc/deployment/ArcProcessor.java | 54 +- .../deployment/BeanArchiveIndexBuildItem.java | 17 +- .../arc/deployment/BeanArchiveProcessor.java | 53 +- .../BeanDeploymentValidatorBuildItem.java | 8 +- .../BeanDiscoveryFinishedBuildItem.java | 21 + .../deployment/BeanRegistrarBuildItem.java | 5 + .../BeanRegistrationPhaseBuildItem.java | 7 + .../arc/deployment/ConfigBuildStep.java | 17 +- .../deployment/ContextRegistrarBuildItem.java | 5 + .../ContextRegistrationPhaseBuildItem.java | 1 + .../CustomScopeAnnotationsBuildItem.java | 2 +- .../arc/deployment/CustomScopeBuildItem.java | 26 + .../ObserverRegistrarBuildItem.java | 5 + .../RegisteredComponentsBuildItem.java | 66 +++ .../SynthesisFinishedBuildItem.java | 21 + .../deployment/SyntheticBeanBuildItem.java | 63 ++- .../deployment/SyntheticBeansProcessor.java | 40 +- .../TransformedAnnotationsBuildItem.java | 13 +- .../MicroprofileMetricsProcessor.java | 3 +- .../qute/deployment/QuteProcessor.java | 12 +- .../deployment/SchedulerProcessor.java | 25 +- .../deployment/UndertowBuildStep.java | 23 +- .../arc/processor/BeanConfiguratorBase.java | 11 + .../quarkus/arc/processor/BeanDeployment.java | 35 +- .../processor/BeanDeploymentValidator.java | 5 + .../io/quarkus/arc/processor/BeanInfo.java | 19 +- .../quarkus/arc/processor/BeanProcessor.java | 17 +- .../quarkus/arc/processor/BeanRegistrar.java | 2 +- .../quarkus/arc/processor/BeanResolver.java | 315 +---------- .../arc/processor/BeanResolverImpl.java | 352 ++++++++++++ .../io/quarkus/arc/processor/BeanStream.java | 12 + .../java/io/quarkus/arc/processor/Beans.java | 16 +- .../quarkus/arc/processor/BuildExtension.java | 2 +- .../arc/processor/InterceptorResolver.java | 2 +- .../beans/BeanRegistrarTest.java | 9 + 38 files changed, 1351 insertions(+), 854 deletions(-) create mode 100644 docs/src/main/asciidoc/cdi-integration.adoc create mode 100644 extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanDiscoveryFinishedBuildItem.java create mode 100644 extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/CustomScopeBuildItem.java create mode 100644 extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/RegisteredComponentsBuildItem.java create mode 100644 extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/SynthesisFinishedBuildItem.java create mode 100644 independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanResolverImpl.java diff --git a/docs/src/main/asciidoc/cdi-integration.adoc b/docs/src/main/asciidoc/cdi-integration.adoc new file mode 100644 index 0000000000000..202b0fad04b74 --- /dev/null +++ b/docs/src/main/asciidoc/cdi-integration.adoc @@ -0,0 +1,532 @@ +//// +This guide is maintained in the main Quarkus repository +and pull requests should be submitted there: +https://github.com/quarkusio/quarkus/tree/master/docs/src/main/asciidoc +//// += Quarkus - CDI Integration Guide + +include::./attributes.adoc[] +:numbered: +:toc: +:toclevels: 2 + +ArC, the CDI container, is bootstrapped at build time. +The downside of this approach is that CDI Portable Extensions cannot be supported. +Nevertheless, the functionality can be achieved using the Quarkus-specific extensions API. + +The container is bootstrapped in multiple phases. +From a high level perspective these phases go as follows: + +1. Initialization +2. Bean discovery +3. Registration of synthetic components +4. Validation + +In the _initialization_ phase the preparatory work is being carried out and custom contexts are registered. +_Bean discovery_ is then the process where the container analyzes all application classes, identifies beans and wires them all together based on the provided metadata. +Subsequently, the extensions can register _synthetic components_. +Attributes of these components are fully controlled by the extensions, i.e. are not derived from an existing class. +Finally, the _deployment is validated_. +For example, the container validates every injection point in the application and fails the build if there is no bean that satisfies the given required type and qualifiers. + +TIP: You can see more information about the bootstrap by enabling additional logging. Simply run the Maven build with `-X` or `--debug` and grep the lines that contain `io.quarkus.arc`. In the <>, you can use `quarkus.log.category."io.quarkus.arc.processor".level=DEBUG` and two special endpoints are also registered automatically to provide some basic debug info in the JSON format. + +Quarkus build steps can produce and consume various build items and hook into each phase. +In the following sections we will describe all the relevant build items and common scenarios. + +== Metadata Sources + +Classes and annotations are the primary source of bean-level metadata. +The initial metadata are read from the _bean archive index_, an immutable https://github.com/wildfly/jandex[Jandex index, window="_blank"] which is built from various sources during <>. +However, extensions can add, remove or transform the metadata at certain stages of the bootstrap. +Moreover, extensions can also register <>. +This is an important aspect to realize when integrating CDI components in Quarkus. + +This way, extensions can turn classes, that would be otherwise ignored, into beans and vice versa. +For example, a class that declares a `@Scheduled` method is always registered as a bean even if it is not annotated with a bean defining annotation and would be normally ignored. + +:sectnums: +:sectnumlevels: 4 + +== Use Case - My Class Is Not Recognized as a Bean + +An `UnsatisfiedResolutionException` indicates a problem during <>. +Sometimes an injection point cannot be satisfied even if there is a class on the classpath that appears to be eligible for injection. +There are several reasons why a class is not recognized and also several ways to fix it. +In the first step we should identify the _reason_. + +[[additional_bean_build_item]] +=== _Reason 1_: Class Is Not discovered + +Quarkus has a <>. +It might happen that the class is not part of the application index. +For example, classes from the _runtime module_ of a Quarkus extension are not indexed automatically. + +_Solution_: Use the `AdditionalBeanBuildItem`. +This build item can be used to specify one or more additional classes to be analyzed during the discovery. +Additional bean classes are transparently added to the application index processed by the container. + +.`AdditionalBeanBuildItem` Example +[source,java] +---- +@BuildStep +AdditionalBeanBuildItem additionalBeans() { + return new AdditionalBeanBuildItem(SmallRyeHealthReporter.class, HealthServlet.class)); <1> +} +---- +<1> `AdditionalBeanBuildItem.Builder` can be used for more complex use cases. + +Bean classes added via `AdditionalBeanBuildItem` are _removable_ by default. +If the container considers them <>, they are just ignored. +However, you can use `AdditionalBeanBuildItem.Builder.setUnremovable()` method to instruct the container to never remove bean classes registered via this build item. +See also <> and <> for more details. + +It is aso possible to set the default scope via `AdditionalBeanBuildItem.Builder#setDefaultScope()`. +The default scope is only used if there is no scope declared on the bean class. + +NOTE: If no default scope is specified the `@Dependent` pseudo-scope is used. + +=== _Reason 2_: Class Is Discovered but Has No Bean Defining Annotation + +In Quarkus, the application is represented by a single bean archive with the https://docs.jboss.org/cdi/spec/2.0/cdi-spec.html#default_bean_discovery[bean discovery mode `annotated`, window="_blank"]. +Therefore, bean classes that don't have a https://docs.jboss.org/cdi/spec/2.0/cdi-spec.html#bean_defining_annotations[bean defining annotation, window="_blank"] are ignored. +Bean defining annotations are declared on the class-level and incluce scopes, stereotypes and `@Interceptor`. + +_Solution 1_: Use the `AutoAddScopeBuildItem`. This build item can be used to add a scope to a class that meets certain conditions. + +.`AutoAddScopeBuildItem` Example +[source,java] +---- +@BuildStep +AutoAddScopeBuildItem autoAddScope() { + return AutoAddScopeBuildItem.builder().containsAnnotations(SCHEDULED_NAME, SCHEDULES_NAME) <1> + .defaultScope(BuiltinScope.SINGLETON) <2> + .build(); +} +---- +<1> Find all classes annotated with `@Scheduled`. +<2> Add `@Singleton` as default scope. Classes already annotated with a scope are skipped automatically. + +_Solution 2_: If you need to process classes annotated with a specific annotation then it's possible to extend the set of bean defining annotations via the `BeanDefiningAnnotationBuildItem`. + +.`BeanDefiningAnnotationBuildItem` Example +[source,java] +---- +@BuildStep +BeanDefiningAnnotationBuildItem additionalBeanDefiningAnnotation() { + return new BeanDefiningAnnotationBuildItem(Annotations.GRAPHQL_API); <1> +} +---- +<1> Add `org.eclipse.microprofile.graphql.GraphQLApi` to the set of bean defining annotations. + +Bean classes added via `BeanDefiningAnnotationBuildItem` are _not removable_ by default, i.e. the resulting beans must not be removed even if they are considered unused. +However, you can change the default behavior. +See also <> and <> for more details. + +It is also possible to specify the default scope. +The default scope is only used if there is no scope declared on the bean class. + +NOTE: If no default scope is specified the `@Dependent` pseudo-scope is used. + +[[unremovable_builditem]] +=== _Reason 3_: Class Was Discovered and Has a Bean Defining Annotation but Was Removed + +The container attempts to <> during the build by default. +This optimization allows for _framework-level dead code elimination_. +In few special cases, it's not possible to correctly identify an unused bean. +In particular, Quarkus is not able to detect the usage of the `CDI.current()` static method yet. +Extensions can eliminate possible false positives by producing an `UnremovableBeanBuildItem`. + +.`UnremovableBeanBuildItem` Example +[source,java] +---- +@BuildStep +UnremovableBeanBuildItem unremovableBeans() { + return UnremovableBeanBuildItem.targetWithAnnotation(STARTUP_NAME); <1> +} +---- +<1> Make all classes annotated with `@Startup` unremovable. + +== Use Case - My Annotation Is Not Recognized as a Qualifier or an Interceptor Binding + +It is likely that the annotation class is not part of the application index. +For example, classes from the _runtime module_ of a Quarkus extension are not indexed automatically. + +_Solution_: Use the `AdditionalBeanBuildItem` as described in <>. + +[[annotations_transformer_build_item]] +== Use Case - I Need To Transform Metadata + +In some cases, it's useful to be able to modify the metadata. +Quarkus provides a powerful alternative to https://docs.jboss.org/cdi/spec/2.0/cdi-spec.html#process_annotated_type[`javax.enterprise.inject.spi.ProcessAnnotatedType`, window="_blank"]. +With an `AnnotationsTransformerBuildItem` it's possible to override the annotations that exist on bean classes. + +For example, you might want to add an interceptor binding to a specific bean class. +Here is how to do it: + +.`AnnotationsTransformerBuildItem` Example +[source,java] +---- +@BuildStep +AnnotationsTransformerBuildItem transform() { + return new AnnotationsTransformerBuildItem(new AnnotationsTransformer() { + + public boolean appliesTo(org.jboss.jandex.AnnotationTarget.Kind kind) { + return kind == org.jboss.jandex.AnnotationTarget.Kind.CLASS; <1> + } + + public void transform(TransformationContext context) { + if (context.getTarget().asClass().name().toString().equals("org.acme.Bar")) { + context.transform().add(MyInterceptorBinding.class).done(); <2> + } + } + }); +} +---- +<1> The transformer is only applied to classes. +<2> If the class name equals to `org.acme.Bar` then add `@MyInterceptorBinding`. Don't forget to invoke `Transformation#done()`. + +NOTE: Keep in mind that annotation transformers must be produced _before_ the bean discovery starts. + +Build steps can query the transformed annotations for a given annotation target via the `TransformedAnnotationsBuildItem`. + +.`TransformedAnnotationsBuildItem` Example +[source,java] +---- +@BuildStep +void queryAnnotations(TransformedAnnotationsBuildItem transformedAnnotations, BuildProducer myBuildItem) { + ClassInfo myClazz = ...; + if (transformedAnnotations.getAnnotations(myClazz).isEmpty()) { <1> + myBuildItem.produce(new MyBuildItem()); + } +} +---- +<1> `TransformedAnnotationsBuildItem.getAnnotations()` will return a possibly transformed set of annotations. + +NOTE: There are other build items specialized in transformation: <> and <>. + +[[inspect_beans]] +== Use Case - Inspect Beans, Observers and Injection Points + +=== _Solution 1_: `BeanDiscoveryFinishedBuildItem` + +Consumers of `BeanDiscoveryFinishedBuildItem` can easily inspect all class-based beans, observers and injection points registered in the application. +However, synthetic beans and observers are _not included_ because this build item is produced _before_ the synthetic components are registered. + +Additionaly, the bean resolver returned from `BeanDiscoveryFinishedBuildItem#getBeanResolver()` can be used to apply the type-safe resolution rules, e.g. to find out whether there is a bean that would satisfy certain combination of required type and qualifiers. + +.`BeanDiscoveryFinishedBuildItem` Example +[source,java] +---- +@BuildStep +void doSomethingWithNamedBeans(BeanDiscoveryFinishedBuildItem beanDiscovery, BuildProducer namedBeans) { + List namedBeans = beanDiscovery.beanStream().withName().collect(toList())); <1> + namedBeans.produce(new NamedBeansBuildItem(namedBeans)); +} +---- +<1> The resulting list will not contain `@Named` synthetic beans. + +=== _Solution 2_: `SynthesisFinishedBuildItem` + +Consumers of `SynthesisFinishedBuildItem` can easily inspect all beans, observers and injection points registered in the application. Synthetic beans and observers are included because this build item is produced _after_ the synthetic components are registered. + +Additionaly, the bean resolver returned from `SynthesisFinishedBuildItem#getBeanResolver()` can be used to apply the type-safe resolution rules, e.g. to find out whether there is a bean that would satisfy certain combination of required type and qualifiers. + +.`SynthesisFinishedBuildItem` Example +[source,java] +---- +@BuildStep +void doSomethingWithNamedBeans(SynthesisFinishedBuildItem synthesisFinished, BuildProducer namedBeans) { + List namedBeans = synthesisFinished.beanStream().withName().collect(toList())); <1> + namedBeans.produce(new NamedBeansBuildItem(namedBeans)); +} +---- +<1> The resulting list will contain `@Named` synthetic beans. + +[[synthetic_beans]] +== Use Case - The Need for Synthetic Beans + +Sometimes it is practical to be able to register a _synthetic bean_. +Bean attributes of a synthetic bean are not derived from a Java class, method or field. +Instead, all the attributes are defined by an extension. +In regular CDI, this could be achieved using the https://docs.jboss.org/cdi/spec/2.0/cdi-spec.html#after_bean_discovery[`AfterBeanDiscovery.addBean()`, window="_blank"] methods. + +_Solution_: If you need to register a synthetic bean then use the `SyntheticBeanBuildItem`. + +.`SyntheticBeanBuildItem` Example 1 +[source,java] +---- +@BuildStep +SyntheticBeanBuildItem syntheticBean() { + return SyntheticBeanBuildItem.configure(String.class) + .qualifiers(new MyQualifierLiteral()) + .creator(mc -> mc.returnValue(mc.load("foo"))) <1> + .done(); +} +---- +<1> Generate the bytecode of the `javax.enterprise.context.spi.Contextual#create(CreationalContext)` implementation. + +The output of a bean configurator is recorded as bytecode. +Therefore, there are some limitations in how a synthetic bean instance is created at runtime. +You can: + +1. Generate the bytecode of the `Contextual#create(CreationalContext)` method directly via `ExtendedBeanConfigurator.creator(Consumer)`. +2. Pass a `io.quarkus.arc.BeanCreator` implementation class via `ExtendedBeanConfigurator#creator(Class>)`, and possibly specify some parameters via `ExtendedBeanConfigurator#param()`. +3. Produce the runtime instance through a proxy returned from a <> and set it via `ExtendedBeanConfigurator#runtimeValue(RuntimeValue)` or `ExtendedBeanConfigurator#supplier(Supplier)`. + +.`SyntheticBeanBuildItem` Example 2 +[source,java] +---- +@BuildStep +@Record(STATIC_INIT) <1> +SyntheticBeanBuildItem syntheticBean(TestRecorder recorder) { + return SyntheticBeanBuildItem.configure(Foo.class).scope(Singleton.class) + .runtimeValue(recorder.createFoo()) <2> + .done(); +} +---- +<1> By default, a synthetic bean is initialized during `STATIC_INIT`. +<2> The bean instance is supplied by a value returned from a recorder method. + +It is possible to mark a synthetic bean to be initialized during `RUNTIME_INIT`. +See the <> for more information about the difference between `STATIC_INIT` and `RUNTIME_INIT`. + +.`RUNTIME_INIT` `SyntheticBeanBuildItem` Example +[source,java] +---- +@BuildStep +@Record(RUNTIME_INIT) <1> +SyntheticBeanBuildItem syntheticBean(TestRecorder recorder) { + return SyntheticBeanBuildItem.configure(Foo.class).scope(Singleton.class) + .setRuntimeInit() <2> + .runtimeValue(recorder.createFoo()) + .done(); +} +---- +<1> The recorder must be executed in the `ExecutionTime.RUNTIME_INIT` phase. +<2> The bean instance is initialized during `RUNTIME_INIT`. + +[IMPORTANT] +==== +Synthetic beans initialized during `RUNTIME_INIT` must not be accessed during `STATIC_INIT`. `RUNTIME_INIT` build steps that access a runtime-init synthetic bean should consume the `SyntheticBeansRuntimeInitBuildItem`: + +[source,java] +---- +@BuildStep +@Record(RUNTIME_INIT) +@Consume(SyntheticBeansRuntimeInitBuildItem.class) <1> +void accessFoo(TestRecorder recorder) { + recorder.foo(); <2> +} +---- +<1> This build step must be executed after `syntheticBean()` completes. +<2> This recorder method results in an invocation upon the `Foo` bean instance and thus we need to make sure that the build step is executed after all synthetic beans are initialized. +==== + +NOTE: It is also possible to use the `BeanRegistrationPhaseBuildItem` to register a synthetic bean. However, we recommend extension authors to stick with `SyntheticBeanBuildItem` which is more idiomatic for Quarkus. + +[[synthetic_observers]] +== Use Case - Synthetic Observers + +Similar to <>, the attributes of a synthetic observer method are not derived from a Java method. Instead, all the attributes are defined by an extension. + +_Solution_: If you need to register a synthetic observer, use the `ObserverRegistrationPhaseBuildItem`. + +IMPORTANT: A build step that consumes the `ObserverRegistrationPhaseBuildItem` should always produce an `ObserverConfiguratorBuildItem` or at least inject a `BuildProducer` for this build item, otherwise it could be ignored or processed at the wrong time (e.g. after the correct CDI bootstrap phase). + +.`ObserverRegistrationPhaseBuildItem` Example +[source,java] +---- +@BuildStep +void syntheticObserver(ObserverRegistrationPhaseBuildItem observerRegistrationPhase, + BuildProducer myBuildItem, + BuildProducer observerConfigurators) { + observerConfigurators.produce(new ObserverConfiguratorBuildItem(observerRegistrationPhase.getContext().configure().observedType(String.class) + .notify(mc -> { + // do some gizmo bytecode generation... + }).done(); + myBuildItem.produce(new MyBuildItem()); +} +---- + +The output of a `ObserverConfigurator` is recorded as bytecode. +Therefore, there are some limitations in how a synthetic observer is invoked at runtime. +Currently, you must generate the bytecode of the method body directly. + +[[generated_beans]] +== Use Case - I Have a Generated Bean Class + +No problem. +You can generate the bytecode of a bean class manually and then all you need to do is to produce a `GeneratedBeanBuildItem` instead of `GeneratedClassBuildItem`. + +.`GeneratedBeanBuildItem` Example +[source,java] +---- +@BuildStep +void generatedBean(BuildProducer generatedBeans) { + ClassOutput beansClassOutput = new GeneratedBeanGizmoAdaptor(generatedBeans); <1> + ClassCreator beanClassCreator = ClassCreator.builder().classOutput(beansClassOutput) + .className("org.acme.MyBean") + .build(); + beanClassCreator.addAnnotation(Singleton.class); + beanClassCreator.close(); <2> +} +---- +<1> `io.quarkus.arc.deployment.GeneratedBeanGizmoAdaptor` makes it easy to produce ``GeneratedBeanBuildItem``s from Gizmo constructs. +<2> The resulting bean class is something like `public class @Singleton MyBean { }`. + +== Use Case - I Need to Validate the Deployment + +Sometimes extensions need to inspect the beans, observers and injection points, then perform additional validations and fail the build if something is wrong. + +_Solution_: If an extension needs to validate the deployment it should use the `ValidationPhaseBuildItem`. + +IMPORTANT: A build step that consumes the `ValidationPhaseBuildItem` should always produce a `ValidationErrorBuildItem` or at least inject a `BuildProducer` for this build item, otherwise it could be ignored or processed at the wrong time (e.g. after the correct CDI bootstrap phase). + +[source,java] +---- +@BuildStep +void validate(ValidationPhaseBuildItem validationPhase, + BuildProducer myBuildItem, + BuildProducer errors) { + if (someCondition) { + errors.produce(new ValidationErrorBuildItem(new IllegalStateException())); + myBuildItem.produce(new MyBuildItem()); + } +} +---- + +TIP: You can easily filter all registered beans via the convenient `BeanStream` returned from the `ValidationPhaseBuildItem.getContext().beans()` method. + +[[custom_context]] +== Use Case - Register a Custom CDI Context + +Sometimes extensions need to extend the set of built-in CDI contexts. + +_Solution_: If you need to register a custom context, use the `ContextRegistrationPhaseBuildItem`. + +IMPORTANT: A build step that consumes the `ContextRegistrationPhaseBuildItem` should always produce a `ContextConfiguratorBuildItem` or at least inject a `BuildProducer` for this build item, otherwise it could be ignored or processed at the wrong time (e.g. after the correct CDI bootstrap phase). + +`ContextRegistrationPhaseBuildItem` Example +[source,java] +---- +@BuildStep +ContextConfiguratorBuildItem registerContext(ContextRegistrationPhaseBuildItem phase) { + return new ContextConfiguratorBuildItem(phase.getContext().configure(TransactionScoped.class).normal().contextClass(TransactionContext.class)); +} +---- + +Additionally, each extension that registers a custom CDI context via `ContextRegistrationPhaseBuildItem` should also produce the `CustomScopeBuildItem` in order to contribute the custom scope annotation name to the set of bean defining annotations. + +`CustomScopeBuildItem` Example +[source,java] +---- +@BuildStep +CustomScopeBuildItem customScope() { + return new CustomScopeBuildItem(DotName.createSimple(TransactionScoped.class.getName())); +} +---- + +=== What if I Need to Know All the Scopes Used in the Application? + +_Solution_: You can inject the `CustomScopeAnnotationsBuildItem` in a build step and use the convenient methods such as `CustomScopeAnnotationsBuildItem.isScopeDeclaredOn()`. + +[[additional_interceptor_bindings]] +== Use Case - Additional Interceptor Bindings + +In rare cases it might be handy to programmatically register an existing annotation that is not annotated with `@javax.interceptor.InterceptorBinding` as an interceptor binding. +This is similar to what CDI achieves through `BeforeBeanDiscovery#addInterceptorBinding()`. +Though here we are going to use `InterceptorBindingRegistrarBuildItem` to get it done. + +.`InterceptorBindingRegistrarBuildItem` Example +[source,java] +---- +@BuildStep +InterceptorBindingRegistrarBuildItem addInterceptorBindings() { + return new InterceptorBindingRegistrarBuildItem(new InterceptorBindingRegistrar() { + @Override + public Map> registerAdditionalBindings() { <1> + return Collections.singletonMap(DotName.createSimple(NotAnInterceptorBinding.class.getName()), + Collections.emptySet()); + } + }); +} +---- + +[[injection_point_transformation]] +== Use Case - Injection Point Transformation + +Every now and then it is handy to be able to change the qualifiers of an injection point programmatically. +You can do just that with `InjectionPointTransformerBuildItem`. +The following sample shows how to apply transformation to injection points with type `Foo` that contain qualifier `MyQualifier`: + +.`InjectionPointTransformerBuildItem` Example +[source,java] +---- +@BuildStep +InjectionPointTransformerBuildItem transformer() { + return new InjectionPointTransformerBuildItem(new InjectionPointsTransformer() { + + public boolean appliesTo(Type requiredType) { + return requiredType.name().equals(DotName.createSimple(Foo.class.getName())); + } + + public void transform(TransformationContext context) { + if (context.getQualifiers().stream() + .anyMatch(a -> a.name().equals(DotName.createSimple(MyQualifier.class.getName())))) { + context.transform() + .removeAll() + .add(DotName.createSimple(MyOtherQualifier.class.getName())) + .done(); + } + } + }); +} +---- + +NOTE: In theory, you can use <> to achieve the same goal. However, there are few differences that make `InjectionPointsTransformer` more suitable for this particular task: (1) annotation transformers are applied to all classes during bean discovery, whereas `InjectionPointsTransformer` is only applied to discovered injection points after bean discovery; (2) with `InjectionPointsTransformer` you don't need to handle various types of injection points (field, parameters of initializer methods, etc.). + +== Use Case - Resource Annotations and Injection + +The `ResourceAnnotationBuildItem` can be used to specify resource annotations that make it possible to resolve non-CDI injection points, such as Jakarta EE resources. +An integrator must also provide a corresponding `io.quarkus.arc.ResourceReferenceProvider` service provider implementation. + +.`ResourceAnnotationBuildItem` Example +[source,java] +---- +@BuildStep +void setupResourceInjection(BuildProducer resourceAnnotations, BuildProducer resources) { + resources.produce(new GeneratedResourceBuildItem("META-INF/services/io.quarkus.arc.ResourceReferenceProvider", + MyResourceReferenceProvider.class.getName().getBytes())); + resourceAnnotations.produce(new ResourceAnnotationBuildItem(DotName.createSimple(MyAnnotation.class.getName()))); +} +---- + +[[build_metadata]] +== Available Build Time Metadata + +Any of the above extensions that operates with `BuildExtension.BuildContext` can leverage certain build time metadata that are generated during build. +The built-in keys located in `io.quarkus.arc.processor.BuildExtension.Key` are: + +ANNOTATION_STORE:: Contains an `AnnotationStore` that keeps information about all `AnnotationTarget` annotations after application of annotation transformers +INJECTION_POINTS:: `Collection` containing all injection points +BEANS:: `Collection` containing all beans +REMOVED_BEANS:: `Collection` containing all the removed beans; see <> for more information +OBSERVERS:: `Collection` containing all observers +SCOPES:: `Collection` containing all scopes, including custom ones +QUALIFIERS:: `Map` containing all qualifiers +INTERCEPTOR_BINDINGS:: `Map` containing all interceptor bindings +STEREOTYPES:: `Map` containing all stereotypes + +To get hold of these, simply query the extension context object for given key. +Note that these metadata are made available as build proceeds which means that extensions can only leverage metadata that were built before the extensions are invoked. +If your extension attempts to retrieve metadata that wasn't yet produced, `null` will be returned. +Here is a summary of which extensions can access which metadata: + +AnnotationsTransformer:: Shouldn't rely on any metadata as it could be used at any time in any phase of the bootstrap +ContextRegistrar:: Has access to `ANNOTATION_STORE`, `QUALIFIERS`, `INTERCEPTOR_BINDINGS`, `STEREOTYPES` +InjectionPointsTransformer:: Has access to `ANNOTATION_STORE`, `QUALIFIERS`, `INTERCEPTOR_BINDINGS`, `STEREOTYPES` +ObserverTransformer:: Has access to `ANNOTATION_STORE`, `QUALIFIERS`, `INTERCEPTOR_BINDINGS`, `STEREOTYPES` +BeanRegistrar:: Has access to `ANNOTATION_STORE`, `QUALIFIERS`, `INTERCEPTOR_BINDINGS`, `STEREOTYPES`, `BEANS` (class-based beans only), `OBSERVERS` (class-based observers only), `INJECTION_POINTS` +ObserverRegistrar:: Has access to `ANNOTATION_STORE`, `QUALIFIERS`, `INTERCEPTOR_BINDINGS`, `STEREOTYPES`, `BEANS`, `OBSERVERS` (class-based observers only), `INJECTION_POINTS` +BeanDeploymentValidator:: Has access to all build metadata diff --git a/docs/src/main/asciidoc/cdi-reference.adoc b/docs/src/main/asciidoc/cdi-reference.adoc index dded084a94a99..e0669b67c423b 100644 --- a/docs/src/main/asciidoc/cdi-reference.adoc +++ b/docs/src/main/asciidoc/cdi-reference.adoc @@ -11,7 +11,7 @@ include::./attributes.adoc[] :sectnumlevels: 4 :toc: -Quarkus DI solution is based on the http://docs.jboss.org/cdi/spec/2.0/cdi-spec.html[Contexts and Dependency Injection for Java 2.0, window="_blank"] specification. +Quarkus DI solution (also called ArC) is based on the http://docs.jboss.org/cdi/spec/2.0/cdi-spec.html[Contexts and Dependency Injection for Java 2.0, window="_blank"] specification. However, it is not a full CDI implementation verified by the TCK. Only a subset of the CDI features is implemented - see also <> and <>. @@ -268,6 +268,7 @@ public class PingResource { <2> Injection itself does not result in the instantiation of `CoolService`. A client proxy is injected. <3> The first invocation upon the injected proxy triggers the instantiation of `CoolService`. +[[startup_event]] ==== Startup Event However, if you really need to instantiate a bean eagerly you can: @@ -735,394 +736,13 @@ class SharedService { <3> You can also specify the "wait time". If it's not possible to acquire the lock in the given time a `LockException` is thrown. [[build_time_apis]] -== Build Time Extension Points - -[[portable_extensions]] -=== Portable Extensions +== Build Time Extensions Quarkus incorporates build-time optimizations in order to provide instant startup and low memory footprint. The downside of this approach is that CDI Portable Extensions cannot be supported. Nevertheless, most of the functionality can be achieved using Quarkus link:writing-extensions[extensions]. +See the <> for more information. -=== Additional Bean Defining Annotations - -As described in <> bean classes that don’t have a bean defining annotation are not discovered. -However, `BeanDefiningAnnotationBuildItem` can be used to extend the set of default bean defining annotations (`@Dependent`, `@Singleton`, `@ApplicationScoped`, `@RequestScoped` and `@Stereotype` annotations): - -[source,java] ----- -@BuildStep -BeanDefiningAnnotationBuildItem additionalBeanDefiningAnnotation() { - return new BeanDefiningAnnotationBuildItem(DotName.createSimple("javax.ws.rs.Path"))); -} ----- - -NOTE: Bean registrations that are result of a `BeanDefiningAnnotationBuildItem` are unremovable by default. See also <>. - -=== Resource Annotations - -`ResourceAnnotationBuildItem` is used to specify resource annotations that make it possible to resolve non-CDI injection points, such as Java EE resources. - -NOTE: An integrator must also provide a corresponding `io.quarkus.arc.ResourceReferenceProvider` implementation. - -[source,java] ----- -@BuildStep -void setupResourceInjection(BuildProducer resourceAnnotations, BuildProducer resources) { - resources.produce(new GeneratedResourceBuildItem("META-INF/services/io.quarkus.arc.ResourceReferenceProvider", - JPAResourceReferenceProvider.class.getName().getBytes())); - resourceAnnotations.produce(new ResourceAnnotationBuildItem(DotName.createSimple(PersistenceContext.class.getName()))); -} ----- - -[[additional_beans]] -=== Additional Beans - -`AdditionalBeanBuildItem` is used to specify additional bean classes to be analyzed during discovery. -Additional bean classes are transparently added to the application index processed by the container. - -[source,java] ----- -@BuildStep -List additionalBeans() { - return Arrays.asList( - new AdditionalBeanBuildItem(SmallRyeHealthReporter.class), - new AdditionalBeanBuildItem(HealthServlet.class)); -} ----- - -NOTE: A bean registration that is a result of an `AdditionalBeanBuildItem` is removable by default. See also <>. - -[[synthetic_beans]] -=== Synthetic Beans - -Sometimes it is very useful to be able to register a synthetic bean. -Bean attributes of a synthetic bean are not derived from a java class, method or field. -Instead, the attributes are specified by an extension. -In CDI, this could be achieved using the `AfterBeanDiscovery.addBean()` methods. -In Quarkus, there are three ways to register a synthetic bean. - -==== `BeanRegistrarBuildItem` - -A build step can produce a `BeanRegistrarBuildItem` and leverage the `io.quarkus.arc.processor.BeanConfigurator` API to build a synthetic bean definition. - -.`BeanRegistrarBuildItem` Example -[source,java] ----- -@BuildStep -BeanRegistrarBuildItem syntheticBean() { - return new BeanRegistrarBuildItem(new BeanRegistrar() { - - @Override - public void register(RegistrationContext registrationContext) { - registrationContext.configure(String.class).types(String.class).qualifiers(new MyQualifierLiteral()).creator(mc -> mc.returnValue(mc.load("foo"))).done(); - } - })); -} ----- - -NOTE: The output of a `BeanConfigurator` is recorded as bytecode. Therefore there are some limitations in how a synthetic bean instance is created. See also `BeanConfigurator.creator()` methods. - -TIP: You can easily filter all class-based beans via the convenient `BeanStream` returned from the `RegistrationContext.beans()` method. - -==== `BeanRegistrationPhaseBuildItem` - -If a build step *needs to produce other build items during the registration* it should use the `BeanRegistrationPhaseBuildItem`. -The reason is that injected objects are only valid during a `@BuildStep` method invocation. - -.`BeanRegistrationPhaseBuildItem` Example -[source,java] ----- -@BuildStep -void syntheticBean(BeanRegistrationPhaseBuildItem beanRegistrationPhase, - BuildProducer myBuildItem, - BuildProducer beanConfigurators) { - beanConfigurators.produce(new BeanConfiguratorBuildItem(beanRegistrationPhase.getContext().configure(String.class).types(String.class).qualifiers(new MyQualifierLiteral()).creator(mc -> mc.returnValue(mc.load("foo"))))); - myBuildItem.produce(new MyBuildItem()); -} ----- - -NOTE: See the `BeanRegistrationPhaseBuildItem` javadoc for more information. - -==== `SyntheticBeanBuildItem` - -Finally, a build step can produce a `SyntheticBeanBuildItem` to register a synthetic bean whose instance can be easily *produced through a <>*. -The extended `BeanConfigurator` accepts either a `io.quarkus.runtime.RuntimeValue` or a `java.util.function.Supplier`. - -.`SyntheticBeanBuildItem` Example -[source,java] ----- -@BuildStep -@Record(STATIC_INIT) <1> -SyntheticBeanBuildItem syntheticBean(TestRecorder recorder) { - return SyntheticBeanBuildItem.configure(Foo.class).scope(Singleton.class) - .runtimeValue(recorder.createFoo()) <2> - .done(); -} ----- -<1> By default, a synthetic bean is initialized during `STATIC_INIT`. -<2> The bean instance is supplied by a value returned from a recorder method. - -It is possible to mark a synthetic bean to be initialized during `RUNTIME_INIT`: - -.`RUNTIME_INIT` `SyntheticBeanBuildItem` Example -[source,java] ----- -@BuildStep -@Record(RUNTIME_INIT) <1> -SyntheticBeanBuildItem syntheticBean(TestRecorder recorder) { - return SyntheticBeanBuildItem.configure(Foo.class).scope(Singleton.class) - .setRuntimeInit() <2> - .runtimeValue(recorder.createFoo()) - .done(); -} ----- -<1> The recorder must be executed in the `ExecutionTime.RUNTIME_INIT` phase. -<2> The bean instance is initialized during `RUNTIME_INIT`. - -[IMPORTANT] -==== -Synthetic bean initialized during `RUNTIME_INIT` must not be accessed during `STATIC_INIT`. `RUNTIME_INIT` build steps that access a runtime-init synthetic bean should consume the `SyntheticBeansRuntimeInitBuildItem`: - -[source,java] ----- -@BuildStep -@Record(RUNTIME_INIT) -@Consume(SyntheticBeansRuntimeInitBuildItem.class) <1> -void accessFoo(TestRecorder recorder) { - recorder.foo(); <2> -} ----- -<1> This build step must be executed after `syntheticBean()` completes. -<2> This recorder method results in an invocation of the `Foo` bean instance. -==== - -=== Annotation Transformations - -A very common task is to override the annotations found on the bean classes. -For example you might want to add an interceptor binding to a specific bean class. -Here is how to do it - use the `AnnotationsTransformerBuildItem`: - -[source,java] ----- -@BuildStep -AnnotationsTransformerBuildItem transform() { - return new AnnotationsTransformerBuildItem(new AnnotationsTransformer() { - - public boolean appliesTo(org.jboss.jandex.AnnotationTarget.Kind kind) { - return kind == org.jboss.jandex.AnnotationTarget.Kind.CLASS; - } - - public void transform(TransformationContext context) { - if (context.getTarget().asClass().name().toString().equals("com.foo.Bar")) { - context.transform().add(MyInterceptorBinding.class).done(); - } - } - }); -} ----- - -=== Additional Interceptor Bindings - -In rare cases it might be handy to programmatically register an existing annotation as interceptor binding. -This is similar to what pure CDI achieves through `BeforeBeanDiscovery#addInterceptorBinding()`. -Though here we are going to use `InterceptorBindingRegistrarBuildItem` to get it done. -Note that you can register multiple annotations in one go: - -[source,java] ----- -@BuildStep -InterceptorBindingRegistrarBuildItem addInterceptorBindings() { - InterceptorBindingRegistrarBuildItem additionalBindingsRegistrar = new InterceptorBindingRegistrarBuildItem(new InterceptorBindingRegistrar() { - @Override - public Collection registerAdditionalBindings() { - Collection result = new HashSet<>(); - result.add(DotName.createSimple(MyAnnotation.class.getName())); - result.add(DotName.createSimple(MyOtherAnnotation.class.getName())); - return result; - } - }); - return additionalBindingsRegistrar; -} ----- - -=== Injection Point Transformation - -Every now and then it is handy to be able to change qualifiers of an injection point programmatically. -You can do just that with `InjectionPointTransformerBuildItem`. -The following sample shows how to apply transformation to injection points with type `Foo` that contain qualifier `MyQualifier`: - -[source,java] ----- -@BuildStep -InjectionPointTransformerBuildItem transformer() { - return new InjectionPointTransformerBuildItem(new InjectionPointsTransformer() { - - public boolean appliesTo(Type requiredType) { - return requiredType.name().equals(DotName.createSimple(Foo.class.getName())); - } - - public void transform(TransformationContext context) { - if (context.getQualifiers().stream() - .anyMatch(a -> a.name().equals(DotName.createSimple(MyQualifier.class.getName())))) { - context.transform() - .removeAll() - .add(DotName.createSimple(MyOtherQualifier.class.getName())) - .done(); - } - } - }); -} ----- - -=== Observer Transformation - -Any https://docs.jboss.org/cdi/spec/2.0/cdi-spec.html#observer_methods[observer method] definition can be vetoed or transformed using an `ObserverTransformerBuildItem`. -The attributes that can be transformed include: - -- https://docs.jboss.org/cdi/api/2.0/javax/enterprise/inject/spi/ObserverMethod.html#getObservedQualifiers--[qualifiers] -- https://docs.jboss.org/cdi/api/2.0/javax/enterprise/inject/spi/ObserverMethod.html#getReception--[reception] -- https://docs.jboss.org/cdi/api/2.0/javax/enterprise/inject/spi/ObserverMethod.html#getPriority--[priority] -- https://docs.jboss.org/cdi/api/2.0/javax/enterprise/inject/spi/ObserverMethod.html#getTransactionPhase--[transaction phase] -- https://docs.jboss.org/cdi/api/2.0/javax/enterprise/inject/spi/ObserverMethod.html#isAsync--[asynchronous] - -[source,java] ----- -@BuildStep -ObserverTransformerBuildItem transformer() { - return new ObserverTransformerBuildItem(new ObserverTransformer() { - - public boolean appliesTo(Type observedType, Set qualifiers) { - return observedType.name.equals(DotName.createSimple(MyEvent.class.getName())); - } - - public void transform(TransformationContext context) { - // Veto all observers of MyEvent - context.veto(); - } - }); -} ----- - -=== Bean Deployment Validation - -Once the bean deployment is ready an extension can perform additional validations and inspect the found beans, observers and injection points. -Register a `BeanDeploymentValidatorBuildItem`: - -[source,java] ----- -@BuildStep -BeanDeploymentValidatorBuildItem beanDeploymentValidator() { - return new BeanDeploymentValidatorBuildItem(new BeanDeploymentValidator() { - public void validate(ValidationContext validationContext) { - for (InjectionPointInfo injectionPoint : validationContext.get(Key.INJECTION_POINTS)) { - System.out.println("Injection point: " + injectionPoint); - } - } - }); -} ----- - -TIP: You can easily filter all registered beans via the convenient `BeanStream` returned from the `ValidationContext.beans()` method. - -If an extension needs to produce other build items during the "validation" phase it should use the `ValidationPhaseBuildItem` instead. -The reason is that injected objects are only valid during a `@BuildStep` method invocation. - -[source,java] ----- -@BuildStep -void validate(ValidationPhaseBuildItem validationPhase, - BuildProducer myBuildItem, - BuildProducer errors) { - if (someCondition) { - errors.produce(new ValidationErrorBuildItem(new IllegalStateException())); - myBuildItem.produce(new MyBuildItem()); - } -} ----- - -NOTE: See `ValidationPhaseBuildItem` javadoc for more information. - - -=== Custom Contexts - -An extension can register a custom `InjectableContext` implementation by means of a `ContextRegistrarBuildItem`: - -[source,java] ----- -@BuildStep -ContextRegistrarBuildItem customContext() { - return new ContextRegistrarBuildItem(new ContextRegistrar() { - public void register(RegistrationContext registrationContext) { - registrationContext.configure(CustomScoped.class).normal().contextClass(MyCustomContext.class).done(); - } - }); -} ----- - -If an extension needs to produce other build items during the "context registration" phase it should use the `ContextRegistrationPhaseBuildItem` instead. -The reason is that injected objects are only valid during a `@BuildStep` method invocation. - -[source,java] ----- -@BuildStep -void addContext(ContextRegistrationPhaseBuildItem contextRegistrationPhase, - BuildProducer myBuildItem, - BuildProducer contexts) { - contexts.produce(new ContextConfiguratorBuildItem(contextRegistrationPhase.getContext().configure(CustomScoped.class).normal().contextClass(MyCustomContext.class))); - myBuildItem.produce(new MyBuildItem()); -} ----- - -NOTE: See `ContextRegistrationPhaseBuildItem` javadoc for more information. - - -[[build_metadata]] -=== Available Build Time Metadata - -Any of the above extensions that operates with `BuildExtension.BuildContext` can leverage certain build time metadata that are generated during build. -The built-in keys located in `io.quarkus.arc.processor.BuildExtension.Key` are: - -* `ANNOTATION_STORE` -** Contains an `AnnotationStore` that keeps information about all `AnnotationTarget` annotations after application of annotation transformers -* `INJECTION_POINTS` -** `Collection` containing all injection points -* `BEANS` -** `Collection` containing all beans -* `REMOVED_BEANS` -** `Collection` containing all the removed beans; see <> for more information -* `OBSERVERS` -** `Collection` containing all observers -* `SCOPES` -** `Collection` containing all scopes, including custom ones -* `QUALIFIERS` -** `Map` containing all qualifiers -* `INTERCEPTOR_BINDINGS` -** `Map` containing all interceptor bindings -* `STEREOTYPES` -** `Map` containing all stereotypes - -To get hold of these, simply query the extension context object for given key. -Note that these metadata are made available as build proceeds which means that extensions can only leverage metadata that were build before they are invoked. -If your extension attempts to retrieve metadata that wasn't yet produced, `null` will be returned. -Here is a summary of which extensions can access which metadata: - -* `AnnotationsTransformer` -** Shouldn't rely on any metadata as it could be used at any time in any phase of the bootstrap -* `ContextRegistrar` -** Has access to `ANNOTATION_STORE`, `QUALIFIERS`, `INTERCEPTOR_BINDINGS`, `STEREOTYPES` -* `InjectionPointsTransformer` -** Has access to `ANNOTATION_STORE`, `QUALIFIERS`, `INTERCEPTOR_BINDINGS`, `STEREOTYPES` -* `ObserverTransformer` -** Has access to `ANNOTATION_STORE`, `QUALIFIERS`, `INTERCEPTOR_BINDINGS`, `STEREOTYPES` -* `BeanRegistrar` -** Has access to `ANNOTATION_STORE`, `QUALIFIERS`, `INTERCEPTOR_BINDINGS`, `STEREOTYPES`, `BEANS` (class-based beans only), `OBSERVERS` (class-based observers only), `INJECTION_POINTS` -* `ObserverRegistrar` -** Has access to `ANNOTATION_STORE`, `QUALIFIERS`, `INTERCEPTOR_BINDINGS`, `STEREOTYPES`, `BEANS`, `OBSERVERS` (class-based observers only), `INJECTION_POINTS` -* `BeanDeploymentValidator` -** Has access to all build metadata - -[[dev-mode]] == Development Mode In the development mode, two special endpoints are registered automatically to provide some basic debug info in the JSON format: diff --git a/docs/src/main/asciidoc/cdi.adoc b/docs/src/main/asciidoc/cdi.adoc index b0da08e5aea12..6409d8632eb7f 100644 --- a/docs/src/main/asciidoc/cdi.adoc +++ b/docs/src/main/asciidoc/cdi.adoc @@ -58,6 +58,7 @@ public class Translator { <2> This is a field injection point. It tells the container that `Translator` depends on the `Dictionary` bean. If there is no matching bean the build fails. <3> This is an interceptor binding annotation. In this case, the annotation comes from the MicroProfile Metrics. The relevant interceptor intercepts the invocation and updates the relevant metrics. We will talk about <> later. +[[typesafe_resolution]] == Nice. How does the dependency resolution work? I see no names or identifiers. That's a good question. diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcProcessor.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcProcessor.java index e79b033c15cda..fb4efd98fad97 100644 --- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcProcessor.java +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcProcessor.java @@ -39,13 +39,16 @@ import io.quarkus.arc.processor.BeanConfigurator; import io.quarkus.arc.processor.BeanDefiningAnnotation; import io.quarkus.arc.processor.BeanDeployment; +import io.quarkus.arc.processor.BeanDeploymentValidator; import io.quarkus.arc.processor.BeanInfo; import io.quarkus.arc.processor.BeanProcessor; +import io.quarkus.arc.processor.BeanRegistrar; import io.quarkus.arc.processor.BytecodeTransformer; import io.quarkus.arc.processor.ContextConfigurator; import io.quarkus.arc.processor.ContextRegistrar; import io.quarkus.arc.processor.DotNames; import io.quarkus.arc.processor.ObserverConfigurator; +import io.quarkus.arc.processor.ObserverRegistrar; import io.quarkus.arc.processor.ReflectionRegistration; import io.quarkus.arc.processor.ResourceOutput; import io.quarkus.arc.processor.StereotypeInfo; @@ -91,6 +94,7 @@ *
    *
  1. {@link ContextRegistrationPhaseBuildItem}
  2. *
  3. {@link BeanRegistrationPhaseBuildItem}
  4. + *
  5. {@link ObserverRegistrationPhaseBuildItem}
  6. *
  7. {@link ValidationPhaseBuildItem}
  8. *
* These build items are especially useful if an extension needs to produce other build items within the given phase. @@ -123,7 +127,7 @@ AdditionalBeanBuildItem quarkusApplication(CombinedIndexBuildItem combinedIndexB .build(); } - // PHASE 1 - build BeanProcessor, register custom contexts + // PHASE 1 - build BeanProcessor @BuildStep public ContextRegistrationPhaseBuildItem initialize( ArcConfig arcConfig, @@ -143,10 +147,9 @@ public ContextRegistrationPhaseBuildItem initialize( List beanDeploymentValidators, List resourceAnnotations, List additionalBeanDefiningAnnotations, - List removalExclusions, Optional testClassPredicate, Capabilities capabilities, - CustomScopeAnnotationsBuildItem scopes, + CustomScopeAnnotationsBuildItem customScopes, LaunchModeBuildItem launchModeBuildItem) { if (!arcConfig.isRemoveUnusedBeansFieldValid()) { @@ -196,7 +199,7 @@ public void transform(TransformationContext transformationContext) { // Not an additional bean type return; } - if (scopes.isScopeDeclaredOn(beanClass)) { + if (customScopes.isScopeDeclaredOn(beanClass)) { // If it declares a scope no action is needed return; } @@ -278,9 +281,6 @@ protected DotName getDotName(BeanInfo bean) { builder.addRemovalExclusion(new BeanClassAnnotationExclusion(annotation.getName())); } } - for (UnremovableBeanBuildItem exclusion : removalExclusions) { - builder.addRemovalExclusion(exclusion.getPredicate()); - } // unremovable beans specified in application.properties if (arcConfig.unremovableTypes.isPresent()) { List> classPredicates = initClassPredicates(arcConfig.unremovableTypes.get()); @@ -366,6 +366,7 @@ public Integer compute(AnnotationTarget target, Collection stere public BeanRegistrationPhaseBuildItem registerBeans(ContextRegistrationPhaseBuildItem contextRegistrationPhase, List contextConfigurators, BuildProducer interceptorResolver, + BuildProducer beanDiscoveryFinished, BuildProducer transformedAnnotations) { for (ContextConfiguratorBuildItem contextConfigurator : contextConfigurators) { @@ -374,13 +375,15 @@ public BeanRegistrationPhaseBuildItem registerBeans(ContextRegistrationPhaseBuil value.done(); } } - contextRegistrationPhase.getBeanProcessor().registerScopes(); - BeanDeployment beanDeployment = contextRegistrationPhase.getBeanProcessor().getBeanDeployment(); + BeanProcessor beanProcessor = contextRegistrationPhase.getBeanProcessor(); + beanProcessor.registerScopes(); + BeanRegistrar.RegistrationContext registrationContext = beanProcessor.registerBeans(); + BeanDeployment beanDeployment = beanProcessor.getBeanDeployment(); interceptorResolver.produce(new InterceptorResolverBuildItem(beanDeployment)); + beanDiscoveryFinished.produce(new BeanDiscoveryFinishedBuildItem(beanDeployment)); transformedAnnotations.produce(new TransformedAnnotationsBuildItem(beanDeployment)); - return new BeanRegistrationPhaseBuildItem(contextRegistrationPhase.getBeanProcessor().registerBeans(), - contextRegistrationPhase.getBeanProcessor()); + return new BeanRegistrationPhaseBuildItem(registrationContext, beanProcessor); } // PHASE 3 - register synthetic observers @@ -393,25 +396,35 @@ public ObserverRegistrationPhaseBuildItem registerSyntheticObservers(BeanRegistr configurator.getValues().forEach(BeanConfigurator::done); } - return new ObserverRegistrationPhaseBuildItem(beanRegistrationPhase.getBeanProcessor().registerSyntheticObservers(), - beanRegistrationPhase.getBeanProcessor()); + BeanProcessor beanProcessor = beanRegistrationPhase.getBeanProcessor(); + ObserverRegistrar.RegistrationContext registrationContext = beanProcessor.registerSyntheticObservers(); + + return new ObserverRegistrationPhaseBuildItem(registrationContext, beanProcessor); } // PHASE 4 - initialize and validate the bean deployment @BuildStep public ValidationPhaseBuildItem validate(ObserverRegistrationPhaseBuildItem observerRegistrationPhase, List observerConfigurators, - BuildProducer bytecodeTransformer) { + List unremovableBeans, + BuildProducer bytecodeTransformer, + BuildProducer synthesisFinished) { for (ObserverConfiguratorBuildItem configurator : observerConfigurators) { // Just make sure the configurator is processed configurator.getValues().forEach(ObserverConfigurator::done); } + BeanProcessor beanProcessor = observerRegistrationPhase.getBeanProcessor(); + synthesisFinished.produce(new SynthesisFinishedBuildItem(beanProcessor.getBeanDeployment())); + Consumer bytecodeTransformerConsumer = new BytecodeTransformerConsumer(bytecodeTransformer); - observerRegistrationPhase.getBeanProcessor().initialize(bytecodeTransformerConsumer); - return new ValidationPhaseBuildItem(observerRegistrationPhase.getBeanProcessor().validate(bytecodeTransformerConsumer), - observerRegistrationPhase.getBeanProcessor()); + + beanProcessor.initialize(bytecodeTransformerConsumer, + unremovableBeans.stream().map(UnremovableBeanBuildItem::getPredicate).collect(Collectors.toList())); + BeanDeploymentValidator.ValidationContext validationContext = beanProcessor.validate(bytecodeTransformerConsumer); + + return new ValidationPhaseBuildItem(validationContext, beanProcessor); } // PHASE 5 - generate resources and initialize the container @@ -490,7 +503,6 @@ public void registerField(FieldInfo fieldInfo) { .collect(Collectors.toList())); return new BeanContainerBuildItem(beanContainer); - } @BuildStep @@ -516,11 +528,15 @@ AdditionalBeanBuildItem loggerProducer() { } @BuildStep - CustomScopeAnnotationsBuildItem exposeCustomScopeNames(List contextBuildItems) { + CustomScopeAnnotationsBuildItem exposeCustomScopeNames(List contextBuildItems, + List customScopes) { Set names = new HashSet<>(); for (ContextRegistrarBuildItem item : contextBuildItems) { names.addAll(item.getAnnotationNames()); } + for (CustomScopeBuildItem customScope : customScopes) { + names.add(customScope.getAnnotationName()); + } return new CustomScopeAnnotationsBuildItem(names); } diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanArchiveIndexBuildItem.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanArchiveIndexBuildItem.java index ed01bf8d206b3..314475a88c8df 100644 --- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanArchiveIndexBuildItem.java +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanArchiveIndexBuildItem.java @@ -1,6 +1,5 @@ package io.quarkus.arc.deployment; -import java.util.List; import java.util.Set; import org.jboss.jandex.DotName; @@ -25,13 +24,10 @@ public final class BeanArchiveIndexBuildItem extends SimpleBuildItem { private final IndexView index; private final Set generatedClassNames; - private final List additionalBeans; - public BeanArchiveIndexBuildItem(IndexView index, Set generatedClassNames, - List additionalBeans) { + public BeanArchiveIndexBuildItem(IndexView index, Set generatedClassNames) { this.index = index; this.generatedClassNames = generatedClassNames; - this.additionalBeans = additionalBeans; } public IndexView getIndex() { @@ -42,15 +38,4 @@ public Set getGeneratedClassNames() { return generatedClassNames; } - /** - * This method will be removed at some point post Quarkus 1.9. - * - * @return the list of additional beans - * @deprecated Use {@link AdditionalBeanBuildItem} instead - */ - @Deprecated - public List getAdditionalBeans() { - return additionalBeans; - } - } diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanArchiveProcessor.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanArchiveProcessor.java index 4d257a63c2927..d9a8d986c73c6 100644 --- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanArchiveProcessor.java +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanArchiveProcessor.java @@ -7,8 +7,6 @@ import java.util.Set; import java.util.stream.Collectors; -import javax.inject.Inject; - import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.AnnotationTarget.Kind; import org.jboss.jandex.CompositeIndex; @@ -36,39 +34,30 @@ public class BeanArchiveProcessor { - @Inject - ApplicationArchivesBuildItem applicationArchivesBuildItem; - - @Inject - List additionalBeanDefiningAnnotations; - - @Inject - List additionalBeans; - - @Inject - List generatedBeans; - - ArcConfig config; - @BuildStep - public BeanArchiveIndexBuildItem build(LiveReloadBuildItem liveReloadBuildItem, - BuildProducer generatedClass) throws Exception { + public BeanArchiveIndexBuildItem build(ArcConfig config, ApplicationArchivesBuildItem applicationArchivesBuildItem, + List additionalBeanDefiningAnnotations, + List additionalBeans, List generatedBeans, + LiveReloadBuildItem liveReloadBuildItem, BuildProducer generatedClass, + CustomScopeAnnotationsBuildItem customScopes) + throws Exception { // First build an index from application archives - IndexView applicationIndex = buildApplicationIndex(); + IndexView applicationIndex = buildApplicationIndex(config, applicationArchivesBuildItem, + additionalBeanDefiningAnnotations, customScopes); // Then build additional index for beans added by extensions Indexer additionalBeanIndexer = new Indexer(); - List additionalBeans = new ArrayList<>(); - for (AdditionalBeanBuildItem i : this.additionalBeans) { - additionalBeans.addAll(i.getBeanClasses()); + List additionalBeanClasses = new ArrayList<>(); + for (AdditionalBeanBuildItem i : additionalBeans) { + additionalBeanClasses.addAll(i.getBeanClasses()); } // NOTE: the types added directly must always declare a scope annotation otherwise they will be ignored during bean discovery - additionalBeans.add(LifecycleEventRunner.class.getName()); + additionalBeanClasses.add(LifecycleEventRunner.class.getName()); // Build the index for additional beans and generated bean classes Set additionalIndex = new HashSet<>(); - for (String beanClass : additionalBeans) { + for (String beanClass : additionalBeanClasses) { IndexingUtil.indexClass(beanClass, additionalBeanIndexer, applicationIndex, additionalIndex, Thread.currentThread().getContextClassLoader()); } @@ -93,11 +82,12 @@ public BeanArchiveIndexBuildItem build(LiveReloadBuildItem liveReloadBuildItem, BeanArchives.buildBeanArchiveIndex(Thread.currentThread().getContextClassLoader(), index.getAdditionalClasses(), applicationIndex, additionalBeanIndexer.complete()), - generatedClassNames, - additionalBeans); + generatedClassNames); } - private IndexView buildApplicationIndex() { + private IndexView buildApplicationIndex(ArcConfig config, ApplicationArchivesBuildItem applicationArchivesBuildItem, + List additionalBeanDefiningAnnotations, + CustomScopeAnnotationsBuildItem customScopes) { Set archives = applicationArchivesBuildItem.getAllApplicationArchives(); @@ -114,10 +104,13 @@ private IndexView buildApplicationIndex() { } } - Collection beanDefiningAnnotations = BeanDeployment + Set beanDefiningAnnotations = BeanDeployment .initBeanDefiningAnnotations(additionalBeanDefiningAnnotations.stream() .map(bda -> new BeanDefiningAnnotation(bda.getName(), bda.getDefaultScope())) .collect(Collectors.toList()), stereotypes); + for (DotName customScopeAnnotationName : customScopes.getCustomScopeNames()) { + beanDefiningAnnotations.add(customScopeAnnotationName); + } // Also include archives that are not bean archives but contain qualifiers or interceptor bindings beanDefiningAnnotations.add(DotNames.QUALIFIER); beanDefiningAnnotations.add(DotNames.INTERCEPTOR_BINDING); @@ -125,7 +118,7 @@ private IndexView buildApplicationIndex() { List indexes = new ArrayList<>(); for (ApplicationArchive archive : applicationArchivesBuildItem.getApplicationArchives()) { - if (isApplicationArchiveExcluded(archive)) { + if (isApplicationArchiveExcluded(config, archive)) { continue; } IndexView index = archive.getIndex(); @@ -140,7 +133,7 @@ && containsBeanDefiningAnnotation(index, beanDefiningAnnotations))) { return CompositeIndex.create(indexes); } - private boolean isApplicationArchiveExcluded(ApplicationArchive archive) { + private boolean isApplicationArchiveExcluded(ArcConfig config, ApplicationArchive archive) { if (archive.getArtifactKey() != null) { AppArtifactKey key = archive.getArtifactKey(); for (IndexDependencyConfig excludeDependency : config.excludeDependency.values()) { diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanDeploymentValidatorBuildItem.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanDeploymentValidatorBuildItem.java index ac674cd404536..b56e1d6afe533 100644 --- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanDeploymentValidatorBuildItem.java +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanDeploymentValidatorBuildItem.java @@ -6,10 +6,12 @@ /** * Register a custom {@link BeanDeploymentValidator} which can either perform additional validation or enforce * validation skip for certain components. - * - * @see NoArgsConstructorProcessor#addMissingConstructors() - * @see ObserverValidationProcessor + * + * This build item will be removed at some point post Quarkus 1.11. + * + * @deprecated Use {@link ValidationPhaseBuildItem} instead */ +@Deprecated public final class BeanDeploymentValidatorBuildItem extends MultiBuildItem { private final BeanDeploymentValidator beanDeploymentValidator; diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanDiscoveryFinishedBuildItem.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanDiscoveryFinishedBuildItem.java new file mode 100644 index 0000000000000..1072487dae251 --- /dev/null +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanDiscoveryFinishedBuildItem.java @@ -0,0 +1,21 @@ +package io.quarkus.arc.deployment; + +import io.quarkus.arc.processor.BeanDeployment; + +/** + * Consumers of this build item can easily inspect all class-based beans, observers and injection points registered in the + * application. Synthetic beans and observers are not included. If you need to consider synthetic components as well use + * the {@link SynthesisFinishedBuildItem} instead. + *

+ * Additionaly, the bean resolver can be used to apply the type-safe resolution rules, e.g. to find out wheter there is a bean + * that would satisfy certain combination of required type and qualifiers. + * + * @see SynthesisFinishedBuildItem + */ +public final class BeanDiscoveryFinishedBuildItem extends RegisteredComponentsBuildItem { + + public BeanDiscoveryFinishedBuildItem(BeanDeployment beanDeployment) { + super(beanDeployment); + } + +} diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanRegistrarBuildItem.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanRegistrarBuildItem.java index d318e50ef1b24..3fca5fa6085c9 100644 --- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanRegistrarBuildItem.java +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanRegistrarBuildItem.java @@ -8,7 +8,12 @@ * specify a synthetic bean through series of configuration methods (scope, type, qualifiers, ...). * * This is a build time alternative to CDI BeanConfigurator API. + * + * This build item will be removed at some point post Quarkus 1.11. + * + * @deprecated Use {@link SyntheticBeanBuildItem} instead */ +@Deprecated public final class BeanRegistrarBuildItem extends MultiBuildItem { private final BeanRegistrar beanRegistrar; diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanRegistrationPhaseBuildItem.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanRegistrationPhaseBuildItem.java index ba956c71f37b7..aa7d79a46923a 100644 --- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanRegistrationPhaseBuildItem.java +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/BeanRegistrationPhaseBuildItem.java @@ -1,12 +1,15 @@ package io.quarkus.arc.deployment; import java.util.Arrays; +import java.util.Collection; import java.util.List; import io.quarkus.arc.processor.BeanConfigurator; import io.quarkus.arc.processor.BeanProcessor; import io.quarkus.arc.processor.BeanRegistrar; import io.quarkus.arc.processor.BeanRegistrar.RegistrationContext; +import io.quarkus.arc.processor.BuildExtension; +import io.quarkus.arc.processor.InjectionPointInfo; import io.quarkus.builder.item.MultiBuildItem; import io.quarkus.builder.item.SimpleBuildItem; import io.quarkus.deployment.annotations.BuildProducer; @@ -36,6 +39,10 @@ public RegistrationContext getContext() { return context; } + public Collection getInjectionPoints() { + return getContext().get(BuildExtension.Key.INJECTION_POINTS); + } + public BeanProcessor getBeanProcessor() { return beanProcessor; } diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ConfigBuildStep.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ConfigBuildStep.java index bdf964b77f242..ed255cd1efa1d 100644 --- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ConfigBuildStep.java +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ConfigBuildStep.java @@ -7,7 +7,6 @@ import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toSet; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -31,7 +30,6 @@ import io.quarkus.arc.deployment.BeanRegistrationPhaseBuildItem.BeanConfiguratorBuildItem; import io.quarkus.arc.processor.BeanRegistrar; -import io.quarkus.arc.processor.BuildExtension; import io.quarkus.arc.processor.DotNames; import io.quarkus.arc.processor.InjectionPointInfo; import io.quarkus.arc.runtime.ConfigBeanCreator; @@ -70,14 +68,14 @@ AdditionalBeanBuildItem bean() { } @BuildStep - void analyzeConfigPropertyInjectionPoints(BeanRegistrationPhaseBuildItem beanRegistrationPhase, + void analyzeConfigPropertyInjectionPoints(BeanDiscoveryFinishedBuildItem beanDiscovery, BuildProducer configProperties, BuildProducer reflectiveClass, - BuildProducer beanConfigurators) { + BuildProducer syntheticBeans) { Set customBeanTypes = new HashSet<>(); - for (InjectionPointInfo injectionPoint : beanRegistrationPhase.getContext().get(BuildExtension.Key.INJECTION_POINTS)) { + for (InjectionPointInfo injectionPoint : beanDiscovery.getInjectionPoints()) { if (injectionPoint.hasDefaultedQualifier()) { // Defaulted qualifier means no @ConfigProperty continue; @@ -131,13 +129,14 @@ void analyzeConfigPropertyInjectionPoints(BeanRegistrationPhaseBuildItem beanReg // Implicit converters are most likely used reflectiveClass.produce(new ReflectiveClassBuildItem(true, false, type.name().toString())); } - beanConfigurators.produce(new BeanConfiguratorBuildItem(beanRegistrationPhase.getContext().configure( - type.kind() == Kind.ARRAY ? DotName.createSimple(ConfigBeanCreator.class.getName()) : type.name()) + DotName implClazz = type.kind() == Kind.ARRAY ? DotName.createSimple(ConfigBeanCreator.class.getName()) + : type.name(); + syntheticBeans.produce(SyntheticBeanBuildItem.configure(implClazz) .creator(ConfigBeanCreator.class) .providerType(type) .types(type) - .qualifiers(AnnotationInstance.create(CONFIG_PROPERTY_NAME, null, Collections.emptyList())) - .param("requiredType", type.name().toString()))); + .addQualifier(CONFIG_PROPERTY_NAME) + .param("requiredType", type.name().toString()).done()); } } diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ContextRegistrarBuildItem.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ContextRegistrarBuildItem.java index 5a24cfe1f85c7..d3e0210797e3b 100644 --- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ContextRegistrarBuildItem.java +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ContextRegistrarBuildItem.java @@ -18,7 +18,12 @@ * * This information is then leveraged in {@link CustomScopeAnnotationsBuildItem} which allows consumers to browse * all known custom scoped within deployment even early in the build process. + * + * This build item will be removed at some point post Quarkus 1.11. + * + * @deprecated User {@link ContextRegistrationPhaseBuildItem} instead */ +@Deprecated public final class ContextRegistrarBuildItem extends MultiBuildItem { private static final Logger LOGGER = Logger.getLogger(ContextRegistrarBuildItem.class); diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ContextRegistrationPhaseBuildItem.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ContextRegistrationPhaseBuildItem.java index 2e34c898d4f18..e8cb7c1487fe1 100644 --- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ContextRegistrationPhaseBuildItem.java +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ContextRegistrationPhaseBuildItem.java @@ -19,6 +19,7 @@ * {@link ArcProcessor#registerBeans(ContextRegistrationPhaseBuildItem, List)}. * * @see ContextConfiguratorBuildItem + * @see CustomScopeBuildItem */ public final class ContextRegistrationPhaseBuildItem extends SimpleBuildItem { diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/CustomScopeAnnotationsBuildItem.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/CustomScopeAnnotationsBuildItem.java index cc537051f04c8..cbfa7f4b3ffa9 100644 --- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/CustomScopeAnnotationsBuildItem.java +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/CustomScopeAnnotationsBuildItem.java @@ -18,7 +18,7 @@ public final class CustomScopeAnnotationsBuildItem extends SimpleBuildItem { private final Set customScopeNames; - public CustomScopeAnnotationsBuildItem(Set customScopeNames) { + CustomScopeAnnotationsBuildItem(Set customScopeNames) { this.customScopeNames = customScopeNames; } diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/CustomScopeBuildItem.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/CustomScopeBuildItem.java new file mode 100644 index 0000000000000..ace06a08b42c9 --- /dev/null +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/CustomScopeBuildItem.java @@ -0,0 +1,26 @@ +package io.quarkus.arc.deployment; + +import org.jboss.jandex.DotName; + +import io.quarkus.builder.item.MultiBuildItem; + +/** + * An extension that registers a custom CDI context via {@link ContextRegistrationPhaseBuildItem} should produce this build + * item in order to contribute the custom scope annotation name to the set of bean defining annotations. + * + * @see CustomScopeAnnotationsBuildItem + * @see ContextRegistrationPhaseBuildItem + */ +public final class CustomScopeBuildItem extends MultiBuildItem { + + private final DotName annotationName; + + public CustomScopeBuildItem(DotName annotationName) { + this.annotationName = annotationName; + } + + public DotName getAnnotationName() { + return annotationName; + } + +} diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ObserverRegistrarBuildItem.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ObserverRegistrarBuildItem.java index b072e4ed4c689..c2ccd193dd19a 100644 --- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ObserverRegistrarBuildItem.java +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ObserverRegistrarBuildItem.java @@ -8,7 +8,12 @@ * specify a synthetic observer through series of configuration methods. * * This is a build time alternative to CDI ObserverMethodConfigurator API. + * + * This build item will be removed at some point post Quarkus 1.11. + * + * @deprecated Use {@link ObserverRegistrationPhaseBuildItem} instead */ +@Deprecated public final class ObserverRegistrarBuildItem extends MultiBuildItem { private final ObserverRegistrar observerRegistrar; diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/RegisteredComponentsBuildItem.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/RegisteredComponentsBuildItem.java new file mode 100644 index 0000000000000..47ce22ad5f548 --- /dev/null +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/RegisteredComponentsBuildItem.java @@ -0,0 +1,66 @@ +package io.quarkus.arc.deployment; + +import java.util.Collection; +import java.util.stream.Stream; + +import io.quarkus.arc.processor.BeanDeployment; +import io.quarkus.arc.processor.BeanInfo; +import io.quarkus.arc.processor.BeanResolver; +import io.quarkus.arc.processor.BeanStream; +import io.quarkus.arc.processor.InjectionPointInfo; +import io.quarkus.arc.processor.ObserverInfo; +import io.quarkus.builder.item.SimpleBuildItem; + +abstract class RegisteredComponentsBuildItem extends SimpleBuildItem { + + private final Collection beans; + private final Collection injectionPoints; + private final Collection observers; + private final BeanResolver beanResolver; + + public RegisteredComponentsBuildItem(BeanDeployment beanDeployment) { + this.beans = beanDeployment.getBeans(); + this.injectionPoints = beanDeployment.getInjectionPoints(); + this.observers = beanDeployment.getObservers(); + this.beanResolver = beanDeployment.getBeanResolver(); + } + + /** + * @return the registered beans + */ + public Collection geBeans() { + return beans; + } + + /** + * @return the registered injection points + */ + public Collection getInjectionPoints() { + return injectionPoints; + } + + /** + * @return the registered observers + */ + public Collection getObservers() { + return observers; + } + + /** + * + * @return a convenient {@link Stream} wrapper that can be used to filter a set of beans + */ + public BeanStream beanStream() { + return new BeanStream(beans); + } + + /** + * The bean resolver can be used to apply the type-safe resolution rules. + * + * @return the bean resolver + */ + public BeanResolver getBeanResolver() { + return beanResolver; + } + +} diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/SynthesisFinishedBuildItem.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/SynthesisFinishedBuildItem.java new file mode 100644 index 0000000000000..a345fc282c4b2 --- /dev/null +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/SynthesisFinishedBuildItem.java @@ -0,0 +1,21 @@ +package io.quarkus.arc.deployment; + +import io.quarkus.arc.processor.BeanDeployment; + +/** + * Consumers of this build item can easily inspect all beans, observers and injection points registered in the + * application. Synthetic beans and observers are included. If interested in class-based components only you can use the + * {@link BeanDiscoveryFinishedBuildItem} instead. + *

+ * Additionaly, the bean resolver can be used to apply the type-safe resolution rules, e.g. to find out whether there is a bean + * that would satisfy certain combination of required type and qualifiers. + * + * @see BeanDiscoveryFinishedBuildItem + */ +public final class SynthesisFinishedBuildItem extends RegisteredComponentsBuildItem { + + public SynthesisFinishedBuildItem(BeanDeployment beanDeployment) { + super(beanDeployment); + } + +} diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/SyntheticBeanBuildItem.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/SyntheticBeanBuildItem.java index ec38029f39620..26d614e751315 100644 --- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/SyntheticBeanBuildItem.java +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/SyntheticBeanBuildItem.java @@ -6,24 +6,39 @@ import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.DotName; -import io.quarkus.arc.processor.BeanConfigurator; import io.quarkus.arc.processor.BeanConfiguratorBase; +import io.quarkus.arc.processor.BeanRegistrar; import io.quarkus.builder.item.MultiBuildItem; import io.quarkus.deployment.annotations.ExecutionTime; import io.quarkus.runtime.RuntimeValue; /** - * Makes it possible to register a synthetic bean whose instance can be easily produced through a recorder. + * Makes it possible to register a synthetic bean. + *

+ * Bean instances can be easily produced through a recorder and set via {@link ExtendedBeanConfigurator#supplier(Supplier)} and + * {@link ExtendedBeanConfigurator#runtimeValue(RuntimeValue)}. * - * @see BeanConfigurator - * @see ExtendedBeanConfigurator#setRuntimeInit() + * @see ExtendedBeanConfigurator + * @see BeanRegistrar */ public final class SyntheticBeanBuildItem extends MultiBuildItem { + /** + * + * @param implClazz + * @return a new configurator instance + * @see ExtendedBeanConfigurator#done() + */ public static ExtendedBeanConfigurator configure(Class implClazz) { return configure(DotName.createSimple(implClazz.getName())); } + /** + * + * @param implClazz + * @return a new configurator instance + * @see ExtendedBeanConfigurator#done() + */ public static ExtendedBeanConfigurator configure(DotName implClazz) { return new ExtendedBeanConfigurator(implClazz).addType(implClazz); } @@ -34,22 +49,26 @@ public static ExtendedBeanConfigurator configure(DotName implClazz) { this.configurator = configurator; } - public ExtendedBeanConfigurator configurator() { + ExtendedBeanConfigurator configurator() { return configurator; } - public boolean isStaticInit() { + boolean isStaticInit() { return configurator.staticInit; } + boolean hasRecorderInstance() { + return configurator.supplier != null || configurator.runtimeValue != null; + } + /** * This construct is not thread-safe and should not be reused. */ public static class ExtendedBeanConfigurator extends BeanConfiguratorBase { - Supplier supplier; - RuntimeValue runtimeValue; - boolean staticInit; + private Supplier supplier; + private RuntimeValue runtimeValue; + private boolean staticInit; ExtendedBeanConfigurator(DotName implClazz) { super(implClazz); @@ -65,8 +84,9 @@ public SyntheticBeanBuildItem done() { if (supplier != null && runtimeValue != null) { throw new IllegalStateException("It is not possible to specify both - a supplier and a runtime value"); } - if (supplier == null && runtimeValue == null) { - throw new IllegalStateException("Either a supplier or a runtime value must be set"); + if (creatorConsumer == null && supplier == null && runtimeValue == null) { + throw new IllegalStateException( + "Synthetic bean does not provide a creation method, use ExtendedBeanConfigurator#creator(), ExtendedBeanConfigurator#supplier() or ExtendedBeanConfigurator#runtimeValue()"); } return new SyntheticBeanBuildItem(this); } @@ -82,9 +102,12 @@ public ExtendedBeanConfigurator runtimeValue(RuntimeValue runtimeValue) { } /** - * By default, synthetic beans are initialized during {@link ExecutionTime#STATIC_INIT}. It is possible to mark a - * synthetic bean to be initialized during {@link ExecutionTime#RUNTIME_INIT}. However, in such case a client that - * attempts to obtain such bean during {@link ExecutionTime#STATIC_INIT} or before runtime-init synthetic beans are + * A synthetic bean whose instance is produced through a recorder is initialized during + * {@link ExecutionTime#STATIC_INIT} by default. + *

+ * It is possible to change this behavior and initialize the bean during the {@link ExecutionTime#RUNTIME_INIT}. + * However, in such case a client that attempts to obtain such bean during {@link ExecutionTime#STATIC_INIT} or before + * runtime-init synthetic beans are * initialized will receive an exception. *

* {@link ExecutionTime#RUNTIME_INIT} build steps that access a runtime-init synthetic bean should consume the @@ -98,11 +121,11 @@ public ExtendedBeanConfigurator setRuntimeInit() { return this; } - public DotName getImplClazz() { + DotName getImplClazz() { return implClazz; } - public Set getQualifiers() { + Set getQualifiers() { return qualifiers; } @@ -111,5 +134,13 @@ protected ExtendedBeanConfigurator self() { return this; } + Supplier getSupplier() { + return supplier; + } + + RuntimeValue getRuntimeValue() { + return runtimeValue; + } + } } diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/SyntheticBeansProcessor.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/SyntheticBeansProcessor.java index 857bec58f595b..7161c6bf4cb55 100644 --- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/SyntheticBeansProcessor.java +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/SyntheticBeansProcessor.java @@ -11,6 +11,7 @@ import org.jboss.jandex.DotName; import io.quarkus.arc.deployment.BeanRegistrationPhaseBuildItem.BeanConfiguratorBuildItem; +import io.quarkus.arc.processor.BeanConfigurator; import io.quarkus.arc.runtime.ArcRecorder; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; @@ -34,8 +35,8 @@ void initStatic(ArcRecorder recorder, List syntheticBean Map> suppliersMap = new HashMap<>(); for (SyntheticBeanBuildItem bean : syntheticBeans) { - if (bean.isStaticInit()) { - initSyntheticBean(recorder, suppliersMap, beanRegistration, bean); + if (bean.hasRecorderInstance() && bean.isStaticInit()) { + configureSyntheticBean(recorder, suppliersMap, beanRegistration, bean); } } // Init the map of bean instances @@ -51,27 +52,40 @@ ServiceStartBuildItem initRuntime(ArcRecorder recorder, List> suppliersMap = new HashMap<>(); for (SyntheticBeanBuildItem bean : syntheticBeans) { - if (!bean.isStaticInit()) { - initSyntheticBean(recorder, suppliersMap, beanRegistration, bean); + if (bean.hasRecorderInstance() && !bean.isStaticInit()) { + configureSyntheticBean(recorder, suppliersMap, beanRegistration, bean); } } recorder.initRuntimeSupplierBeans(suppliersMap); return new ServiceStartBuildItem("runtime-bean-init"); } - private void initSyntheticBean(ArcRecorder recorder, Map> suppliersMap, + @BuildStep + void initRegular(List syntheticBeans, + BeanRegistrationPhaseBuildItem beanRegistration, BuildProducer configurators) { + + for (SyntheticBeanBuildItem bean : syntheticBeans) { + if (!bean.hasRecorderInstance()) { + configureSyntheticBean(null, null, beanRegistration, bean); + } + } + } + + private void configureSyntheticBean(ArcRecorder recorder, Map> suppliersMap, BeanRegistrationPhaseBuildItem beanRegistration, SyntheticBeanBuildItem bean) { DotName implClazz = bean.configurator().getImplClazz(); String name = createName(implClazz.toString(), bean.configurator().getQualifiers().toString()); - if (bean.configurator().runtimeValue != null) { - suppliersMap.put(name, recorder.createSupplier(bean.configurator().runtimeValue)); - } else { - suppliersMap.put(name, bean.configurator().supplier); + if (bean.configurator().getRuntimeValue() != null) { + suppliersMap.put(name, recorder.createSupplier(bean.configurator().getRuntimeValue())); + } else if (bean.configurator().getSupplier() != null) { + suppliersMap.put(name, bean.configurator().getSupplier()); + } + BeanConfigurator configurator = beanRegistration.getContext().configure(implClazz) + .read(bean.configurator()); + if (bean.hasRecorderInstance()) { + configurator.creator(creator(name, bean)); } - beanRegistration.getContext().configure(implClazz) - .read(bean.configurator()) - .creator(creator(name, bean)) - .done(); + configurator.done(); } private String createName(String beanClass, String qualifiers) { diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/TransformedAnnotationsBuildItem.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/TransformedAnnotationsBuildItem.java index 10bc173dc5535..bb5f5574f968a 100644 --- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/TransformedAnnotationsBuildItem.java +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/TransformedAnnotationsBuildItem.java @@ -5,6 +5,7 @@ import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.AnnotationTarget; +import org.jboss.jandex.DotName; import io.quarkus.arc.processor.AnnotationsTransformer; import io.quarkus.arc.processor.BeanDeployment; @@ -18,19 +19,23 @@ public final class TransformedAnnotationsBuildItem extends SimpleBuildItem implements Function> { - private final Function> fun; + private final BeanDeployment beanDeployment; TransformedAnnotationsBuildItem(BeanDeployment beanDeployment) { - this.fun = beanDeployment::getAnnotations; + this.beanDeployment = beanDeployment; } public Collection getAnnotations(AnnotationTarget target) { - return fun.apply(target); + return beanDeployment.getAnnotations(target); + } + + public AnnotationInstance getAnnotation(AnnotationTarget target, DotName name) { + return beanDeployment.getAnnotation(target, name); } @Override public Collection apply(AnnotationTarget target) { - return fun.apply(target); + return beanDeployment.getAnnotations(target); } } diff --git a/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/binder/mpmetrics/MicroprofileMetricsProcessor.java b/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/binder/mpmetrics/MicroprofileMetricsProcessor.java index c6793f0177e83..380519f593096 100644 --- a/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/binder/mpmetrics/MicroprofileMetricsProcessor.java +++ b/extensions/micrometer/deployment/src/main/java/io/quarkus/micrometer/deployment/binder/mpmetrics/MicroprofileMetricsProcessor.java @@ -86,8 +86,7 @@ void logWarningForMpMetricsUsage(CombinedIndexBuildItem combinedIndexBuildItem, } if (!mpMetricsPresent) { - for (InjectionPointInfo injectionPoint : beanRegistrationPhase.getContext() - .get(BuildExtension.Key.INJECTION_POINTS)) { + for (InjectionPointInfo injectionPoint : beanRegistrationPhase.getInjectionPoints()) { if (injectionPoint.getRequiredType().name().equals(MetricDotNames.METRIC_REGISTRY)) { mpMetricsPresent = true; break; diff --git a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java index 21484e5cda811..d0af2dd94e24b 100644 --- a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java +++ b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java @@ -49,13 +49,11 @@ import io.quarkus.arc.deployment.AdditionalBeanBuildItem; import io.quarkus.arc.deployment.BeanArchiveIndexBuildItem; -import io.quarkus.arc.deployment.BeanRegistrationPhaseBuildItem; -import io.quarkus.arc.deployment.BeanRegistrationPhaseBuildItem.BeanConfiguratorBuildItem; +import io.quarkus.arc.deployment.BeanDiscoveryFinishedBuildItem; import io.quarkus.arc.deployment.SyntheticBeanBuildItem; import io.quarkus.arc.deployment.ValidationPhaseBuildItem; import io.quarkus.arc.deployment.ValidationPhaseBuildItem.ValidationErrorBuildItem; import io.quarkus.arc.processor.BeanInfo; -import io.quarkus.arc.processor.BuildExtension; import io.quarkus.arc.processor.DotNames; import io.quarkus.arc.processor.InjectionPointInfo; import io.quarkus.bootstrap.classloading.QuarkusClassLoader; @@ -422,9 +420,7 @@ void validateExpressions(TemplatesAnalysisBuildItem templatesAnalysis, BeanArchi List excludes, BuildProducer incorrectExpressions, BuildProducer implicitClasses, - BeanRegistrationPhaseBuildItem registrationPhase, - // This producer is needed to ensure the correct ordering, ie. this build step must be executed before the ArC validation step - BuildProducer configurators) { + BeanDiscoveryFinishedBuildItem beanDiscovery) { IndexView index = beanArchiveIndex.getIndex(); Function templateIdToPathFun = new Function() { @@ -437,7 +433,7 @@ public String apply(String id) { // IMPLEMENTATION NOTE: // We do not support injection of synthetic beans with names // Dependency on the ValidationPhaseBuildItem would result in a cycle in the build chain - Map namedBeans = registrationPhase.getContext().beans().withName() + Map namedBeans = beanDiscovery.beanStream().withName() .collect(toMap(BeanInfo::getName, Function.identity())); // Map implicit class -> set of used members @@ -928,7 +924,7 @@ void validateTemplateInjectionPoints(QuteConfig config, List scheduledBusinessMethods, - BuildProducer beans) { - - AnnotationStore annotationStore = beanRegistrationPhase.getContext().get(BuildExtension.Key.ANNOTATION_STORE); + void collectScheduledMethods(BeanArchiveIndexBuildItem beanArchives, BeanDiscoveryFinishedBuildItem beanDiscovery, + TransformedAnnotationsBuildItem transformedAnnotations, + BuildProducer scheduledBusinessMethods) { // We need to collect all business methods annotated with @Scheduled first - for (BeanInfo bean : beanRegistrationPhase.getContext().beans().classBeans()) { - collectScheduledMethods(beanArchives.getIndex(), annotationStore, bean, + for (BeanInfo bean : beanDiscovery.beanStream().classBeans()) { + collectScheduledMethods(beanArchives.getIndex(), transformedAnnotations, bean, bean.getTarget().get().asClass(), scheduledBusinessMethods); } } - private void collectScheduledMethods(IndexView index, AnnotationStore annotationStore, BeanInfo bean, + private void collectScheduledMethods(IndexView index, TransformedAnnotationsBuildItem transformedAnnotations, BeanInfo bean, ClassInfo beanClass, BuildProducer scheduledBusinessMethods) { for (MethodInfo method : beanClass.methods()) { List schedules = null; - AnnotationInstance scheduledAnnotation = annotationStore.getAnnotation(method, SCHEDULED_NAME); + AnnotationInstance scheduledAnnotation = transformedAnnotations.getAnnotation(method, SCHEDULED_NAME); if (scheduledAnnotation != null) { schedules = Collections.singletonList(scheduledAnnotation); } else { - AnnotationInstance schedulesAnnotation = annotationStore.getAnnotation(method, SCHEDULES_NAME); + AnnotationInstance schedulesAnnotation = transformedAnnotations.getAnnotation(method, SCHEDULES_NAME); if (schedulesAnnotation != null) { schedules = new ArrayList<>(); for (AnnotationInstance scheduledInstance : schedulesAnnotation.value().asNestedArray()) { @@ -141,7 +138,7 @@ private void collectScheduledMethods(IndexView index, AnnotationStore annotation if (superClassName != null) { ClassInfo superClass = index.getClassByName(superClassName); if (superClass != null) { - collectScheduledMethods(index, annotationStore, bean, superClass, scheduledBusinessMethods); + collectScheduledMethods(index, transformedAnnotations, bean, superClass, scheduledBusinessMethods); } } } diff --git a/extensions/undertow/deployment/src/main/java/io/quarkus/undertow/deployment/UndertowBuildStep.java b/extensions/undertow/deployment/src/main/java/io/quarkus/undertow/deployment/UndertowBuildStep.java index 8d85da9adbfd8..da52a3a17058a 100644 --- a/extensions/undertow/deployment/src/main/java/io/quarkus/undertow/deployment/UndertowBuildStep.java +++ b/extensions/undertow/deployment/src/main/java/io/quarkus/undertow/deployment/UndertowBuildStep.java @@ -77,9 +77,10 @@ import io.quarkus.arc.deployment.AdditionalBeanBuildItem; import io.quarkus.arc.deployment.BeanContainerBuildItem; -import io.quarkus.arc.deployment.ContextRegistrarBuildItem; +import io.quarkus.arc.deployment.ContextRegistrationPhaseBuildItem; +import io.quarkus.arc.deployment.ContextRegistrationPhaseBuildItem.ContextConfiguratorBuildItem; +import io.quarkus.arc.deployment.CustomScopeBuildItem; import io.quarkus.arc.deployment.SyntheticBeanBuildItem; -import io.quarkus.arc.processor.ContextRegistrar; import io.quarkus.deployment.Capabilities; import io.quarkus.deployment.Capability; import io.quarkus.deployment.Feature; @@ -182,22 +183,26 @@ public ServiceStartBuildItem boot(UndertowDeploymentRecorder recorder, @BuildStep void integrateCdi(BuildProducer additionalBeans, - BuildProducer contextRegistrars, BuildProducer listeners, Capabilities capabilities) { additionalBeans.produce(new AdditionalBeanBuildItem(ServletProducer.class)); if (capabilities.isPresent(Capability.SECURITY)) { additionalBeans.produce(AdditionalBeanBuildItem.unremovableOf(ServletHttpSecurityPolicy.class)); } - contextRegistrars.produce(new ContextRegistrarBuildItem(new ContextRegistrar() { - @Override - public void register(RegistrationContext registrationContext) { - registrationContext.configure(SessionScoped.class).normal().contextClass(HttpSessionContext.class).done(); - } - }, SessionScoped.class)); listeners.produce(new ListenerBuildItem(HttpSessionContext.class.getName())); } + @BuildStep + ContextConfiguratorBuildItem registerContext(ContextRegistrationPhaseBuildItem phase) { + return new ContextConfiguratorBuildItem( + phase.getContext().configure(SessionScoped.class).normal().contextClass(HttpSessionContext.class)); + } + + @BuildStep + CustomScopeBuildItem customScope() { + return new CustomScopeBuildItem(DotName.createSimple(SessionScoped.class.getName())); + } + /** * Register the undertow-handlers.conf file */ diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanConfiguratorBase.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanConfiguratorBase.java index 99d157f194475..c5f30c1e8b1b4 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanConfiguratorBase.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanConfiguratorBase.java @@ -2,6 +2,7 @@ import io.quarkus.arc.BeanCreator; import io.quarkus.arc.BeanDestroyer; +import io.quarkus.arc.InjectableReferenceProvider; import io.quarkus.gizmo.FieldDescriptor; import io.quarkus.gizmo.MethodCreator; import io.quarkus.gizmo.MethodDescriptor; @@ -205,6 +206,16 @@ public B param(String name, boolean value) { return self(); } + /** + * The provider type is the "real" type of the bean instance created via + * {@link InjectableReferenceProvider#get(CreationalContext)}. + *

+ * The container attempts to derive the provider type from the implementation class. However, in some cases it's better to + * specify it manually. + * + * @param providerType + * @return self + */ public B providerType(Type providerType) { this.providerType = providerType; return self(); diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java index 53e74efad35c0..b4f8050b01601 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java @@ -78,7 +78,7 @@ public class BeanDeployment { private final List observers; - private final BeanResolver beanResolver; + final BeanResolverImpl beanResolver; private final InterceptorResolver interceptorResolver; @@ -166,7 +166,7 @@ public class BeanDeployment { this.beans = new CopyOnWriteArrayList<>(); this.observers = new CopyOnWriteArrayList<>(); - this.beanResolver = new BeanResolver(this); + this.beanResolver = new BeanResolverImpl(this); this.interceptorResolver = new InterceptorResolver(this); this.transformUnproxyableClasses = builder.transformUnproxyableClasses; this.jtaCapabilities = builder.jtaCapabilities; @@ -224,7 +224,8 @@ BeanRegistrar.RegistrationContext registerBeans(List beanRegistra return registerSyntheticBeans(beanRegistrars, buildContext); } - void init(Consumer bytecodeTransformerConsumer) { + void init(Consumer bytecodeTransformerConsumer, + List> additionalUnusedBeanExclusions) { long start = System.currentTimeMillis(); // Collect dependency resolution errors @@ -239,6 +240,10 @@ void init(Consumer bytecodeTransformerConsumer) { interceptor.init(errors, bytecodeTransformerConsumer, transformUnproxyableClasses); } processErrors(errors); + List> allUnusedExclusions = new ArrayList<>(additionalUnusedBeanExclusions); + if (unusedExclusions != null) { + allUnusedExclusions.addAll(unusedExclusions); + } if (removeUnusedBeans) { long removalStart = System.currentTimeMillis(); @@ -264,7 +269,7 @@ void init(Consumer bytecodeTransformerConsumer) { continue test; } // Custom exclusions - for (Predicate exclusion : unusedExclusions) { + for (Predicate exclusion : allUnusedExclusions) { if (exclusion.test(bean)) { continue test; } @@ -355,12 +360,16 @@ public Collection getInterceptorBindings() { return Collections.unmodifiableCollection(interceptorBindings.values()); } + public Collection getInjectionPoints() { + return Collections.unmodifiableList(injectionPoints); + } + public Collection getObservers() { - return observers; + return Collections.unmodifiableList(observers); } public Collection getInterceptors() { - return interceptors; + return Collections.unmodifiableList(interceptors); } /** @@ -383,12 +392,12 @@ public IndexView getApplicationIndex() { return applicationIndex; } - boolean hasApplicationIndex() { - return applicationIndex != null; + public BeanResolver getBeanResolver() { + return beanResolver; } - BeanResolver getBeanResolver() { - return beanResolver; + boolean hasApplicationIndex() { + return applicationIndex != null; } public InterceptorResolver getInterceptorResolver() { @@ -423,6 +432,10 @@ Collection extractQualifiers(AnnotationInstance annotation) } } + BeanResolverImpl beanResolver() { + return beanResolver; + } + ClassInfo getInterceptorBinding(DotName name) { return interceptorBindings.get(name); } @@ -451,7 +464,7 @@ public Collection getAnnotations(AnnotationTarget target) { return annotationStore.getAnnotations(target); } - AnnotationInstance getAnnotation(AnnotationTarget target, DotName name) { + public AnnotationInstance getAnnotation(AnnotationTarget target, DotName name) { return annotationStore.getAnnotation(target, name); } diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeploymentValidator.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeploymentValidator.java index b4b1f7cdc62b9..3a22329e6f121 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeploymentValidator.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeploymentValidator.java @@ -1,5 +1,6 @@ package io.quarkus.arc.processor; +import java.util.Collection; import java.util.List; import javax.enterprise.inject.spi.DeploymentException; @@ -40,6 +41,10 @@ interface ValidationContext extends BuildContext { */ BeanStream removedBeans(); + default Collection getInjectionPoints() { + return get(BuildExtension.Key.INJECTION_POINTS); + } + } } diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanInfo.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanInfo.java index 2569f451be452..c66364b69a128 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanInfo.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanInfo.java @@ -29,8 +29,7 @@ import org.jboss.jandex.Type; /** - * - * @author Martin Kouba + * Represents a CDI bean at build time. */ public class BeanInfo implements InjectionTargetInfo { @@ -339,6 +338,22 @@ public boolean isDefaultBean() { return defaultBean; } + /** + * @param requiredType + * @param requiredQualifiers + * @return {@code true} if this bean is assignable to the required type and qualifiers + */ + public boolean isAssignableTo(Type requiredType, AnnotationInstance... requiredQualifiers) { + Set qualifiers; + if (requiredQualifiers.length == 0) { + qualifiers = Collections.emptySet(); + } else { + qualifiers = new HashSet<>(); + Collections.addAll(qualifiers, requiredQualifiers); + } + return Beans.matches(this, requiredType, qualifiers); + } + Consumer getCreatorConsumer() { return creatorConsumer; } diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanProcessor.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanProcessor.java index 9873e75080441..bdcc012b75581 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanProcessor.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanProcessor.java @@ -122,9 +122,11 @@ public ObserverRegistrar.RegistrationContext registerSyntheticObservers() { /** * * @param bytecodeTransformerConsumer Used to register a bytecode transformation + * @param additionalUnusedBeanExclusions Additional predicates to exclude unused beans */ - public void initialize(Consumer bytecodeTransformerConsumer) { - beanDeployment.init(bytecodeTransformerConsumer); + public void initialize(Consumer bytecodeTransformerConsumer, + List> additionalUnusedBeanExclusions) { + beanDeployment.init(bytecodeTransformerConsumer, additionalUnusedBeanExclusions); } /** @@ -242,7 +244,7 @@ public void accept(BytecodeTransformer transformer) { registerScopes(); registerBeans(); registerSyntheticObservers(); - initialize(unsupportedBytecodeTransformer); + initialize(unsupportedBytecodeTransformer, Collections.emptyList()); ValidationContext validationContext = validate(unsupportedBytecodeTransformer); processValidationErrors(validationContext); generateResources(null, new HashSet<>(), unsupportedBytecodeTransformer, true); @@ -444,7 +446,7 @@ public void setAllowMocking(boolean allowMocking) { * * * @param removeUnusedBeans - * @return + * @return self */ public Builder setRemoveUnusedBeans(boolean removeUnusedBeans) { this.removeUnusedBeans = removeUnusedBeans; @@ -452,13 +454,14 @@ public Builder setRemoveUnusedBeans(boolean removeUnusedBeans) { } /** + * Exclude unused beans that match the given predicate from removal. * - * @param exclusion + * @param predicate * @return self * @see #setRemoveUnusedBeans(boolean) */ - public Builder addRemovalExclusion(Predicate exclusion) { - this.removalExclusions.add(exclusion); + public Builder addRemovalExclusion(Predicate predicate) { + this.removalExclusions.add(predicate); return this; } diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanRegistrar.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanRegistrar.java index 03c0ad2366ff0..53c0057364a16 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanRegistrar.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanRegistrar.java @@ -3,7 +3,7 @@ import org.jboss.jandex.DotName; /** - * Allows a build-time extension to register synthetic beans and observers. + * Allows a build-time extension to register synthetic beans. * * @author Martin Kouba */ diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanResolver.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanResolver.java index 247b9a194d5bb..171b28a69d3b0 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanResolver.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanResolver.java @@ -1,312 +1,33 @@ package io.quarkus.arc.processor; -import static java.util.Collections.singletonList; -import static org.jboss.jandex.Type.Kind.ARRAY; -import static org.jboss.jandex.Type.Kind.CLASS; -import static org.jboss.jandex.Type.Kind.PARAMETERIZED_TYPE; -import static org.jboss.jandex.Type.Kind.TYPE_VARIABLE; -import static org.jboss.jandex.Type.Kind.WILDCARD_TYPE; - -import io.quarkus.arc.processor.InjectionPointInfo.TypeAndQualifiers; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.function.Function; -import org.jboss.jandex.ClassInfo; -import org.jboss.jandex.ClassType; -import org.jboss.jandex.DotName; +import javax.enterprise.inject.AmbiguousResolutionException; +import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.Type; -import org.jboss.jandex.Type.Kind; -import org.jboss.jandex.TypeVariable; -import org.jboss.jandex.WildcardType; /** * Implements type-safe resolution rules. */ -class BeanResolver { - - private final BeanDeployment beanDeployment; - - private final ConcurrentMap> assignableFromMap; - - private final Function> assignableFromMapFunction; - - private final Map> resolved; - - public BeanResolver(BeanDeployment beanDeployment) { - this.beanDeployment = beanDeployment; - this.assignableFromMap = new ConcurrentHashMap<>(); - this.assignableFromMapFunction = name -> { - Set assignables = new HashSet<>(); - for (ClassInfo subclass : beanDeployment.getBeanArchiveIndex().getAllKnownSubclasses(name)) { - assignables.add(subclass.name()); - } - for (ClassInfo implementor : beanDeployment.getBeanArchiveIndex().getAllKnownImplementors(name)) { - assignables.add(implementor.name()); - } - if (beanDeployment.hasApplicationIndex()) { - for (ClassInfo subclass : beanDeployment.getApplicationIndex().getAllKnownSubclasses(name)) { - assignables.add(subclass.name()); - } - for (ClassInfo implementor : beanDeployment.getApplicationIndex().getAllKnownImplementors(name)) { - assignables.add(implementor.name()); - } - } - return assignables; - }; - this.resolved = new ConcurrentHashMap<>(); - } - - List resolve(TypeAndQualifiers typeAndQualifiers) { - return resolved.computeIfAbsent(typeAndQualifiers, this::findMatching); - } - - private List findMatching(TypeAndQualifiers typeAndQualifiers) { - List resolved = new ArrayList<>(); - for (BeanInfo b : beanDeployment.getBeans()) { - if (Beans.matches(b, typeAndQualifiers)) { - resolved.add(b); - } - } - return resolved.isEmpty() ? Collections.emptyList() : resolved; - } - - List findTypeMatching(Type type) { - List resolved = new ArrayList<>(); - for (BeanInfo b : beanDeployment.getBeans()) { - if (Beans.matchesType(b, type)) { - resolved.add(b); - } - } - return resolved.isEmpty() ? Collections.emptyList() : resolved; - } - - boolean matches(Type requiredType, Type beanType) { - return matchesNoBoxing(Types.box(requiredType), Types.box(beanType)); - } - - boolean matchesNoBoxing(Type requiredType, Type beanType) { - if (requiredType == beanType) { - return true; - } - - if (ARRAY.equals(requiredType.kind())) { - if (ARRAY.equals(beanType.kind())) { - // Array types are considered to match only if their element types are identical - return matchesNoBoxing(requiredType.asArrayType().component(), beanType.asArrayType().component()); - } - } else if (CLASS.equals(requiredType.kind())) { - if (CLASS.equals(beanType.kind())) { - return requiredType.name().equals(beanType.name()); - } else if (PARAMETERIZED_TYPE.equals(beanType.kind())) { - // A parameterized bean type is considered assignable to a raw required type if the raw types - // are identical and all type parameters of the bean type are either unbounded type variables or - // java.lang.Object. - if (!requiredType.name().equals(beanType.asParameterizedType().name())) { - return false; - } - return containsUnboundedTypeVariablesOrObjects(beanType.asParameterizedType().arguments()); - } - } else if (PARAMETERIZED_TYPE.equals(requiredType.kind())) { - if (CLASS.equals(beanType.kind())) { - // A raw bean type is considered assignable to a parameterized required type if the raw types are - // identical and all type parameters of the required type are either unbounded type variables or - // java.lang.Object. - if (!beanType.name().equals(requiredType.asParameterizedType().name())) { - return false; - } - return containsUnboundedTypeVariablesOrObjects(requiredType.asParameterizedType().arguments()); - } else if (PARAMETERIZED_TYPE.equals(beanType.kind())) { - // A parameterized bean type is considered assignable to a parameterized required type if they have - // identical raw type and for each parameter. - if (!requiredType.name().equals(beanType.name())) { - return false; - } - List requiredTypeArguments = requiredType.asParameterizedType().arguments(); - List beanTypeArguments = beanType.asParameterizedType().arguments(); - if (requiredTypeArguments.size() != beanTypeArguments.size()) { - throw new IllegalArgumentException("Invalid argument combination " + requiredType + "; " + beanType); - } - for (int i = 0; i < requiredTypeArguments.size(); i++) { - if (!parametersMatch(requiredTypeArguments.get(i), beanTypeArguments.get(i))) { - return false; - } - } - return true; - } - } else if (WILDCARD_TYPE.equals(requiredType.kind())) { - return parametersMatch(requiredType, beanType); - } - return false; - } - - boolean parametersMatch(Type requiredParameter, Type beanParameter) { - if (isActualType(requiredParameter) && isActualType(beanParameter)) { - /* - * the required type parameter and the bean type parameter are actual types with identical raw type, and, if the - * type is parameterized, the bean - * type parameter is assignable to the required type parameter according to these rules, or - */ - return matches(requiredParameter, beanParameter); - } - if (WILDCARD_TYPE.equals(requiredParameter.kind()) && isActualType(beanParameter)) { - /* - * the required type parameter is a wildcard, the bean type parameter is an actual type and the actual type is - * assignable to the upper bound, if - * any, of the wildcard and assignable from the lower bound, if any, of the wildcard, or - */ - return parametersMatch(requiredParameter.asWildcardType(), beanParameter); - } - if (WILDCARD_TYPE.equals(requiredParameter.kind()) && TYPE_VARIABLE.equals(beanParameter.kind())) { - /* - * the required type parameter is a wildcard, the bean type parameter is a type variable and the upper bound of the - * type variable is assignable to - * or assignable from the upper bound, if any, of the wildcard and assignable from the lower bound, if any, of the - * wildcard, or - */ - return parametersMatch(requiredParameter.asWildcardType(), beanParameter.asTypeVariable()); - } - if (isActualType(requiredParameter) && TYPE_VARIABLE.equals(beanParameter.kind())) { - /* - * the required type parameter is an actual type, the bean type parameter is a type variable and the actual type is - * assignable to the upper bound, - * if any, of the type variable, or - */ - return parametersMatch(requiredParameter, beanParameter.asTypeVariable()); - } - if (TYPE_VARIABLE.equals(requiredParameter.kind()) && TYPE_VARIABLE.equals(beanParameter.kind())) { - /* - * the required type parameter and the bean type parameter are both type variables and the upper bound of the - * required type parameter is assignable - * to the upper bound, if any, of the bean type parameter - */ - return parametersMatch(requiredParameter.asTypeVariable(), beanParameter.asTypeVariable()); - } - return false; - } - - boolean parametersMatch(WildcardType requiredParameter, Type beanParameter) { - return (lowerBoundsOfWildcardMatch(beanParameter, requiredParameter) - && upperBoundsOfWildcardMatch(requiredParameter, beanParameter)); - } - - boolean parametersMatch(WildcardType requiredParameter, TypeVariable beanParameter) { - List beanParameterBounds = getUppermostTypeVariableBounds(beanParameter); - if (!lowerBoundsOfWildcardMatch(beanParameterBounds, requiredParameter)) { - return false; - } - - List requiredUpperBounds = Collections.singletonList(requiredParameter.extendsBound()); - // upper bound of the type variable is assignable to OR assignable from the upper bound of the wildcard - return (boundsMatch(requiredUpperBounds, beanParameterBounds) || boundsMatch(beanParameterBounds, requiredUpperBounds)); - } - - boolean parametersMatch(Type requiredParameter, TypeVariable beanParameter) { - for (Type bound : getUppermostTypeVariableBounds(beanParameter)) { - if (!isAssignableFrom(bound, requiredParameter)) { - return false; - } - } - return true; - } - - boolean parametersMatch(TypeVariable requiredParameter, TypeVariable beanParameter) { - return boundsMatch(getUppermostTypeVariableBounds(beanParameter), getUppermostTypeVariableBounds(requiredParameter)); - } +public interface BeanResolver { /** - * Returns true iff for each bound T, there is at least one bound from stricterBounds assignable to T. - * This reflects that - * stricterBounds are at least as strict as bounds are. + * Note that this method does not attempt to resolve the ambiguity. + * + * @param requiredType + * @param requiredQualifiers + * @return the set of beans which have the given required type and qualifiers + * @see #resolveAmbiguity(Set) */ - boolean boundsMatch(List bounds, List stricterBounds) { - // getUppermostBounds to make sure that both arrays of bounds contain ONLY ACTUAL TYPES! otherwise, the CovariantTypes - // assignability rules do not reflect our needs - bounds = getUppermostBounds(bounds); - stricterBounds = getUppermostBounds(stricterBounds); - for (Type bound : bounds) { - for (Type stricterBound : stricterBounds) { - if (!isAssignableFrom(bound, stricterBound)) { - return false; - } - } - } - return true; - } - - boolean isAssignableFrom(Type type1, Type type2) { - // java.lang.Object is assignable from any type - if (type1.name().equals(DotNames.OBJECT)) { - return true; - } - // type1 is the same as type2 - if (type1.name().equals(type2.name())) { - return true; - } - // type1 is a superclass - return assignableFromMap.computeIfAbsent(type1.name(), assignableFromMapFunction).contains(type2.name()); - } - - boolean lowerBoundsOfWildcardMatch(Type parameter, WildcardType requiredParameter) { - return lowerBoundsOfWildcardMatch(singletonList(parameter), requiredParameter); - } + Set resolveBeans(Type requiredType, AnnotationInstance... requiredQualifiers); - boolean lowerBoundsOfWildcardMatch(List beanParameterBounds, WildcardType requiredParameter) { - if (requiredParameter.superBound() != null) { - if (!boundsMatch(beanParameterBounds, singletonList(requiredParameter.superBound()))) { - return false; - } - } - return true; - } - - boolean upperBoundsOfWildcardMatch(WildcardType requiredParameter, Type parameter) { - return boundsMatch(singletonList(requiredParameter.extendsBound()), singletonList(parameter)); - } - - /* - * TypeVariable bounds are treated specially - CDI assignability rules are applied. Standard Java covariant assignability - * rules are applied to all other - * types of bounds. This is not explicitly mentioned in the specification but is implied. + /** + * Apply the ambiguous dependency resolution rules. + * + * @param beans + * @return the resolved bean, or null + * @throws AmbiguousResolutionException + * @see {@link #resolveBeans(Type, AnnotationInstance...)} */ - List getUppermostTypeVariableBounds(TypeVariable bound) { - if (TYPE_VARIABLE.equals(bound.bounds().get(0).kind())) { - return getUppermostTypeVariableBounds(bound.bounds().get(0).asTypeVariable()); - } - return bound.bounds(); - } - - List getUppermostBounds(List bounds) { - // if a type variable (or wildcard) declares a bound which is a type variable, it can declare no other bound - if (TYPE_VARIABLE.equals(bounds.get(0).kind())) { - return getUppermostTypeVariableBounds(bounds.get(0).asTypeVariable()); - } - return bounds; - } - - static boolean isActualType(Type type) { - return CLASS.equals(type.kind()) || PARAMETERIZED_TYPE.equals(type.kind()) || ARRAY.equals(type.kind()); - } - - static boolean containsUnboundedTypeVariablesOrObjects(List types) { - for (Type type : types) { - if (ClassType.OBJECT_TYPE.equals(type)) { - continue; - } - if (Kind.TYPE_VARIABLE.equals(type.kind())) { - List bounds = type.asTypeVariable().bounds(); - if (bounds.isEmpty() || bounds.size() == 1 && ClassType.OBJECT_TYPE.equals(bounds.get(0))) { - continue; - } - } - return false; - } - return true; - } + BeanInfo resolveAmbiguity(Set beans); } diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanResolverImpl.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanResolverImpl.java new file mode 100644 index 0000000000000..90d60969b1d27 --- /dev/null +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanResolverImpl.java @@ -0,0 +1,352 @@ +package io.quarkus.arc.processor; + +import static java.util.Collections.singletonList; +import static org.jboss.jandex.Type.Kind.ARRAY; +import static org.jboss.jandex.Type.Kind.CLASS; +import static org.jboss.jandex.Type.Kind.PARAMETERIZED_TYPE; +import static org.jboss.jandex.Type.Kind.TYPE_VARIABLE; +import static org.jboss.jandex.Type.Kind.WILDCARD_TYPE; + +import io.quarkus.arc.processor.InjectionPointInfo.TypeAndQualifiers; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.function.Function; +import javax.enterprise.inject.AmbiguousResolutionException; +import org.jboss.jandex.AnnotationInstance; +import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.ClassType; +import org.jboss.jandex.DotName; +import org.jboss.jandex.Type; +import org.jboss.jandex.Type.Kind; +import org.jboss.jandex.TypeVariable; +import org.jboss.jandex.WildcardType; + +class BeanResolverImpl implements BeanResolver { + + private final BeanDeployment beanDeployment; + + private final ConcurrentMap> assignableFromMap; + + private final Function> assignableFromMapFunction; + + private final Map> resolved; + + BeanResolverImpl(BeanDeployment beanDeployment) { + this.beanDeployment = beanDeployment; + this.assignableFromMap = new ConcurrentHashMap<>(); + this.assignableFromMapFunction = name -> { + Set assignables = new HashSet<>(); + for (ClassInfo subclass : beanDeployment.getBeanArchiveIndex().getAllKnownSubclasses(name)) { + assignables.add(subclass.name()); + } + for (ClassInfo implementor : beanDeployment.getBeanArchiveIndex().getAllKnownImplementors(name)) { + assignables.add(implementor.name()); + } + if (beanDeployment.hasApplicationIndex()) { + for (ClassInfo subclass : beanDeployment.getApplicationIndex().getAllKnownSubclasses(name)) { + assignables.add(subclass.name()); + } + for (ClassInfo implementor : beanDeployment.getApplicationIndex().getAllKnownImplementors(name)) { + assignables.add(implementor.name()); + } + } + return assignables; + }; + this.resolved = new ConcurrentHashMap<>(); + } + + @Override + public Set resolveBeans(Type requiredType, AnnotationInstance... requiredQualifiers) { + Objects.requireNonNull(requiredType, "Required type must not be null"); + Set qualifiers; + if (requiredQualifiers.length == 0) { + qualifiers = Collections.emptySet(); + } else { + qualifiers = new HashSet<>(); + Collections.addAll(qualifiers, requiredQualifiers); + } + TypeAndQualifiers typeAndQualifiers = new TypeAndQualifiers(requiredType, qualifiers); + // Note that this method must not cache the results beacause it can be used before synthetic components are registered + List beans = findMatching(typeAndQualifiers); + Set ret; + if (beans.isEmpty()) { + ret = Collections.emptySet(); + } else if (beans.size() == 1) { + ret = Collections.singleton(beans.get(0)); + } else { + ret = new HashSet<>(beans); + } + return ret; + } + + @Override + public BeanInfo resolveAmbiguity(Set beans) { + if (beans == null || beans.isEmpty()) { + return null; + } + if (beans.size() > 1) { + BeanInfo selected = Beans.resolveAmbiguity(beans); + if (selected != null) { + return selected; + } + throw new AmbiguousResolutionException(beans.toString()); + } else { + return beans.iterator().next(); + } + } + + List resolve(TypeAndQualifiers typeAndQualifiers) { + return resolved.computeIfAbsent(typeAndQualifiers, this::findMatching); + } + + private List findMatching(TypeAndQualifiers typeAndQualifiers) { + List resolved = new ArrayList<>(); + for (BeanInfo b : beanDeployment.getBeans()) { + if (Beans.matches(b, typeAndQualifiers)) { + resolved.add(b); + } + } + return resolved.isEmpty() ? Collections.emptyList() : resolved; + } + + List findTypeMatching(Type type) { + List resolved = new ArrayList<>(); + for (BeanInfo b : beanDeployment.getBeans()) { + if (Beans.matchesType(b, type)) { + resolved.add(b); + } + } + return resolved.isEmpty() ? Collections.emptyList() : resolved; + } + + boolean matches(Type requiredType, Type beanType) { + return matchesNoBoxing(Types.box(requiredType), Types.box(beanType)); + } + + boolean matchesNoBoxing(Type requiredType, Type beanType) { + if (requiredType == beanType) { + return true; + } + + if (ARRAY.equals(requiredType.kind())) { + if (ARRAY.equals(beanType.kind())) { + // Array types are considered to match only if their element types are identical + return matchesNoBoxing(requiredType.asArrayType().component(), beanType.asArrayType().component()); + } + } else if (CLASS.equals(requiredType.kind())) { + if (CLASS.equals(beanType.kind())) { + return requiredType.name().equals(beanType.name()); + } else if (PARAMETERIZED_TYPE.equals(beanType.kind())) { + // A parameterized bean type is considered assignable to a raw required type if the raw types + // are identical and all type parameters of the bean type are either unbounded type variables or + // java.lang.Object. + if (!requiredType.name().equals(beanType.asParameterizedType().name())) { + return false; + } + return containsUnboundedTypeVariablesOrObjects(beanType.asParameterizedType().arguments()); + } + } else if (PARAMETERIZED_TYPE.equals(requiredType.kind())) { + if (CLASS.equals(beanType.kind())) { + // A raw bean type is considered assignable to a parameterized required type if the raw types are + // identical and all type parameters of the required type are either unbounded type variables or + // java.lang.Object. + if (!beanType.name().equals(requiredType.asParameterizedType().name())) { + return false; + } + return containsUnboundedTypeVariablesOrObjects(requiredType.asParameterizedType().arguments()); + } else if (PARAMETERIZED_TYPE.equals(beanType.kind())) { + // A parameterized bean type is considered assignable to a parameterized required type if they have + // identical raw type and for each parameter. + if (!requiredType.name().equals(beanType.name())) { + return false; + } + List requiredTypeArguments = requiredType.asParameterizedType().arguments(); + List beanTypeArguments = beanType.asParameterizedType().arguments(); + if (requiredTypeArguments.size() != beanTypeArguments.size()) { + throw new IllegalArgumentException("Invalid argument combination " + requiredType + "; " + beanType); + } + for (int i = 0; i < requiredTypeArguments.size(); i++) { + if (!parametersMatch(requiredTypeArguments.get(i), beanTypeArguments.get(i))) { + return false; + } + } + return true; + } + } else if (WILDCARD_TYPE.equals(requiredType.kind())) { + return parametersMatch(requiredType, beanType); + } + return false; + } + + boolean parametersMatch(Type requiredParameter, Type beanParameter) { + if (isActualType(requiredParameter) && isActualType(beanParameter)) { + /* + * the required type parameter and the bean type parameter are actual types with identical raw type, and, if the + * type is parameterized, the bean + * type parameter is assignable to the required type parameter according to these rules, or + */ + return matches(requiredParameter, beanParameter); + } + if (WILDCARD_TYPE.equals(requiredParameter.kind()) && isActualType(beanParameter)) { + /* + * the required type parameter is a wildcard, the bean type parameter is an actual type and the actual type is + * assignable to the upper bound, if + * any, of the wildcard and assignable from the lower bound, if any, of the wildcard, or + */ + return parametersMatch(requiredParameter.asWildcardType(), beanParameter); + } + if (WILDCARD_TYPE.equals(requiredParameter.kind()) && TYPE_VARIABLE.equals(beanParameter.kind())) { + /* + * the required type parameter is a wildcard, the bean type parameter is a type variable and the upper bound of the + * type variable is assignable to + * or assignable from the upper bound, if any, of the wildcard and assignable from the lower bound, if any, of the + * wildcard, or + */ + return parametersMatch(requiredParameter.asWildcardType(), beanParameter.asTypeVariable()); + } + if (isActualType(requiredParameter) && TYPE_VARIABLE.equals(beanParameter.kind())) { + /* + * the required type parameter is an actual type, the bean type parameter is a type variable and the actual type is + * assignable to the upper bound, + * if any, of the type variable, or + */ + return parametersMatch(requiredParameter, beanParameter.asTypeVariable()); + } + if (TYPE_VARIABLE.equals(requiredParameter.kind()) && TYPE_VARIABLE.equals(beanParameter.kind())) { + /* + * the required type parameter and the bean type parameter are both type variables and the upper bound of the + * required type parameter is assignable + * to the upper bound, if any, of the bean type parameter + */ + return parametersMatch(requiredParameter.asTypeVariable(), beanParameter.asTypeVariable()); + } + return false; + } + + boolean parametersMatch(WildcardType requiredParameter, Type beanParameter) { + return (lowerBoundsOfWildcardMatch(beanParameter, requiredParameter) + && upperBoundsOfWildcardMatch(requiredParameter, beanParameter)); + } + + boolean parametersMatch(WildcardType requiredParameter, TypeVariable beanParameter) { + List beanParameterBounds = getUppermostTypeVariableBounds(beanParameter); + if (!lowerBoundsOfWildcardMatch(beanParameterBounds, requiredParameter)) { + return false; + } + + List requiredUpperBounds = Collections.singletonList(requiredParameter.extendsBound()); + // upper bound of the type variable is assignable to OR assignable from the upper bound of the wildcard + return (boundsMatch(requiredUpperBounds, beanParameterBounds) || boundsMatch(beanParameterBounds, requiredUpperBounds)); + } + + boolean parametersMatch(Type requiredParameter, TypeVariable beanParameter) { + for (Type bound : getUppermostTypeVariableBounds(beanParameter)) { + if (!isAssignableFrom(bound, requiredParameter)) { + return false; + } + } + return true; + } + + boolean parametersMatch(TypeVariable requiredParameter, TypeVariable beanParameter) { + return boundsMatch(getUppermostTypeVariableBounds(beanParameter), getUppermostTypeVariableBounds(requiredParameter)); + } + + /** + * Returns true iff for each bound T, there is at least one bound from stricterBounds assignable to T. + * This reflects that + * stricterBounds are at least as strict as bounds are. + */ + boolean boundsMatch(List bounds, List stricterBounds) { + // getUppermostBounds to make sure that both arrays of bounds contain ONLY ACTUAL TYPES! otherwise, the CovariantTypes + // assignability rules do not reflect our needs + bounds = getUppermostBounds(bounds); + stricterBounds = getUppermostBounds(stricterBounds); + for (Type bound : bounds) { + for (Type stricterBound : stricterBounds) { + if (!isAssignableFrom(bound, stricterBound)) { + return false; + } + } + } + return true; + } + + boolean isAssignableFrom(Type type1, Type type2) { + // java.lang.Object is assignable from any type + if (type1.name().equals(DotNames.OBJECT)) { + return true; + } + // type1 is the same as type2 + if (type1.name().equals(type2.name())) { + return true; + } + // type1 is a superclass + return assignableFromMap.computeIfAbsent(type1.name(), assignableFromMapFunction).contains(type2.name()); + } + + boolean lowerBoundsOfWildcardMatch(Type parameter, WildcardType requiredParameter) { + return lowerBoundsOfWildcardMatch(singletonList(parameter), requiredParameter); + } + + boolean lowerBoundsOfWildcardMatch(List beanParameterBounds, WildcardType requiredParameter) { + if (requiredParameter.superBound() != null) { + if (!boundsMatch(beanParameterBounds, singletonList(requiredParameter.superBound()))) { + return false; + } + } + return true; + } + + boolean upperBoundsOfWildcardMatch(WildcardType requiredParameter, Type parameter) { + return boundsMatch(singletonList(requiredParameter.extendsBound()), singletonList(parameter)); + } + + /* + * TypeVariable bounds are treated specially - CDI assignability rules are applied. Standard Java covariant assignability + * rules are applied to all other + * types of bounds. This is not explicitly mentioned in the specification but is implied. + */ + List getUppermostTypeVariableBounds(TypeVariable bound) { + if (TYPE_VARIABLE.equals(bound.bounds().get(0).kind())) { + return getUppermostTypeVariableBounds(bound.bounds().get(0).asTypeVariable()); + } + return bound.bounds(); + } + + List getUppermostBounds(List bounds) { + // if a type variable (or wildcard) declares a bound which is a type variable, it can declare no other bound + if (TYPE_VARIABLE.equals(bounds.get(0).kind())) { + return getUppermostTypeVariableBounds(bounds.get(0).asTypeVariable()); + } + return bounds; + } + + static boolean isActualType(Type type) { + return CLASS.equals(type.kind()) || PARAMETERIZED_TYPE.equals(type.kind()) || ARRAY.equals(type.kind()); + } + + static boolean containsUnboundedTypeVariablesOrObjects(List types) { + for (Type type : types) { + if (ClassType.OBJECT_TYPE.equals(type)) { + continue; + } + if (Kind.TYPE_VARIABLE.equals(type.kind())) { + List bounds = type.asTypeVariable().bounds(); + if (bounds.isEmpty() || bounds.size() == 1 && ClassType.OBJECT_TYPE.equals(bounds.get(0))) { + continue; + } + } + return false; + } + return true; + } + +} diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanStream.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanStream.java index baec444fb1926..d3d742e95318f 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanStream.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanStream.java @@ -10,6 +10,7 @@ import java.util.stream.Collector; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.DotName; import org.jboss.jandex.Type; @@ -256,6 +257,17 @@ public BeanStream alternativeBeans() { return this; } + /** + * + * @param requiredType + * @param requiredQualifiers + * @return the new stream of beans assignable to the required type and qualifiers + */ + public BeanStream assignableTo(Type requiredType, AnnotationInstance... requiredQualifiers) { + stream = stream.filter(bean -> bean.isAssignableTo(requiredType, requiredQualifiers)); + return this; + } + /** * Terminal operation. * diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Beans.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Beans.java index b9b006cb185ea..d52af6c17b476 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Beans.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Beans.java @@ -428,12 +428,16 @@ private static String initStereotypeName(List stereotypes, Annot } static boolean matches(BeanInfo bean, TypeAndQualifiers typeAndQualifiers) { - // Bean has all the required qualifiers and a bean type that matches the required type - return hasQualifiers(bean, typeAndQualifiers.qualifiers) && matchesType(bean, typeAndQualifiers.type); + return matches(bean, typeAndQualifiers.type, typeAndQualifiers.qualifiers); + } + + static boolean matches(BeanInfo bean, Type requiredType, Set requiredQualifiers) { + // Bean has all the required qualifiers and a bean type that matches the required type + return hasQualifiers(bean, requiredQualifiers) && matchesType(bean, requiredType); } static boolean matchesType(BeanInfo bean, Type requiredType) { - BeanResolver beanResolver = bean.getDeployment().getBeanResolver(); + BeanResolverImpl beanResolver = bean.getDeployment().beanResolver; for (Type beanType : bean.getTypes()) { if (beanResolver.matches(requiredType, beanType)) { return true; @@ -459,10 +463,10 @@ static void resolveInjectionPoint(BeanDeployment deployment, InjectionTargetInfo // Skip built-in beans return; } - List resolved = deployment.getBeanResolver().resolve(injectionPoint.getTypeAndQualifiers()); + List resolved = deployment.beanResolver.resolve(injectionPoint.getTypeAndQualifiers()); BeanInfo selected = null; if (resolved.isEmpty()) { - List typeMatching = deployment.getBeanResolver().findTypeMatching(injectionPoint.getRequiredType()); + List typeMatching = deployment.beanResolver.findTypeMatching(injectionPoint.getRequiredType()); StringBuilder message = new StringBuilder("Unsatisfied dependency for type "); addStandardErroneousDependencyMessage(target, injectionPoint, message); @@ -507,7 +511,7 @@ private static void addStandardErroneousDependencyMessage(InjectionTargetInfo ta message.append(target); } - static BeanInfo resolveAmbiguity(List resolved) { + static BeanInfo resolveAmbiguity(Collection resolved) { List resolvedAmbiguity = new ArrayList<>(resolved); // First eliminate default beans for (Iterator iterator = resolvedAmbiguity.iterator(); iterator.hasNext();) { diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BuildExtension.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BuildExtension.java index 0c24459d0b379..98eb601bc4f3c 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BuildExtension.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BuildExtension.java @@ -59,7 +59,7 @@ interface Key { static Key> SCOPES = new SimpleKey<>(BUILT_IN_PREFIX + "scopes"); static Key> QUALIFIERS = new SimpleKey<>(BUILT_IN_PREFIX + "qualifiers"); static Key> INTERCEPTOR_BINDINGS = new SimpleKey<>(BUILT_IN_PREFIX + "interceptorBindings"); - static Key> STEREOTYPES = new SimpleKey<>(BUILT_IN_PREFIX + "stereotypes"); + static Key> STEREOTYPES = new SimpleKey<>(BUILT_IN_PREFIX + "stereotypes"); String asString(); } diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/InterceptorResolver.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/InterceptorResolver.java index 9c7b677d5f07b..e98e0cb957476 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/InterceptorResolver.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/InterceptorResolver.java @@ -10,7 +10,7 @@ import org.jboss.jandex.AnnotationValue; import org.jboss.jandex.ClassInfo; -public class InterceptorResolver { +public final class InterceptorResolver { private final BeanDeployment beanDeployment; diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/buildextension/beans/BeanRegistrarTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/buildextension/beans/BeanRegistrarTest.java index 544126979f1dd..dec25178504ed 100644 --- a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/buildextension/beans/BeanRegistrarTest.java +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/buildextension/beans/BeanRegistrarTest.java @@ -27,6 +27,8 @@ import javax.enterprise.util.AnnotationLiteral; import javax.inject.Qualifier; import javax.inject.Singleton; +import org.jboss.jandex.DotName; +import org.jboss.jandex.Type; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -107,6 +109,13 @@ public void register(RegistrationContext context) { .creator(StringCreator.class).addQualifier().annotation(NextQualifier.class).addValue("name", "Roman") .addValue("age", 42) .addValue("classes", new Class[] { String.class }).done().unremovable().done(); + + uselessBean = context.beans() + .assignableTo( + Type.create(DotName.createSimple(UselessBean.class.getName()), org.jboss.jandex.Type.Kind.CLASS)) + .firstResult(); + assertTrue(uselessBean.isPresent()); + assertEquals(UselessBean.class.getName(), uselessBean.get().getBeanClass().toString()); } } From 656d604b42ac22dcb5fa023611390f14cf9e08ff Mon Sep 17 00:00:00 2001 From: George Gastaldi Date: Wed, 9 Dec 2020 12:47:05 -0300 Subject: [PATCH 35/62] Bump jib-core to 0.16.0 --- bom/application/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 06ec653f586c7..6c31d32cc22d1 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -191,7 +191,7 @@ 1.1.1 2.5.2 2.2.0 - 0.15.0 + 0.16.0 1.34.0 2.1 From f3ac461b76842a08b7e1e13549ab2bfdededa172 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Dec 2020 22:12:35 +0000 Subject: [PATCH 36/62] Bump awssdk.version from 2.15.38 to 2.15.43 Bumps `awssdk.version` from 2.15.38 to 2.15.43. Updates `software.amazon.awssdk:bom` from 2.15.38 to 2.15.43 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.15.38...2.15.43) Updates `apache-client` from 2.15.38 to 2.15.43 Signed-off-by: dependabot[bot] --- bom/application/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 6c31d32cc22d1..767328864fd8e 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -148,7 +148,7 @@ 3.6.0 1.3.1 2.4.0 - 2.15.38 + 2.15.43 2.37.1 1.3.0 1.4.20 From b1bdb56b59521d57f1e6e3dc6e3ace3b7259ca7f Mon Sep 17 00:00:00 2001 From: Stuart Douglas Date: Thu, 10 Dec 2020 16:06:26 +1100 Subject: [PATCH 37/62] Remove unused test injection code This is from back in 1.2 and has not been used in a long time. --- .../deployment/test/TestResourceProvider.java | 7 ------ .../test/common/TestInjectionManager.java | 25 ------------------- 2 files changed, 32 deletions(-) delete mode 100644 core/deployment/src/main/java/io/quarkus/deployment/test/TestResourceProvider.java delete mode 100644 test-framework/common/src/main/java/io/quarkus/test/common/TestInjectionManager.java diff --git a/core/deployment/src/main/java/io/quarkus/deployment/test/TestResourceProvider.java b/core/deployment/src/main/java/io/quarkus/deployment/test/TestResourceProvider.java deleted file mode 100644 index 606f910e9251c..0000000000000 --- a/core/deployment/src/main/java/io/quarkus/deployment/test/TestResourceProvider.java +++ /dev/null @@ -1,7 +0,0 @@ -package io.quarkus.deployment.test; - -public interface TestResourceProvider { - - void inject(Object test); - -} diff --git a/test-framework/common/src/main/java/io/quarkus/test/common/TestInjectionManager.java b/test-framework/common/src/main/java/io/quarkus/test/common/TestInjectionManager.java deleted file mode 100644 index d23ea9e354b74..0000000000000 --- a/test-framework/common/src/main/java/io/quarkus/test/common/TestInjectionManager.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.quarkus.test.common; - -import java.util.ArrayList; -import java.util.List; -import java.util.ServiceLoader; - -import io.quarkus.deployment.test.TestResourceProvider; - -public class TestInjectionManager { - - private static final List resourceProviders = new ArrayList<>(); - - static { - for (TestResourceProvider i : ServiceLoader.load(TestResourceProvider.class)) { - resourceProviders.add(i); - } - } - - public static void inject(Object test) { - for (TestResourceProvider i : resourceProviders) { - i.inject(test); - } - } - -} From d8be799f87e093c36155e5a35df84c355a84df63 Mon Sep 17 00:00:00 2001 From: Stuart Douglas Date: Fri, 27 Nov 2020 16:29:48 +1100 Subject: [PATCH 38/62] RESTEasy Reactive: Move more logic outside Quarkus This allows RR to have its own unit tests that do not require Quarkus to bootstrap, while also making the code more re-usable. --- .../deployment/JaxrsClientProcessor.java | 13 +- .../ApplicationResultBuildItem.java | 71 +--- .../common/deployment/FactoryUtils.java | 2 + .../QuarkusResteasyReactiveDotNames.java | 3 - .../ResourceInterceptorsBuildItem.java | 18 + ...ourceInterceptorsContributorBuildItem.java | 20 + .../ResourceScanningResultBuildItem.java | 63 +-- .../ResteasyReactiveCommonProcessor.java | 361 ++++++------------ .../common/deployment/SerializersUtil.java | 6 +- .../ResteasyReactiveJacksonProcessor.java | 4 +- .../deployment/ContextResolversBuildItem.java | 18 + .../deployment/ExceptionMappersBuildItem.java | 18 + .../ParamConverterProvidersBuildItem.java | 18 + .../QuarkusServerEndpointIndexer.java | 198 ++++++++++ .../ResteasyReactiveCDIProcessor.java | 33 +- .../deployment/ResteasyReactiveProcessor.java | 242 ++++-------- .../ResteasyReactiveScanningProcessor.java | 242 ++++++------ .../runtime/ResteasyReactiveRecorder.java | 10 +- .../common/processor/EndpointIndexer.java | 3 +- .../reactive/common/processor/JandexUtil.java | 33 +- .../processor/ResteasyReactiveDotNames.java | 3 + .../scanning/ApplicationScanningResult.java | 73 ++++ .../scanning/ResourceScanningResult.java | 66 ++++ .../ResteasyReactiveInterceptorScanner.java | 119 ++++++ .../scanning/ResteasyReactiveScanner.java | 220 +++++++++++ .../common/model/InterceptorContainer.java | 15 + .../model/PreMatchInterceptorContainer.java | 11 + .../common/model/ResourceContextResolver.java | 10 + .../common/model/ResourceExceptionMapper.java | 16 +- .../common/model/ResourceInterceptor.java | 11 + .../common/model/ResourceInterceptors.java | 9 + .../model/ResourceParamConverterProvider.java | 10 + .../reactive/common/model/ResourceWriter.java | 3 +- .../common/util/ReflectionBeanFactory.java | 32 ++ .../util/ReflectionBeanFactoryCreator.java | 12 + .../reactive/spi/ThreadSetupAction.java | 22 ++ .../resteasy-reactive/pom.xml | 50 +++ .../ReflectionEndpointInvokerFactory.java | 79 ++++ .../processor}/ServerEndpointIndexer.java | 291 ++++---------- ...esteasyReactiveContextResolverScanner.java | 78 ++++ ...steasyReactiveExceptionMappingScanner.java | 74 ++++ .../ResteasyReactiveFeatureScanner.java | 95 +++++ ...ResteasyReactiveParamConverterScanner.java | 56 +++ .../reactive/server/core/Deployment.java | 2 + .../reactive/server/core/DeploymentInfo.java | 4 + .../server/core/ExceptionMapping.java | 20 + .../parameters/converters/ListConverter.java | 2 +- .../converters/NoopParameterConverter.java | 2 +- .../converters/ParameterConverter.java | 2 +- .../converters/PathSegmentParamConverter.java | 2 +- .../converters/RuntimeParameterConverter.java | 2 +- .../converters/RuntimeResolvedConverter.java | 2 +- .../parameters/converters/SetConverter.java | 2 +- .../converters/SortedSetConverter.java | 2 +- .../startup/RuntimeDeploymentManager.java | 4 +- .../startup/RuntimeInterceptorDeployment.java | 2 +- .../startup/RuntimeResourceDeployment.java | 2 +- .../{core => model}/ContextResolvers.java | 18 +- .../{core => model}/DynamicFeatures.java | 5 +- .../server/{core => model}/Features.java | 2 +- .../ParamConverterProviders.java | 11 +- .../resteasy-reactive/server/vertx/pom.xml | 23 ++ .../vertx/ResteasyReactiveVertxHandler.java | 19 + .../vertx/VertxRequestContextFactory.java | 19 + .../server/vertx/test/HelloResource.java | 15 + .../test/SimpleVertxResteasyReactiveTest.java | 32 ++ .../test/framework/InMemoryLogHandler.java | 44 +++ .../framework/ResteasyReactiveUnitTest.java | 340 +++++++++++++++++ 68 files changed, 2368 insertions(+), 941 deletions(-) create mode 100644 extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ResourceInterceptorsBuildItem.java create mode 100644 extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ResourceInterceptorsContributorBuildItem.java create mode 100644 extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ContextResolversBuildItem.java create mode 100644 extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ExceptionMappersBuildItem.java create mode 100644 extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ParamConverterProvidersBuildItem.java create mode 100644 extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/QuarkusServerEndpointIndexer.java create mode 100644 independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/scanning/ApplicationScanningResult.java create mode 100644 independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/scanning/ResourceScanningResult.java create mode 100644 independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/scanning/ResteasyReactiveInterceptorScanner.java create mode 100644 independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/scanning/ResteasyReactiveScanner.java create mode 100644 independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/util/ReflectionBeanFactory.java create mode 100644 independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/util/ReflectionBeanFactoryCreator.java create mode 100644 independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/ReflectionEndpointInvokerFactory.java rename {extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment => independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor}/ServerEndpointIndexer.java (59%) create mode 100644 independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/scanning/ResteasyReactiveContextResolverScanner.java create mode 100644 independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/scanning/ResteasyReactiveExceptionMappingScanner.java create mode 100644 independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/scanning/ResteasyReactiveFeatureScanner.java create mode 100644 independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/scanning/ResteasyReactiveParamConverterScanner.java rename independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/{core => model}/ContextResolvers.java (82%) rename independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/{core => model}/DynamicFeatures.java (80%) rename independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/{core => model}/Features.java (89%) rename independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/{core => model}/ParamConverterProviders.java (58%) create mode 100644 independent-projects/resteasy-reactive/server/vertx/src/main/java/org/jboss/resteasy/reactive/server/vertx/ResteasyReactiveVertxHandler.java create mode 100644 independent-projects/resteasy-reactive/server/vertx/src/main/java/org/jboss/resteasy/reactive/server/vertx/VertxRequestContextFactory.java create mode 100644 independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/HelloResource.java create mode 100644 independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/SimpleVertxResteasyReactiveTest.java create mode 100644 independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/framework/InMemoryLogHandler.java create mode 100644 independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/framework/ResteasyReactiveUnitTest.java diff --git a/extensions/resteasy-reactive/quarkus-jaxrs-client/deployment/src/main/java/io/quarkus/resteasy/reactive/client/deployment/JaxrsClientProcessor.java b/extensions/resteasy-reactive/quarkus-jaxrs-client/deployment/src/main/java/io/quarkus/resteasy/reactive/client/deployment/JaxrsClientProcessor.java index f9289b4f9c365..456061bf3a155 100644 --- a/extensions/resteasy-reactive/quarkus-jaxrs-client/deployment/src/main/java/io/quarkus/resteasy/reactive/client/deployment/JaxrsClientProcessor.java +++ b/extensions/resteasy-reactive/quarkus-jaxrs-client/deployment/src/main/java/io/quarkus/resteasy/reactive/client/deployment/JaxrsClientProcessor.java @@ -30,6 +30,7 @@ import org.jboss.resteasy.reactive.common.processor.AdditionalReaders; import org.jboss.resteasy.reactive.common.processor.AdditionalWriters; import org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames; +import org.jboss.resteasy.reactive.common.processor.scanning.ResourceScanningResult; import io.quarkus.arc.deployment.BeanArchiveIndexBuildItem; import io.quarkus.arc.deployment.BeanContainerBuildItem; @@ -82,10 +83,12 @@ void setupClientProxies(ResteasyReactiveClientRecorder recorder, messageBodyWriterBuildItems, beanContainerBuildItem, applicationResultBuildItem, serialisers, RuntimeType.CLIENT); - if (resourceScanningResultBuildItem == null || resourceScanningResultBuildItem.getPathInterfaces().isEmpty()) { + if (resourceScanningResultBuildItem == null + || resourceScanningResultBuildItem.getResult().getPathInterfaces().isEmpty()) { recorder.setupClientProxies(new HashMap<>()); return; } + ResourceScanningResult result = resourceScanningResultBuildItem.getResult(); AdditionalReaders additionalReaders = new AdditionalReaders(); AdditionalWriters additionalWriters = new AdditionalWriters(); @@ -94,19 +97,19 @@ void setupClientProxies(ResteasyReactiveClientRecorder recorder, ClientEndpointIndexer clientEndpointIndexer = new ClientEndpointIndexer.Builder() .setIndex(index) .setExistingConverters(new HashMap<>()) - .setScannedResourcePaths(resourceScanningResultBuildItem.getScannedResourcePaths()) + .setScannedResourcePaths(result.getScannedResourcePaths()) .setConfig(new org.jboss.resteasy.reactive.common.ResteasyReactiveConfig(config.inputBufferSize.asLongValue(), config.singleDefaultProduces)) .setAdditionalReaders(additionalReaders) - .setHttpAnnotationToMethod(resourceScanningResultBuildItem.getHttpAnnotationToMethod()) + .setHttpAnnotationToMethod(result.getHttpAnnotationToMethod()) .setInjectableBeans(new HashMap<>()) .setFactoryCreator(new QuarkusFactoryCreator(recorder, beanContainerBuildItem.getValue())) .setAdditionalWriters(additionalWriters) - .setDefaultBlocking(applicationResultBuildItem.isBlocking()) + .setDefaultBlocking(applicationResultBuildItem.getResult().isBlocking()) .setHasRuntimeConverters(false).build(); List clientDefinitions = new ArrayList<>(); - for (Map.Entry i : resourceScanningResultBuildItem.getPathInterfaces().entrySet()) { + for (Map.Entry i : result.getPathInterfaces().entrySet()) { ClassInfo clazz = index.getClassByName(i.getKey()); //these interfaces can also be clients //so we generate client proxies for them diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ApplicationResultBuildItem.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ApplicationResultBuildItem.java index bc243b2beb657..b7e89913d54da 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ApplicationResultBuildItem.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ApplicationResultBuildItem.java @@ -1,77 +1,18 @@ package io.quarkus.resteasy.reactive.common.deployment; -import java.util.Set; - -import javax.ws.rs.core.Application; - -import org.jboss.jandex.ClassInfo; -import org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames; +import org.jboss.resteasy.reactive.common.processor.scanning.ApplicationScanningResult; import io.quarkus.builder.item.SimpleBuildItem; public final class ApplicationResultBuildItem extends SimpleBuildItem { - final Set allowedClasses; - final Set singletonClasses; - final Set globalNameBindings; - final boolean filterClasses; - final Application application; - final ClassInfo selectedAppClass; - final boolean blocking; - - public ApplicationResultBuildItem(Set allowedClasses, Set singletonClasses, Set globalNameBindings, - boolean filterClasses, Application application, ClassInfo selectedAppClass, boolean blocking) { - this.allowedClasses = allowedClasses; - this.singletonClasses = singletonClasses; - this.globalNameBindings = globalNameBindings; - this.filterClasses = filterClasses; - this.application = application; - this.selectedAppClass = selectedAppClass; - this.blocking = blocking; - } - - public KeepProviderResult keepProvider(ClassInfo providerClass) { - if (filterClasses) { - // we don't care about provider annotations, they're manually registered (but for the server only) - return allowedClasses.contains(providerClass.name().toString()) ? KeepProviderResult.SERVER_ONLY - : KeepProviderResult.DISCARD; - } - return providerClass.classAnnotation(ResteasyReactiveDotNames.PROVIDER) != null ? KeepProviderResult.NORMAL - : KeepProviderResult.DISCARD; - } - - public Set getAllowedClasses() { - return allowedClasses; - } - - public Set getSingletonClasses() { - return singletonClasses; - } + final ApplicationScanningResult result; - public Set getGlobalNameBindings() { - return globalNameBindings; + public ApplicationResultBuildItem(ApplicationScanningResult result) { + this.result = result; } - public boolean isFilterClasses() { - return filterClasses; + public ApplicationScanningResult getResult() { + return result; } - - public Application getApplication() { - return application; - } - - public ClassInfo getSelectedAppClass() { - return selectedAppClass; - } - - public boolean isBlocking() { - return blocking; - } - - public enum KeepProviderResult { - NORMAL, - SERVER_ONLY, - DISCARD - } - } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/FactoryUtils.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/FactoryUtils.java index 8511a9db922e6..1c9bb14dd2eed 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/FactoryUtils.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/FactoryUtils.java @@ -1,5 +1,6 @@ package io.quarkus.resteasy.reactive.common.deployment; +import java.util.Objects; import java.util.Set; import org.jboss.jandex.ClassInfo; @@ -19,6 +20,7 @@ public static BeanFactory factory(ClassInfo providerClass, Set si public static BeanFactory factory(String providerClass, Set singletons, ResteasyReactiveCommonRecorder recorder, BeanContainerBuildItem beanContainerBuildItem) { + Objects.requireNonNull(providerClass, "providerClass cannot be null"); if (singletons.contains(providerClass)) { return new SingletonBeanFactory<>(providerClass); } else { diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/QuarkusResteasyReactiveDotNames.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/QuarkusResteasyReactiveDotNames.java index 5d0c45e25130a..a3c68765bc262 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/QuarkusResteasyReactiveDotNames.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/QuarkusResteasyReactiveDotNames.java @@ -10,7 +10,4 @@ public class QuarkusResteasyReactiveDotNames { public static final DotName HTTP_SERVER_REQUEST = DotName.createSimple(HttpServerRequest.class.getName()); public static final DotName HTTP_SERVER_RESPONSE = DotName.createSimple(HttpServerResponse.class.getName()); - // TODO: fix this hack by moving all the logic that handles this annotation to the server processor - public static final DotName SERVER_EXCEPTION_MAPPER = DotName - .createSimple("org.jboss.resteasy.reactive.server.ServerExceptionMapper"); } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ResourceInterceptorsBuildItem.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ResourceInterceptorsBuildItem.java new file mode 100644 index 0000000000000..ecb18b973c204 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ResourceInterceptorsBuildItem.java @@ -0,0 +1,18 @@ +package io.quarkus.resteasy.reactive.common.deployment; + +import org.jboss.resteasy.reactive.common.model.ResourceInterceptors; + +import io.quarkus.builder.item.SimpleBuildItem; + +public final class ResourceInterceptorsBuildItem extends SimpleBuildItem { + + private final ResourceInterceptors resourceInterceptors; + + public ResourceInterceptorsBuildItem(ResourceInterceptors resourceInterceptors) { + this.resourceInterceptors = resourceInterceptors; + } + + public ResourceInterceptors getResourceInterceptors() { + return resourceInterceptors; + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ResourceInterceptorsContributorBuildItem.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ResourceInterceptorsContributorBuildItem.java new file mode 100644 index 0000000000000..c458e3af17de8 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ResourceInterceptorsContributorBuildItem.java @@ -0,0 +1,20 @@ +package io.quarkus.resteasy.reactive.common.deployment; + +import java.util.function.Consumer; + +import org.jboss.resteasy.reactive.common.model.ResourceInterceptors; + +import io.quarkus.builder.item.MultiBuildItem; + +public final class ResourceInterceptorsContributorBuildItem extends MultiBuildItem { + + private final Consumer buildTask; + + public ResourceInterceptorsContributorBuildItem(Consumer function) { + this.buildTask = function; + } + + public Consumer getBuildTask() { + return buildTask; + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ResourceScanningResultBuildItem.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ResourceScanningResultBuildItem.java index 93a98e45782f3..51e73b9bf6efa 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ResourceScanningResultBuildItem.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ResourceScanningResultBuildItem.java @@ -1,69 +1,18 @@ package io.quarkus.resteasy.reactive.common.deployment; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.jboss.jandex.ClassInfo; -import org.jboss.jandex.DotName; -import org.jboss.jandex.MethodInfo; +import org.jboss.resteasy.reactive.common.processor.scanning.ResourceScanningResult; import io.quarkus.builder.item.SimpleBuildItem; public final class ResourceScanningResultBuildItem extends SimpleBuildItem { - final Map scannedResources; - final Map scannedResourcePaths; - final Map possibleSubResources; - final Map pathInterfaces; - final Map resourcesThatNeedCustomProducer; - final Set beanParams; - final Map httpAnnotationToMethod; - final List classLevelExceptionMappers; - - public ResourceScanningResultBuildItem(Map scannedResources, Map scannedResourcePaths, - Map possibleSubResources, Map pathInterfaces, - Map resourcesThatNeedCustomProducer, - Set beanParams, Map httpAnnotationToMethod, List classLevelExceptionMappers) { - this.scannedResources = scannedResources; - this.scannedResourcePaths = scannedResourcePaths; - this.possibleSubResources = possibleSubResources; - this.pathInterfaces = pathInterfaces; - this.resourcesThatNeedCustomProducer = resourcesThatNeedCustomProducer; - this.beanParams = beanParams; - this.httpAnnotationToMethod = httpAnnotationToMethod; - this.classLevelExceptionMappers = classLevelExceptionMappers; - } - - public Map getScannedResources() { - return scannedResources; - } - - public Map getScannedResourcePaths() { - return scannedResourcePaths; - } - - public Map getPossibleSubResources() { - return possibleSubResources; - } - - public Map getPathInterfaces() { - return pathInterfaces; - } - - public Map getResourcesThatNeedCustomProducer() { - return resourcesThatNeedCustomProducer; - } - - public Set getBeanParams() { - return beanParams; - } + final ResourceScanningResult result; - public Map getHttpAnnotationToMethod() { - return httpAnnotationToMethod; + public ResourceScanningResultBuildItem(ResourceScanningResult result) { + this.result = result; } - public List getClassLevelExceptionMappers() { - return classLevelExceptionMappers; + public ResourceScanningResult getResult() { + return result; } } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ResteasyReactiveCommonProcessor.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ResteasyReactiveCommonProcessor.java index 7e85d88cca4de..44acc3d879f8a 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ResteasyReactiveCommonProcessor.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ResteasyReactiveCommonProcessor.java @@ -1,54 +1,42 @@ package io.quarkus.resteasy.reactive.common.deployment; -import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.DELETE; -import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.GET; -import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.HEAD; -import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.OPTIONS; -import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.PATCH; -import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.POST; -import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.PUT; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Modifier; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Optional; import java.util.Set; -import java.util.function.Function; -import java.util.stream.Collectors; +import java.util.function.Consumer; import javax.ws.rs.RuntimeType; -import javax.ws.rs.core.Application; import org.jboss.jandex.AnnotationInstance; -import org.jboss.jandex.AnnotationTarget; 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.resteasy.reactive.common.processor.NameBindingUtil; +import org.jboss.resteasy.reactive.common.model.InterceptorContainer; +import org.jboss.resteasy.reactive.common.model.PreMatchInterceptorContainer; +import org.jboss.resteasy.reactive.common.model.ResourceInterceptor; +import org.jboss.resteasy.reactive.common.model.ResourceInterceptors; import org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames; +import org.jboss.resteasy.reactive.common.processor.scanning.ApplicationScanningResult; +import org.jboss.resteasy.reactive.common.processor.scanning.ApplicationScanningResult.KeepProviderResult; +import org.jboss.resteasy.reactive.common.processor.scanning.ResourceScanningResult; +import org.jboss.resteasy.reactive.common.processor.scanning.ResteasyReactiveInterceptorScanner; +import org.jboss.resteasy.reactive.common.processor.scanning.ResteasyReactiveScanner; import io.quarkus.arc.deployment.AdditionalBeanBuildItem; import io.quarkus.arc.deployment.AnnotationsTransformerBuildItem; import io.quarkus.arc.deployment.BeanArchiveIndexBuildItem; import io.quarkus.arc.deployment.BeanContainerBuildItem; -import io.quarkus.arc.processor.DotNames; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.builditem.CombinedIndexBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; import io.quarkus.deployment.util.JandexUtil; -import io.quarkus.resteasy.reactive.common.deployment.ApplicationResultBuildItem.KeepProviderResult; import io.quarkus.resteasy.reactive.spi.AbstractInterceptorBuildItem; import io.quarkus.resteasy.reactive.spi.ContainerRequestFilterBuildItem; +import io.quarkus.resteasy.reactive.spi.ContainerResponseFilterBuildItem; import io.quarkus.resteasy.reactive.spi.MessageBodyReaderBuildItem; import io.quarkus.resteasy.reactive.spi.MessageBodyWriterBuildItem; import io.quarkus.resteasy.reactive.spi.ReaderInterceptorBuildItem; @@ -56,114 +44,112 @@ public class ResteasyReactiveCommonProcessor { - private static Map BUILTIN_HTTP_ANNOTATIONS_TO_METHOD = new HashMap<>(); - - static { - BUILTIN_HTTP_ANNOTATIONS_TO_METHOD.put(GET, "GET"); - BUILTIN_HTTP_ANNOTATIONS_TO_METHOD.put(POST, "POST"); - BUILTIN_HTTP_ANNOTATIONS_TO_METHOD.put(HEAD, "HEAD"); - BUILTIN_HTTP_ANNOTATIONS_TO_METHOD.put(PUT, "PUT"); - BUILTIN_HTTP_ANNOTATIONS_TO_METHOD.put(DELETE, "DELETE"); - BUILTIN_HTTP_ANNOTATIONS_TO_METHOD.put(PATCH, "PATCH"); - BUILTIN_HTTP_ANNOTATIONS_TO_METHOD.put(OPTIONS, "OPTIONS"); - BUILTIN_HTTP_ANNOTATIONS_TO_METHOD = Collections.unmodifiableMap(BUILTIN_HTTP_ANNOTATIONS_TO_METHOD); - } - @BuildStep ApplicationResultBuildItem handleApplication(CombinedIndexBuildItem combinedIndexBuildItem, BuildProducer reflectiveClass) { + ApplicationScanningResult result = ResteasyReactiveScanner + .scanForApplicationClass(combinedIndexBuildItem.getComputingIndex()); + if (result.getSelectedAppClass() != null) { + reflectiveClass.produce(new ReflectiveClassBuildItem(false, false, result.getSelectedAppClass().name().toString())); + } + return new ApplicationResultBuildItem(result); + } - IndexView index = combinedIndexBuildItem.getIndex(); - Collection applications = index - .getAllKnownSubclasses(ResteasyReactiveDotNames.APPLICATION); - - Set allowedClasses = new HashSet<>(); - Set singletonClasses = new HashSet<>(); - Set globalNameBindings = new HashSet<>(); - boolean filterClasses = false; - Application application = null; - ClassInfo selectedAppClass = null; - boolean blocking = false; - for (ClassInfo applicationClassInfo : applications) { - if (selectedAppClass != null) { - throw new RuntimeException("More than one Application class: " + applications); - } - selectedAppClass = applicationClassInfo; - // FIXME: yell if there's more than one - String applicationClass = applicationClassInfo.name().toString(); - reflectiveClass.produce(new ReflectiveClassBuildItem(true, false, false, applicationClass)); - try { - Class appClass = Thread.currentThread().getContextClassLoader().loadClass(applicationClass); - application = (Application) appClass.getConstructor().newInstance(); - Set> classes = application.getClasses(); - if (!classes.isEmpty()) { - for (Class klass : classes) { - allowedClasses.add(klass.getName()); - } - filterClasses = true; - } - classes = application.getSingletons().stream().map(Object::getClass).collect(Collectors.toSet()); - if (!classes.isEmpty()) { - for (Class klass : classes) { - allowedClasses.add(klass.getName()); - singletonClasses.add(klass.getName()); - } - filterClasses = true; - } - } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException - | InvocationTargetException e) { - throw new RuntimeException("Unable to handle class: " + applicationClass, e); - } - if (applicationClassInfo.classAnnotation(ResteasyReactiveDotNames.NON_BLOCKING) != null) { - blocking = false; - } else if (applicationClassInfo.classAnnotation(ResteasyReactiveDotNames.NON_BLOCKING) != null) { - blocking = true; + @BuildStep + public ResourceInterceptorsContributorBuildItem scanForIOInterceptors(CombinedIndexBuildItem combinedIndexBuildItem, + ApplicationResultBuildItem applicationResultBuildItem) { + return new ResourceInterceptorsContributorBuildItem(new Consumer() { + @Override + public void accept(ResourceInterceptors interceptors) { + ResteasyReactiveInterceptorScanner.scanForIOInterceptors(interceptors, + combinedIndexBuildItem.getComputingIndex(), + applicationResultBuildItem.getResult()); } - } - return new ApplicationResultBuildItem(allowedClasses, singletonClasses, globalNameBindings, filterClasses, application, - selectedAppClass, blocking); + }); } @BuildStep - public void scanForIOInterceptors(CombinedIndexBuildItem combinedIndexBuildItem, + public ResourceInterceptorsBuildItem buildResourceInterceptors(List scanningTasks, ApplicationResultBuildItem applicationResultBuildItem, - BuildProducer writerInterceptorProducer, - BuildProducer readerInterceptorProducer) { - IndexView index = combinedIndexBuildItem.getIndex(); - Collection readerInterceptors = index - .getAllKnownImplementors(ResteasyReactiveDotNames.READER_INTERCEPTOR); - Collection writerInterceptors = index - .getAllKnownImplementors(ResteasyReactiveDotNames.WRITER_INTERCEPTOR); - - for (ClassInfo filterClass : writerInterceptors) { - handleDiscoveredInterceptor(applicationResultBuildItem, writerInterceptorProducer, index, filterClass, - WriterInterceptorBuildItem.Builder::new); + BuildProducer additionalBeanBuildItemBuildProducer, + List writerInterceptors, + List readerInterceptors, + List requestFilters, + List responseFilters) { + ResourceInterceptors resourceInterceptors = new ResourceInterceptors(); + for (ResourceInterceptorsContributorBuildItem i : scanningTasks) { + i.getBuildTask().accept(resourceInterceptors); + } + AdditionalBeanBuildItem.Builder beanBuilder = AdditionalBeanBuildItem.builder(); + registerContainerBeans(beanBuilder, resourceInterceptors.getContainerResponseFilters()); + registerContainerBeans(beanBuilder, resourceInterceptors.getContainerRequestFilters()); + registerContainerBeans(beanBuilder, resourceInterceptors.getReaderInterceptors()); + registerContainerBeans(beanBuilder, resourceInterceptors.getWriterInterceptors()); + Set globalNameBindings = applicationResultBuildItem.getResult().getGlobalNameBindings(); + for (WriterInterceptorBuildItem i : writerInterceptors) { + registerInterceptors(globalNameBindings, resourceInterceptors.getWriterInterceptors(), i, beanBuilder); + } + for (ReaderInterceptorBuildItem i : readerInterceptors) { + registerInterceptors(globalNameBindings, resourceInterceptors.getReaderInterceptors(), i, beanBuilder); } - for (ClassInfo filterClass : readerInterceptors) { - handleDiscoveredInterceptor(applicationResultBuildItem, readerInterceptorProducer, index, filterClass, - ReaderInterceptorBuildItem.Builder::new); + for (ContainerRequestFilterBuildItem i : requestFilters) { + registerInterceptors(globalNameBindings, resourceInterceptors.getContainerRequestFilters(), i, beanBuilder); } + for (ContainerResponseFilterBuildItem i : responseFilters) { + registerInterceptors(globalNameBindings, resourceInterceptors.getContainerResponseFilters(), i, beanBuilder); + } + additionalBeanBuildItemBuildProducer.produce(beanBuilder.setUnremovable().build()); + return new ResourceInterceptorsBuildItem(resourceInterceptors); } - public static > void handleDiscoveredInterceptor( - ApplicationResultBuildItem applicationResultBuildItem, BuildProducer producer, IndexView index, - ClassInfo filterClass, Function builderCreator) { - KeepProviderResult keepProviderResult = applicationResultBuildItem.keepProvider(filterClass); - if (keepProviderResult != KeepProviderResult.DISCARD) { - B builder = builderCreator.apply(filterClass.name().toString()); - builder.setRegisterAsBean(true); - if (filterClass.classAnnotation(ResteasyReactiveDotNames.PRE_MATCHING) != null) { - if (builder instanceof ContainerRequestFilterBuildItem.Builder) { - ((ContainerRequestFilterBuildItem.Builder) builder).setPreMatching(true); - } + protected void registerInterceptors(Set globalNameBindings, + InterceptorContainer interceptors, B filterItem, AdditionalBeanBuildItem.Builder beanBuilder) { + if (filterItem.isRegisterAsBean()) { + beanBuilder.addBeanClass(filterItem.getClassName()); + } + ResourceInterceptor interceptor = interceptors.create(); + interceptor.setClassName(filterItem.getClassName()); + Integer priority = filterItem.getPriority(); + if (priority != null) { + interceptor.setPriority(priority); + } + if (interceptors instanceof PreMatchInterceptorContainer + && ((ContainerRequestFilterBuildItem) filterItem).isPreMatching()) { + ((PreMatchInterceptorContainer) interceptors).addPreMatchInterceptor(interceptor); + + } else { + Set nameBindingNames = filterItem.getNameBindingNames(); + if (nameBindingNames.isEmpty() || namePresent(nameBindingNames, globalNameBindings)) { + interceptors.addGlobalRequestInterceptor(interceptor); + } else { + interceptor.setNameBindingNames(nameBindingNames); + interceptors.addNameRequestInterceptor(interceptor); + } + } + } + + private void registerContainerBeans(AdditionalBeanBuildItem.Builder additionalProviders, + InterceptorContainer container) { + for (ResourceInterceptor i : container.getGlobalResourceInterceptors()) { + additionalProviders.addBeanClass(i.getClassName()); + } + for (ResourceInterceptor i : container.getNameResourceInterceptors()) { + additionalProviders.addBeanClass(i.getClassName()); + } + if (container instanceof PreMatchInterceptorContainer) { + for (ResourceInterceptor i : ((PreMatchInterceptorContainer) container).getPreMatchInterceptors()) { + additionalProviders.addBeanClass(i.getClassName()); } - builder.setNameBindingNames(NameBindingUtil.nameBindingNames(index, filterClass)); - AnnotationInstance priorityInstance = filterClass.classAnnotation(ResteasyReactiveDotNames.PRIORITY); - if (priorityInstance != null) { - builder.setPriority(priorityInstance.value().asInt()); + } + } + + private boolean namePresent(Set nameBindingNames, Set globalNameBindings) { + for (String i : globalNameBindings) { + if (nameBindingNames.contains(i)) { + return true; } - producer.produce(builder.build()); } + return false; } @BuildStep @@ -172,103 +158,17 @@ void scanResources( CombinedIndexBuildItem combinedIndexBuildItem, BuildProducer annotationsTransformerBuildItemBuildProducer, BuildProducer resourceScanningResultBuildItemBuildProducer) { - IndexView index = combinedIndexBuildItem.getIndex(); - Collection paths = index.getAnnotations(ResteasyReactiveDotNames.PATH); - Collection allPaths = new ArrayList<>(paths); - - if (allPaths.isEmpty()) { - // no detected @Path, bail out + ResourceScanningResult res = ResteasyReactiveScanner.scanResources(combinedIndexBuildItem.getComputingIndex()); + if (res == null) { return; } - - Map scannedResources = new HashMap<>(); - Map scannedResourcePaths = new HashMap<>(); - Map possibleSubResources = new HashMap<>(); - Map pathInterfaces = new HashMap<>(); - Map resourcesThatNeedCustomProducer = new HashMap<>(); - List methodExceptionMappers = new ArrayList<>(); - Set beanParams = new HashSet<>(); - - for (AnnotationInstance beanParamAnnotation : index.getAnnotations(ResteasyReactiveDotNames.BEAN_PARAM)) { - AnnotationTarget target = beanParamAnnotation.target(); - // FIXME: this isn't right wrt generics - switch (target.kind()) { - case FIELD: - beanParams.add(target.asField().type().toString()); - break; - case METHOD: - Type setterParamType = target.asMethod().parameters().get(0); - beanParams.add(setterParamType.toString()); - break; - case METHOD_PARAMETER: - MethodInfo method = target.asMethodParameter().method(); - int paramIndex = target.asMethodParameter().position(); - Type paramType = method.parameters().get(paramIndex); - beanParams.add(paramType.toString()); - break; - default: - break; - } - } - - for (AnnotationInstance annotation : allPaths) { - if (annotation.target().kind() == AnnotationTarget.Kind.CLASS) { - ClassInfo clazz = annotation.target().asClass(); - if (!Modifier.isInterface(clazz.flags())) { - scannedResources.put(clazz.name(), clazz); - scannedResourcePaths.put(clazz.name(), annotation.value().asString()); - } else { - pathInterfaces.put(clazz.name(), annotation.value().asString()); - } - MethodInfo ctor = hasJaxRsCtorParams(clazz); - if (ctor != null) { - resourcesThatNeedCustomProducer.put(clazz.name(), ctor); - } - List exceptionMapperAnnotationInstances = clazz.annotations() - .get(QuarkusResteasyReactiveDotNames.SERVER_EXCEPTION_MAPPER); - if (exceptionMapperAnnotationInstances != null) { - for (AnnotationInstance instance : exceptionMapperAnnotationInstances) { - if (instance.target().kind() != AnnotationTarget.Kind.METHOD) { - continue; - } - methodExceptionMappers.add(instance.target().asMethod()); - } - } - } - } - - if (!resourcesThatNeedCustomProducer.isEmpty()) { + if (!res.getResourcesThatNeedCustomProducer().isEmpty()) { annotationsTransformerBuildItemBuildProducer .produce(new AnnotationsTransformerBuildItem( - new VetoingAnnotationTransformer(resourcesThatNeedCustomProducer.keySet()))); - } - - for (Map.Entry i : pathInterfaces.entrySet()) { - for (ClassInfo clazz : index.getAllKnownImplementors(i.getKey())) { - if (!Modifier.isAbstract(clazz.flags())) { - if ((clazz.enclosingClass() == null || Modifier.isStatic(clazz.flags())) && - clazz.enclosingMethod() == null) { - if (!scannedResources.containsKey(clazz.name())) { - scannedResources.put(clazz.name(), clazz); - scannedResourcePaths.put(clazz.name(), i.getValue()); - } - } - } - } - } - - Map httpAnnotationToMethod = new HashMap<>(BUILTIN_HTTP_ANNOTATIONS_TO_METHOD); - Collection httpMethodInstances = index.getAnnotations(ResteasyReactiveDotNames.HTTP_METHOD); - for (AnnotationInstance httpMethodInstance : httpMethodInstances) { - if (httpMethodInstance.target().kind() != AnnotationTarget.Kind.CLASS) { - continue; - } - httpAnnotationToMethod.put(httpMethodInstance.target().asClass().name(), httpMethodInstance.value().asString()); + new VetoingAnnotationTransformer(res.getResourcesThatNeedCustomProducer().keySet()))); } - resourceScanningResultBuildItemBuildProducer.produce(new ResourceScanningResultBuildItem(scannedResources, - scannedResourcePaths, possibleSubResources, pathInterfaces, resourcesThatNeedCustomProducer, beanParams, - httpAnnotationToMethod, methodExceptionMappers)); + resourceScanningResultBuildItemBuildProducer.produce(new ResourceScanningResultBuildItem(res)); } @BuildStep @@ -292,7 +192,7 @@ public void setupEndpoints(BeanArchiveIndexBuildItem beanArchiveIndexBuildItem, .getAllKnownImplementors(ResteasyReactiveDotNames.MESSAGE_BODY_READER); for (ClassInfo writerClass : writers) { - KeepProviderResult keepProviderResult = applicationResultBuildItem.keepProvider(writerClass); + KeepProviderResult keepProviderResult = applicationResultBuildItem.getResult().keepProvider(writerClass); if (keepProviderResult != KeepProviderResult.DISCARD) { RuntimeType runtimeType = null; if (keepProviderResult == KeepProviderResult.SERVER_ONLY) { @@ -317,7 +217,7 @@ public void setupEndpoints(BeanArchiveIndexBuildItem beanArchiveIndexBuildItem, } for (ClassInfo readerClass : readers) { - KeepProviderResult keepProviderResult = applicationResultBuildItem.keepProvider(readerClass); + KeepProviderResult keepProviderResult = applicationResultBuildItem.getResult().keepProvider(readerClass); if (keepProviderResult != KeepProviderResult.DISCARD) { List typeParameters = JandexUtil.resolveTypeParameters(readerClass.name(), ResteasyReactiveDotNames.MESSAGE_BODY_READER, @@ -343,49 +243,4 @@ public void setupEndpoints(BeanArchiveIndexBuildItem beanArchiveIndexBuildItem, } } - @BuildStep - void additionalBeans(List writerInterceptors, - List readerInterceptors, - BuildProducer additionalBean) { - - AdditionalBeanBuildItem.Builder additionalProviders = AdditionalBeanBuildItem.builder(); - for (WriterInterceptorBuildItem i : writerInterceptors) { - if (i.isRegisterAsBean()) { - additionalProviders.addBeanClass(i.getClassName()); - } - } - for (ReaderInterceptorBuildItem i : readerInterceptors) { - if (i.isRegisterAsBean()) { - additionalProviders.addBeanClass(i.getClassName()); - } - } - additionalBean.produce(additionalProviders.setUnremovable().setDefaultScope(DotNames.SINGLETON).build()); - } - - private MethodInfo hasJaxRsCtorParams(ClassInfo classInfo) { - List methods = classInfo.methods(); - List ctors = new ArrayList<>(); - for (MethodInfo method : methods) { - if (method.name().equals("")) { - ctors.add(method); - } - } - if (ctors.size() != 1) { // we only need to deal with a single ctor here - return null; - } - MethodInfo ctor = ctors.get(0); - if (ctor.parameters().size() == 0) { // default ctor - we don't need to do anything - return null; - } - - boolean needsHandling = false; - for (DotName dotName : ResteasyReactiveDotNames.RESOURCE_CTOR_PARAMS_THAT_NEED_HANDLING) { - if (ctor.hasAnnotation(dotName)) { - needsHandling = true; - break; - } - } - return needsHandling ? ctor : null; - } - } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/SerializersUtil.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/SerializersUtil.java index a117ca90c3688..aebe1ce26e672 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/SerializersUtil.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/SerializersUtil.java @@ -31,7 +31,8 @@ public static void setupSerializers(ResteasyReactiveCommonRecorder recorder, ResourceWriter writer = new ResourceWriter(); writer.setBuiltin(additionalWriter.isBuiltin()); String writerClassName = additionalWriter.getClassName(); - writer.setFactory(FactoryUtils.factory(writerClassName, applicationResultBuildItem.getSingletonClasses(), recorder, + writer.setFactory(FactoryUtils.factory(writerClassName, + applicationResultBuildItem.getResult().getSingletonClasses(), recorder, beanContainerBuildItem)); writer.setConstraint(additionalWriter.getRuntimeType()); if (!additionalWriter.getMediaTypeStrings().isEmpty()) { @@ -45,7 +46,8 @@ public static void setupSerializers(ResteasyReactiveCommonRecorder recorder, ResourceReader reader = new ResourceReader(); reader.setBuiltin(false); String readerClassName = additionalReader.getClassName(); - reader.setFactory(FactoryUtils.factory(readerClassName, applicationResultBuildItem.getSingletonClasses(), recorder, + reader.setFactory(FactoryUtils.factory(readerClassName, + applicationResultBuildItem.getResult().getSingletonClasses(), recorder, beanContainerBuildItem)); reader.setConstraint(additionalReader.getRuntimeType()); if (!additionalReader.getMediaTypeStrings().isEmpty()) { diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/ResteasyReactiveJacksonProcessor.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/ResteasyReactiveJacksonProcessor.java index 87fdb716e2ed6..3a5c50d42faef 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/ResteasyReactiveJacksonProcessor.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/ResteasyReactiveJacksonProcessor.java @@ -58,8 +58,8 @@ void registerForReflection(Optional resourceSca if (!resourceScanningResultBuildItem.isPresent()) { return; } - - Collection resourceClasses = resourceScanningResultBuildItem.get().getScannedResources().values(); + Collection resourceClasses = resourceScanningResultBuildItem.get().getResult().getScannedResources() + .values(); Set classesNeedingReflectionOnMethods = new HashSet<>(); for (ClassInfo resourceClass : resourceClasses) { if (resourceClass.annotations().containsKey(JSON_VIEW)) { diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ContextResolversBuildItem.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ContextResolversBuildItem.java new file mode 100644 index 0000000000000..101389dd26056 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ContextResolversBuildItem.java @@ -0,0 +1,18 @@ +package io.quarkus.resteasy.reactive.server.deployment; + +import org.jboss.resteasy.reactive.server.model.ContextResolvers; + +import io.quarkus.builder.item.SimpleBuildItem; + +public final class ContextResolversBuildItem extends SimpleBuildItem { + + private final ContextResolvers contextResolvers; + + public ContextResolversBuildItem(ContextResolvers contextResolvers) { + this.contextResolvers = contextResolvers; + } + + public ContextResolvers getContextResolvers() { + return contextResolvers; + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ExceptionMappersBuildItem.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ExceptionMappersBuildItem.java new file mode 100644 index 0000000000000..2ad8c5fe5412e --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ExceptionMappersBuildItem.java @@ -0,0 +1,18 @@ +package io.quarkus.resteasy.reactive.server.deployment; + +import org.jboss.resteasy.reactive.server.core.ExceptionMapping; + +import io.quarkus.builder.item.SimpleBuildItem; + +final class ExceptionMappersBuildItem extends SimpleBuildItem { + + private final ExceptionMapping exceptionMapping; + + public ExceptionMappersBuildItem(ExceptionMapping exceptionMapping) { + this.exceptionMapping = exceptionMapping; + } + + public ExceptionMapping getExceptionMapping() { + return exceptionMapping; + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ParamConverterProvidersBuildItem.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ParamConverterProvidersBuildItem.java new file mode 100644 index 0000000000000..c68450e46aa5e --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ParamConverterProvidersBuildItem.java @@ -0,0 +1,18 @@ +package io.quarkus.resteasy.reactive.server.deployment; + +import org.jboss.resteasy.reactive.server.model.ParamConverterProviders; + +import io.quarkus.builder.item.SimpleBuildItem; + +final class ParamConverterProvidersBuildItem extends SimpleBuildItem { + + private final ParamConverterProviders paramConverterProviders; + + public ParamConverterProvidersBuildItem(ParamConverterProviders paramConverterProviders) { + this.paramConverterProviders = paramConverterProviders; + } + + public ParamConverterProviders getParamConverterProviders() { + return paramConverterProviders; + } +} 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 new file mode 100644 index 0000000000000..e0f2ffdeab433 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/QuarkusServerEndpointIndexer.java @@ -0,0 +1,198 @@ +package io.quarkus.resteasy.reactive.server.deployment; + +import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.STRING; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.ClassType; +import org.jboss.jandex.DotName; +import org.jboss.jandex.FieldInfo; +import org.jboss.jandex.IndexView; +import org.jboss.jandex.MethodInfo; +import org.jboss.resteasy.reactive.server.core.Deployment; +import org.jboss.resteasy.reactive.server.core.parameters.converters.GeneratedParameterConverter; +import org.jboss.resteasy.reactive.server.core.parameters.converters.NoopParameterConverter; +import org.jboss.resteasy.reactive.server.core.parameters.converters.ParameterConverter; +import org.jboss.resteasy.reactive.server.core.parameters.converters.ParameterConverterSupplier; +import org.jboss.resteasy.reactive.server.core.parameters.converters.RuntimeResolvedConverter; +import org.jboss.resteasy.reactive.server.model.ServerResourceMethod; +import org.jboss.resteasy.reactive.server.processor.ServerEndpointIndexer; +import org.jboss.resteasy.reactive.server.processor.ServerIndexedParameter; + +import io.quarkus.deployment.GeneratedClassGizmoAdaptor; +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.builditem.BytecodeTransformerBuildItem; +import io.quarkus.deployment.builditem.GeneratedClassBuildItem; +import io.quarkus.gizmo.ClassCreator; +import io.quarkus.gizmo.MethodCreator; +import io.quarkus.gizmo.MethodDescriptor; +import io.quarkus.gizmo.ResultHandle; +import io.vertx.core.http.HttpServerRequest; +import io.vertx.core.http.HttpServerResponse; +import io.vertx.ext.web.RoutingContext; + +public class QuarkusServerEndpointIndexer + extends ServerEndpointIndexer { + private final MethodCreator initConverters; + private final BuildProducer generatedClassBuildItemBuildProducer; + private final BuildProducer bytecodeTransformerBuildProducer; + private static final Set CONTEXT_TYPES = Collections.unmodifiableSet(new HashSet<>(Arrays.asList( + DotName.createSimple(HttpServerRequest.class.getName()), + DotName.createSimple(HttpServerResponse.class.getName()), + DotName.createSimple(RoutingContext.class.getName())))); + + QuarkusServerEndpointIndexer(Builder builder) { + super(builder); + this.initConverters = builder.initConverters; + this.generatedClassBuildItemBuildProducer = builder.generatedClassBuildItemBuildProducer; + this.bytecodeTransformerBuildProducer = builder.bytecodeTransformerBuildProducer; + } + + protected boolean isContextType(ClassType klass) { + return super.isContextType(klass) || CONTEXT_TYPES.contains(klass.name()); + } + + @Override + protected ParameterConverterSupplier extractConverter(String elementType, IndexView indexView, + Map existingConverters, String errorLocation, boolean hasRuntimeConverters) { + if (elementType.equals(String.class.getName())) { + if (hasRuntimeConverters) + return new RuntimeResolvedConverter.Supplier().setDelegate(new NoopParameterConverter.Supplier()); + // String needs no conversion + return null; + } else if (existingConverters.containsKey(elementType)) { + String className = existingConverters.get(elementType); + ParameterConverterSupplier delegate; + if (className == null) + delegate = null; + else + delegate = new GeneratedParameterConverter().setClassName(className); + if (hasRuntimeConverters) + return new RuntimeResolvedConverter.Supplier().setDelegate(delegate); + if (delegate == null) + throw new RuntimeException("Failed to find converter for " + elementType); + return delegate; + } + + MethodDescriptor fromString = null; + MethodDescriptor valueOf = null; + MethodInfo stringCtor = null; + String primitiveWrapperType = primitiveTypes.get(elementType); + String prefix = ""; + if (primitiveWrapperType != null) { + valueOf = MethodDescriptor.ofMethod(primitiveWrapperType, "valueOf", primitiveWrapperType, String.class); + prefix = "io.quarkus.generated."; + } else { + ClassInfo type = indexView.getClassByName(DotName.createSimple(elementType)); + if (type != null) { + for (MethodInfo i : type.methods()) { + if (i.parameters().size() == 1) { + if (i.parameters().get(0).name().equals(STRING)) { + if (i.name().equals("")) { + stringCtor = i; + } else if (i.name().equals("valueOf")) { + valueOf = MethodDescriptor.of(i); + } else if (i.name().equals("fromString")) { + fromString = MethodDescriptor.of(i); + } + } + } + } + if (type.isEnum()) { + //spec weirdness, enums order is different + if (fromString != null) { + valueOf = null; + } + } + } + } + + String baseName; + ParameterConverterSupplier delegate; + if (stringCtor != null || valueOf != null || fromString != null) { + baseName = prefix + elementType + "$quarkusrestparamConverter$"; + try (ClassCreator classCreator = new ClassCreator( + new GeneratedClassGizmoAdaptor(generatedClassBuildItemBuildProducer, true), baseName, null, + Object.class.getName(), ParameterConverter.class.getName())) { + MethodCreator mc = classCreator.getMethodCreator("convert", Object.class, Object.class); + if (stringCtor != null) { + ResultHandle ret = mc.newInstance(stringCtor, mc.getMethodParam(0)); + mc.returnValue(ret); + } else if (valueOf != null) { + ResultHandle ret = mc.invokeStaticMethod(valueOf, mc.getMethodParam(0)); + mc.returnValue(ret); + } else if (fromString != null) { + ResultHandle ret = mc.invokeStaticMethod(fromString, mc.getMethodParam(0)); + mc.returnValue(ret); + } + } + delegate = new GeneratedParameterConverter().setClassName(baseName); + } else { + // let's not try this again + baseName = null; + delegate = null; + } + existingConverters.put(elementType, baseName); + if (hasRuntimeConverters) + return new RuntimeResolvedConverter.Supplier().setDelegate(delegate); + if (delegate == null) + throw new RuntimeException("Failed to find converter for " + elementType); + return delegate; + } + + @Override + protected ServerResourceMethod createResourceMethod() { + return new ServerResourceMethod(); + } + + protected void handleFieldExtractors(String currentTypeName, Map fieldExtractors, + boolean superTypeIsInjectable) { + bytecodeTransformerBuildProducer.produce(new BytecodeTransformerBuildItem(currentTypeName, + new ClassInjectorTransformer(fieldExtractors, superTypeIsInjectable))); + } + + protected void handleConverter(String currentTypeName, FieldInfo field) { + initConverters.invokeStaticMethod(MethodDescriptor.ofMethod(currentTypeName, + ClassInjectorTransformer.INIT_CONVERTER_METHOD_NAME + field.name(), + void.class, Deployment.class), + initConverters.getMethodParam(0)); + } + + public static final class Builder extends AbstractBuilder { + + private BuildProducer generatedClassBuildItemBuildProducer; + private BuildProducer bytecodeTransformerBuildProducer; + private MethodCreator initConverters; + + @Override + public QuarkusServerEndpointIndexer build() { + return new QuarkusServerEndpointIndexer(this); + } + + public MethodCreator getInitConverters() { + return initConverters; + } + + public Builder setBytecodeTransformerBuildProducer( + BuildProducer bytecodeTransformerBuildProducer) { + this.bytecodeTransformerBuildProducer = bytecodeTransformerBuildProducer; + return this; + } + + public Builder setGeneratedClassBuildItemBuildProducer( + BuildProducer generatedClassBuildItemBuildProducer) { + this.generatedClassBuildItemBuildProducer = generatedClassBuildItemBuildProducer; + return this; + } + + public Builder setInitConverters(MethodCreator initConverters) { + this.initConverters = initConverters; + return this; + } + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveCDIProcessor.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveCDIProcessor.java index 30a9ed4fcdcc4..10333d55536c6 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveCDIProcessor.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveCDIProcessor.java @@ -16,11 +16,7 @@ import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.resteasy.reactive.server.runtime.QuarkusContextProducers; -import io.quarkus.resteasy.reactive.spi.ContainerRequestFilterBuildItem; -import io.quarkus.resteasy.reactive.spi.ContainerResponseFilterBuildItem; -import io.quarkus.resteasy.reactive.spi.ContextResolverBuildItem; import io.quarkus.resteasy.reactive.spi.DynamicFeatureBuildItem; -import io.quarkus.resteasy.reactive.spi.ExceptionMapperBuildItem; import io.quarkus.resteasy.reactive.spi.JaxrsFeatureBuildItem; public class ResteasyReactiveCDIProcessor { @@ -33,7 +29,6 @@ AutoInjectAnnotationBuildItem contextInjection( .build()); return new AutoInjectAnnotationBuildItem(ResteasyReactiveServerDotNames.CONTEXT, DotName.createSimple(BeanParam.class.getName())); - } @BuildStep @@ -49,30 +44,11 @@ void beanDefiningAnnotations(BuildProducer bean } @BuildStep - void additionalBeans(List additionalContainerRequestFilters, - List additionalContainerResponseFilters, - List additionalDynamicFeatures, - List additionalExceptionMappers, - BuildProducer additionalBean, + void additionalBeans(List additionalDynamicFeatures, List featureBuildItems, - List contextResolverBuildItems) { + BuildProducer additionalBean) { AdditionalBeanBuildItem.Builder additionalProviders = AdditionalBeanBuildItem.builder(); - for (ContainerRequestFilterBuildItem requestFilter : additionalContainerRequestFilters) { - if (requestFilter.isRegisterAsBean()) { - additionalProviders.addBeanClass(requestFilter.getClassName()); - } - } - for (ContainerResponseFilterBuildItem responseFilter : additionalContainerResponseFilters) { - if (responseFilter.isRegisterAsBean()) { - additionalProviders.addBeanClass(responseFilter.getClassName()); - } - } - for (ExceptionMapperBuildItem exceptionMapper : additionalExceptionMappers) { - if (exceptionMapper.isRegisterAsBean()) { - additionalProviders.addBeanClass(exceptionMapper.getClassName()); - } - } for (DynamicFeatureBuildItem dynamicFeature : additionalDynamicFeatures) { if (dynamicFeature.isRegisterAsBean()) { additionalProviders.addBeanClass(dynamicFeature.getClassName()); @@ -83,11 +59,6 @@ void additionalBeans(List additionalContainerRe additionalProviders.addBeanClass(dynamicFeature.getClassName()); } } - for (ContextResolverBuildItem contextResolver : contextResolverBuildItems) { - if (contextResolver.isRegisterAsBean()) { - additionalProviders.addBeanClass(contextResolver.getClassName()); - } - } additionalBean.produce(additionalProviders.setUnremovable().setDefaultScope(DotNames.SINGLETON).build()); } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java index c7a73b0eb338f..319f2904f6c29 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java @@ -13,6 +13,7 @@ import java.util.Optional; import java.util.Set; import java.util.function.Consumer; +import java.util.function.Function; import java.util.stream.Stream; import javax.ws.rs.Priorities; @@ -33,32 +34,28 @@ import org.jboss.resteasy.reactive.common.core.Serialisers; import org.jboss.resteasy.reactive.common.core.SingletonBeanFactory; import org.jboss.resteasy.reactive.common.model.InjectableBean; -import org.jboss.resteasy.reactive.common.model.InterceptorContainer; -import org.jboss.resteasy.reactive.common.model.PreMatchInterceptorContainer; import org.jboss.resteasy.reactive.common.model.ResourceClass; -import org.jboss.resteasy.reactive.common.model.ResourceContextResolver; import org.jboss.resteasy.reactive.common.model.ResourceDynamicFeature; -import org.jboss.resteasy.reactive.common.model.ResourceExceptionMapper; import org.jboss.resteasy.reactive.common.model.ResourceFeature; -import org.jboss.resteasy.reactive.common.model.ResourceInterceptor; import org.jboss.resteasy.reactive.common.model.ResourceInterceptors; import org.jboss.resteasy.reactive.common.model.ResourceMethod; -import org.jboss.resteasy.reactive.common.model.ResourceParamConverterProvider; import org.jboss.resteasy.reactive.common.model.ResourceReader; import org.jboss.resteasy.reactive.common.model.ResourceWriter; import org.jboss.resteasy.reactive.common.processor.AdditionalReaderWriter; import org.jboss.resteasy.reactive.common.processor.AdditionalReaders; import org.jboss.resteasy.reactive.common.processor.AdditionalWriters; import org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames; +import org.jboss.resteasy.reactive.common.processor.scanning.ApplicationScanningResult; +import org.jboss.resteasy.reactive.common.processor.scanning.ResourceScanningResult; import org.jboss.resteasy.reactive.common.util.Encode; -import org.jboss.resteasy.reactive.server.core.ContextResolvers; import org.jboss.resteasy.reactive.server.core.Deployment; import org.jboss.resteasy.reactive.server.core.DeploymentInfo; -import org.jboss.resteasy.reactive.server.core.DynamicFeatures; import org.jboss.resteasy.reactive.server.core.ExceptionMapping; -import org.jboss.resteasy.reactive.server.core.Features; -import org.jboss.resteasy.reactive.server.core.ParamConverterProviders; import org.jboss.resteasy.reactive.server.core.ServerSerialisers; +import org.jboss.resteasy.reactive.server.model.ContextResolvers; +import org.jboss.resteasy.reactive.server.model.DynamicFeatures; +import org.jboss.resteasy.reactive.server.model.Features; +import org.jboss.resteasy.reactive.server.model.ParamConverterProviders; import org.jboss.resteasy.reactive.spi.BeanFactory; import io.quarkus.arc.deployment.AdditionalBeanBuildItem; @@ -88,28 +85,30 @@ import io.quarkus.resteasy.reactive.common.deployment.ApplicationResultBuildItem; import io.quarkus.resteasy.reactive.common.deployment.FactoryUtils; import io.quarkus.resteasy.reactive.common.deployment.QuarkusFactoryCreator; +import io.quarkus.resteasy.reactive.common.deployment.ResourceInterceptorsBuildItem; import io.quarkus.resteasy.reactive.common.deployment.ResourceScanningResultBuildItem; import io.quarkus.resteasy.reactive.common.deployment.SerializersUtil; import io.quarkus.resteasy.reactive.common.runtime.ResteasyReactiveConfig; import io.quarkus.resteasy.reactive.server.runtime.ResteasyReactiveInitialiser; import io.quarkus.resteasy.reactive.server.runtime.ResteasyReactiveRecorder; import io.quarkus.resteasy.reactive.server.runtime.ServerVertxBufferMessageBodyWriter; +import io.quarkus.resteasy.reactive.server.runtime.exceptionmappers.AuthenticationCompletionExceptionMapper; import io.quarkus.resteasy.reactive.server.runtime.exceptionmappers.AuthenticationFailedExceptionMapper; +import io.quarkus.resteasy.reactive.server.runtime.exceptionmappers.AuthenticationRedirectExceptionMapper; +import io.quarkus.resteasy.reactive.server.runtime.exceptionmappers.ForbiddenExceptionMapper; import io.quarkus.resteasy.reactive.server.runtime.exceptionmappers.UnauthorizedExceptionMapper; -import io.quarkus.resteasy.reactive.spi.AbstractInterceptorBuildItem; -import io.quarkus.resteasy.reactive.spi.ContainerRequestFilterBuildItem; -import io.quarkus.resteasy.reactive.spi.ContainerResponseFilterBuildItem; -import io.quarkus.resteasy.reactive.spi.ContextResolverBuildItem; import io.quarkus.resteasy.reactive.spi.CustomExceptionMapperBuildItem; import io.quarkus.resteasy.reactive.spi.DynamicFeatureBuildItem; import io.quarkus.resteasy.reactive.spi.ExceptionMapperBuildItem; import io.quarkus.resteasy.reactive.spi.JaxrsFeatureBuildItem; import io.quarkus.resteasy.reactive.spi.MessageBodyReaderBuildItem; import io.quarkus.resteasy.reactive.spi.MessageBodyWriterBuildItem; -import io.quarkus.resteasy.reactive.spi.ParamConverterBuildItem; -import io.quarkus.resteasy.reactive.spi.ReaderInterceptorBuildItem; -import io.quarkus.resteasy.reactive.spi.WriterInterceptorBuildItem; import io.quarkus.runtime.RuntimeValue; +import io.quarkus.security.AuthenticationCompletionException; +import io.quarkus.security.AuthenticationFailedException; +import io.quarkus.security.AuthenticationRedirectException; +import io.quarkus.security.ForbiddenException; +import io.quarkus.security.UnauthorizedException; import io.quarkus.vertx.http.deployment.RouteBuildItem; import io.quarkus.vertx.http.runtime.BasicRoute; import io.quarkus.vertx.http.runtime.HttpBuildTimeConfig; @@ -146,9 +145,9 @@ void generateCustomProducer(Optional resourceSc return; } - Map resourcesThatNeedCustomProducer = resourceScanningResultBuildItem.get() + Map resourcesThatNeedCustomProducer = resourceScanningResultBuildItem.get().getResult() .getResourcesThatNeedCustomProducer(); - Set beanParams = resourceScanningResultBuildItem.get() + Set beanParams = resourceScanningResultBuildItem.get().getResult() .getBeanParams(); if (!resourcesThatNeedCustomProducer.isEmpty() || !beanParams.isEmpty()) { CustomResourceProducersGenerator.generate(resourcesThatNeedCustomProducer, beanParams, @@ -165,7 +164,8 @@ void handleClassLevelExceptionMappers(Optional if (!resourceScanningResultBuildItem.isPresent()) { return; } - List methodExceptionMapper = resourceScanningResultBuildItem.get().getClassLevelExceptionMappers(); + List methodExceptionMapper = resourceScanningResultBuildItem.get().getResult() + .getClassLevelExceptionMappers(); if (methodExceptionMapper.isEmpty()) { return; } @@ -207,24 +207,21 @@ public void setupEndpoints(Capabilities capabilities, BeanArchiveIndexBuildItem RecorderContext recorderContext, ShutdownContextBuildItem shutdownContext, HttpBuildTimeConfig vertxConfig, - List containerRequestFilters, - List containerResponseFilters, - List writerInterceptors, - List readerInterceptors, - List exceptionMappers, List dynamicFeatures, List additionalMessageBodyReaders, List additionalMessageBodyWriters, List features, - List paramConverterBuildItems, - List contextResolvers, Optional requestContextFactoryBuildItem, Optional classLevelExceptionMappers, BuildProducer quarkusRestDeploymentBuildItemBuildProducer, BuildProducer reflectiveClass, BuildProducer reflectiveHierarchy, BuildProducer routes, - ApplicationResultBuildItem applicationResultBuildItem) throws NoSuchMethodException { + ApplicationResultBuildItem applicationResultBuildItem, + ResourceInterceptorsBuildItem resourceInterceptorsBuildItem, + ExceptionMappersBuildItem exceptionMappersBuildItem, + ParamConverterProvidersBuildItem paramConverterProvidersBuildItem, + ContextResolversBuildItem contextResolversBuildItem) throws NoSuchMethodException { if (!resourceScanningResultBuildItem.isPresent()) { // no detected @Path, bail out @@ -243,27 +240,17 @@ public void setupEndpoints(Capabilities capabilities, BeanArchiveIndexBuildItem IndexView index = beanArchiveIndexBuildItem.getIndex(); - Map scannedResources = resourceScanningResultBuildItem.get().getScannedResources(); - Map scannedResourcePaths = resourceScanningResultBuildItem.get().getScannedResourcePaths(); - Map possibleSubResources = resourceScanningResultBuildItem.get().getPossibleSubResources(); - Map pathInterfaces = resourceScanningResultBuildItem.get().getPathInterfaces(); - - Set allowedClasses = applicationResultBuildItem.getAllowedClasses(); - Set singletonClasses = applicationResultBuildItem.getSingletonClasses(); - Set globalNameBindings = applicationResultBuildItem.getGlobalNameBindings(); - boolean filterClasses = applicationResultBuildItem.isFilterClasses(); - Application application = applicationResultBuildItem.getApplication(); - ClassInfo selectedAppClass = applicationResultBuildItem.getSelectedAppClass(); - - ParamConverterProviders converterProviders = new ParamConverterProviders(); - for (ParamConverterBuildItem paramConverter : paramConverterBuildItems) { - ResourceParamConverterProvider converter = new ResourceParamConverterProvider(); - converter.setFactory( - FactoryUtils.factory(paramConverter.getClassName(), singletonClasses, recorder, beanContainerBuildItem)); - converter.setPriority(paramConverter.getPriority()); - converterProviders.addParamConverterProviders(converter); - } - converterProviders.sort(); + ResourceScanningResult result = resourceScanningResultBuildItem.get().getResult(); + Map scannedResources = result.getScannedResources(); + Map scannedResourcePaths = result.getScannedResourcePaths(); + Map possibleSubResources = result.getPossibleSubResources(); + Map pathInterfaces = result.getPathInterfaces(); + + ApplicationScanningResult appResult = applicationResultBuildItem.getResult(); + Set allowedClasses = appResult.getAllowedClasses(); + Set singletonClasses = appResult.getSingletonClasses(); + boolean filterClasses = appResult.isFilterClasses(); + Application application = appResult.getApplication(); Map existingConverters = new HashMap<>(); List resourceClasses = new ArrayList<>(); @@ -271,12 +258,26 @@ public void setupEndpoints(Capabilities capabilities, BeanArchiveIndexBuildItem AdditionalReaders additionalReaders = new AdditionalReaders(); AdditionalWriters additionalWriters = new AdditionalWriters(); Map injectableBeans = new HashMap<>(); - ServerEndpointIndexer serverEndpointIndexer; + QuarkusServerEndpointIndexer serverEndpointIndexer; + + ResourceInterceptors interceptors = resourceInterceptorsBuildItem.getResourceInterceptors(); + ExceptionMapping exceptionMapping = exceptionMappersBuildItem.getExceptionMapping(); + ContextResolvers contextResolvers = contextResolversBuildItem.getContextResolvers(); + ParamConverterProviders paramConverterProviders = paramConverterProvidersBuildItem.getParamConverterProviders(); + Function> factoryFunction = s -> FactoryUtils.factory(s, singletonClasses, recorder, + beanContainerBuildItem); + interceptors.initializeDefaultFactories(factoryFunction); + exceptionMapping.initializeDefaultFactories(factoryFunction); + contextResolvers.initializeDefaultFactories(factoryFunction); + paramConverterProviders.initializeDefaultFactories(factoryFunction); + paramConverterProviders.sort(); + interceptors.sort(); + try (ClassCreator c = new ClassCreator(new GeneratedClassGizmoAdaptor(generatedClassBuildItemBuildProducer, true), QUARKUS_INIT_CLASS, null, Object.class.getName(), ResteasyReactiveInitialiser.class.getName()); MethodCreator initConverters = c.getMethodCreator("init", void.class, Deployment.class)) { - serverEndpointIndexer = new ServerEndpointIndexer.Builder() + serverEndpointIndexer = new QuarkusServerEndpointIndexer.Builder() .setIndex(index) .setFactoryCreator(new QuarkusFactoryCreator(recorder, beanContainerBuildItem.getValue())) .setEndpointInvokerFactory(new QuarkusInvokerFactory(generatedClassBuildItemBuildProducer, recorder)) @@ -286,10 +287,10 @@ public void setupEndpoints(Capabilities capabilities, BeanArchiveIndexBuildItem .setConfig(new org.jboss.resteasy.reactive.common.ResteasyReactiveConfig( config.inputBufferSize.asLongValue(), config.singleDefaultProduces)) .setAdditionalReaders(additionalReaders) - .setHttpAnnotationToMethod(resourceScanningResultBuildItem.get().getHttpAnnotationToMethod()) + .setHttpAnnotationToMethod(result.getHttpAnnotationToMethod()) .setInjectableBeans(injectableBeans).setAdditionalWriters(additionalWriters) - .setDefaultBlocking(applicationResultBuildItem.isBlocking()) - .setHasRuntimeConverters(!converterProviders.getParamConverterProviders().isEmpty()) + .setDefaultBlocking(appResult.isBlocking()) + .setHasRuntimeConverters(!paramConverterProviders.getParamConverterProviders().isEmpty()) .setClassLevelExceptionMappers( classLevelExceptionMappers.isPresent() ? classLevelExceptionMappers.get().getMappers() : Collections.emptyMap()) @@ -342,10 +343,6 @@ private boolean hasAnnotation(MethodInfo method, short paramPosition, DotName an }) .setInitConverters(initConverters).build(); - if (selectedAppClass != null) { - globalNameBindings = serverEndpointIndexer.nameBindingNames(selectedAppClass); - } - for (ClassInfo i : scannedResources.values()) { if (filterClasses && !allowedClasses.contains(i.name().toString())) { continue; @@ -361,7 +358,7 @@ private boolean hasAnnotation(MethodInfo method, short paramPosition, DotName an //now index possible sub resources. These are all classes that have method annotations //that are not annotated @Path Deque toScan = new ArrayDeque<>(); - for (DotName methodAnnotation : resourceScanningResultBuildItem.get().getHttpAnnotationToMethod().keySet()) { + for (DotName methodAnnotation : result.getHttpAnnotationToMethod().keySet()) { for (AnnotationInstance instance : index.getAnnotations(methodAnnotation)) { MethodInfo method = instance.target().asMethod(); ClassInfo classInfo = method.declaringClass(); @@ -386,53 +383,6 @@ private boolean hasAnnotation(MethodInfo method, short paramPosition, DotName an toScan.addAll(index.getKnownDirectSubclasses(classInfo.name())); } - ResourceInterceptors interceptors = new ResourceInterceptors(); - for (ContainerRequestFilterBuildItem filter : containerRequestFilters) { - registerInterceptors(beanContainerBuildItem, recorder, singletonClasses, - globalNameBindings, interceptors.getContainerRequestFilters(), filter); - } - - for (ContainerResponseFilterBuildItem filterClass : containerResponseFilters) { - registerInterceptors(beanContainerBuildItem, recorder, singletonClasses, - globalNameBindings, interceptors.getContainerResponseFilters(), filterClass); - } - for (WriterInterceptorBuildItem filterClass : writerInterceptors) { - registerInterceptors(beanContainerBuildItem, recorder, singletonClasses, - globalNameBindings, interceptors.getWriterInterceptors(), filterClass); - } - for (ReaderInterceptorBuildItem filterClass : readerInterceptors) { - registerInterceptors(beanContainerBuildItem, recorder, singletonClasses, - globalNameBindings, interceptors.getReaderInterceptors(), filterClass); - } - - ExceptionMapping exceptionMapping = new ExceptionMapping(); - Map> handledExceptionToHigherPriorityMapper = new HashMap<>(); - for (ExceptionMapperBuildItem additionalExceptionMapper : exceptionMappers) { - DotName handledExceptionDotName = DotName.createSimple(additionalExceptionMapper.getHandledExceptionName()); - int priority = Priorities.USER; - if (additionalExceptionMapper.getPriority() != null) { - priority = additionalExceptionMapper.getPriority(); - } - registerExceptionMapper(recorder, handledExceptionToHigherPriorityMapper, - beanContainerBuildItem, - additionalExceptionMapper.getClassName(), - handledExceptionDotName, - priority, singletonClasses); - } - for (Map.Entry> entry : handledExceptionToHigherPriorityMapper - .entrySet()) { - recorder.registerExceptionMapper(exceptionMapping, entry.getKey().toString(), entry.getValue()); - } - - ContextResolvers ctxResolvers = new ContextResolvers(); - for (ContextResolverBuildItem resolverClass : contextResolvers) { - ResourceContextResolver resolver = new ResourceContextResolver(); - resolver.setFactory( - FactoryUtils.factory(resolverClass.getClassName(), singletonClasses, recorder, beanContainerBuildItem)); - resolver.setMediaTypeStrings(resolverClass.getMediaTypes()); - recorder.registerContextResolver(ctxResolvers, resolverClass.getProvidedType(), resolver); - } - Features feats = new Features(); for (JaxrsFeatureBuildItem feature : features) { ResourceFeature resourceFeature = new ResourceFeature(); @@ -503,7 +453,7 @@ private boolean hasAnnotation(MethodInfo method, short paramPosition, DotName an .setConfig(new org.jboss.resteasy.reactive.common.ResteasyReactiveConfig( config.inputBufferSize.asLongValue(), config.singleDefaultProduces)) .setExceptionMapping(exceptionMapping) - .setCtxResolvers(ctxResolvers) + .setCtxResolvers(contextResolvers) .setFeatures(feats) .setClientProxyUnwrapper(new ClientProxyUnwrapper()) .setApplicationSupplier(recorder.handleApplication(applicationClass, singletonClasses.isEmpty())) @@ -513,7 +463,7 @@ private boolean hasAnnotation(MethodInfo method, short paramPosition, DotName an .setApplicationPath(applicationPath) .setResourceClasses(resourceClasses) .setLocatableResourceClasses(subResourceClasses) - .setParamConverterProviders(converterProviders), + .setParamConverterProviders(paramConverterProviders), beanContainerBuildItem.getValue(), shutdownContext, vertxConfig, requestContextFactoryBuildItem.map(RequestContextFactoryBuildItem::getFactory).orElse(null), initClassFactory); @@ -539,59 +489,29 @@ private boolean hasAnnotation(MethodInfo method, short paramPosition, DotName an } } - protected void registerInterceptors( - BeanContainerBuildItem beanContainerBuildItem, ResteasyReactiveRecorder recorder, - Set singletonClasses, Set globalNameBindings, - InterceptorContainer interceptors, B filterItem) { - ResourceInterceptor interceptor = interceptors.create(); - Integer priority = filterItem.getPriority(); - if (priority != null) { - interceptor.setPriority(priority); - } - interceptor - .setFactory( - FactoryUtils.factory(filterItem.getClassName(), singletonClasses, recorder, beanContainerBuildItem)); - if (interceptors instanceof PreMatchInterceptorContainer - && ((ContainerRequestFilterBuildItem) filterItem).isPreMatching()) { - ((PreMatchInterceptorContainer) interceptors).addPreMatchInterceptor(interceptor); - - } else { - Set nameBindingNames = filterItem.getNameBindingNames(); - if (nameBindingNames.isEmpty() || namePresent(nameBindingNames, globalNameBindings)) { - interceptors.addGlobalRequestInterceptor(interceptor); - } else { - interceptor.setNameBindingNames(nameBindingNames); - interceptors.addNameRequestInterceptor(interceptor); - } - } - - } - - private boolean namePresent(Set nameBindingNames, Set globalNameBindings) { - for (String i : globalNameBindings) { - if (nameBindingNames.contains(i)) { - return true; - } - } - return false; - } - - private void registerExceptionMapper(ResteasyReactiveRecorder recorder, - Map> handledExceptionToHigherPriorityMapper, - BeanContainerBuildItem beanContainerBuildItem, - String mapperClassName, - DotName handledExceptionDotName, int priority, Set singletonClasses) { - ResourceExceptionMapper mapper = new ResourceExceptionMapper<>(); - mapper.setPriority(priority); - mapper.setFactory(FactoryUtils.factory(mapperClassName, singletonClasses, recorder, beanContainerBuildItem)); - if (handledExceptionToHigherPriorityMapper.containsKey(handledExceptionDotName)) { - if (mapper.getPriority() < handledExceptionToHigherPriorityMapper.get(handledExceptionDotName) - .getPriority()) { - handledExceptionToHigherPriorityMapper.put(handledExceptionDotName, mapper); - } - } else { - handledExceptionToHigherPriorityMapper.put(handledExceptionDotName, mapper); - } + @BuildStep + public void securityExceptionMappers(BuildProducer exceptionMapperBuildItemBuildProducer) { + // built-ins + exceptionMapperBuildItemBuildProducer.produce(new ExceptionMapperBuildItem( + AuthenticationCompletionExceptionMapper.class.getName(), + AuthenticationCompletionException.class.getName(), + Priorities.USER, false)); + exceptionMapperBuildItemBuildProducer.produce(new ExceptionMapperBuildItem( + AuthenticationFailedExceptionMapper.class.getName(), + AuthenticationFailedException.class.getName(), + Priorities.USER + 1, false)); + exceptionMapperBuildItemBuildProducer.produce(new ExceptionMapperBuildItem( + AuthenticationRedirectExceptionMapper.class.getName(), + AuthenticationRedirectException.class.getName(), + Priorities.USER, false)); + exceptionMapperBuildItemBuildProducer.produce(new ExceptionMapperBuildItem( + ForbiddenExceptionMapper.class.getName(), + ForbiddenException.class.getName(), + Priorities.USER + 1, false)); + exceptionMapperBuildItemBuildProducer.produce(new ExceptionMapperBuildItem( + UnauthorizedExceptionMapper.class.getName(), + UnauthorizedException.class.getName(), + Priorities.USER + 1, false)); } private String determineApplicationPath(IndexView index) { diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveScanningProcessor.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveScanningProcessor.java index 747cdb4ea03b3..3c659a82847d7 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveScanningProcessor.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveScanningProcessor.java @@ -1,9 +1,9 @@ package io.quarkus.resteasy.reactive.server.deployment; -import static io.quarkus.resteasy.reactive.common.deployment.QuarkusResteasyReactiveDotNames.*; -import static io.quarkus.resteasy.reactive.server.deployment.ResteasyReactiveServerDotNames.*; +import static io.quarkus.resteasy.reactive.server.deployment.ResteasyReactiveServerDotNames.SERVER_REQUEST_FILTER; +import static io.quarkus.resteasy.reactive.server.deployment.ResteasyReactiveServerDotNames.SERVER_RESPONSE_FILTER; +import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.SERVER_EXCEPTION_MAPPER; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; @@ -11,6 +11,7 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.function.Consumer; import javax.ws.rs.Priorities; @@ -23,8 +24,20 @@ import org.jboss.jandex.IndexView; import org.jboss.jandex.Indexer; import org.jboss.jandex.MethodInfo; -import org.jboss.jandex.Type; +import org.jboss.resteasy.reactive.common.model.ResourceContextResolver; +import org.jboss.resteasy.reactive.common.model.ResourceExceptionMapper; +import org.jboss.resteasy.reactive.common.model.ResourceInterceptors; +import org.jboss.resteasy.reactive.common.model.ResourceParamConverterProvider; import org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames; +import org.jboss.resteasy.reactive.common.processor.scanning.ApplicationScanningResult; +import org.jboss.resteasy.reactive.common.processor.scanning.ResteasyReactiveInterceptorScanner; +import org.jboss.resteasy.reactive.server.core.ExceptionMapping; +import org.jboss.resteasy.reactive.server.model.ContextResolvers; +import org.jboss.resteasy.reactive.server.model.ParamConverterProviders; +import org.jboss.resteasy.reactive.server.processor.scanning.ResteasyReactiveContextResolverScanner; +import org.jboss.resteasy.reactive.server.processor.scanning.ResteasyReactiveExceptionMappingScanner; +import org.jboss.resteasy.reactive.server.processor.scanning.ResteasyReactiveFeatureScanner; +import org.jboss.resteasy.reactive.server.processor.scanning.ResteasyReactiveParamConverterScanner; import io.quarkus.arc.deployment.AdditionalBeanBuildItem; import io.quarkus.arc.deployment.GeneratedBeanBuildItem; @@ -34,15 +47,9 @@ import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.builditem.CombinedIndexBuildItem; import io.quarkus.deployment.index.IndexingUtil; -import io.quarkus.deployment.util.JandexUtil; import io.quarkus.resteasy.reactive.common.deployment.ApplicationResultBuildItem; +import io.quarkus.resteasy.reactive.common.deployment.ResourceInterceptorsContributorBuildItem; import io.quarkus.resteasy.reactive.common.deployment.ResourceScanningResultBuildItem; -import io.quarkus.resteasy.reactive.common.deployment.ResteasyReactiveCommonProcessor; -import io.quarkus.resteasy.reactive.server.runtime.exceptionmappers.AuthenticationCompletionExceptionMapper; -import io.quarkus.resteasy.reactive.server.runtime.exceptionmappers.AuthenticationFailedExceptionMapper; -import io.quarkus.resteasy.reactive.server.runtime.exceptionmappers.AuthenticationRedirectExceptionMapper; -import io.quarkus.resteasy.reactive.server.runtime.exceptionmappers.ForbiddenExceptionMapper; -import io.quarkus.resteasy.reactive.server.runtime.exceptionmappers.UnauthorizedExceptionMapper; import io.quarkus.resteasy.reactive.spi.ContainerRequestFilterBuildItem; import io.quarkus.resteasy.reactive.spi.ContainerResponseFilterBuildItem; import io.quarkus.resteasy.reactive.spi.ContextResolverBuildItem; @@ -53,11 +60,6 @@ import io.quarkus.resteasy.reactive.spi.ExceptionMapperBuildItem; import io.quarkus.resteasy.reactive.spi.JaxrsFeatureBuildItem; import io.quarkus.resteasy.reactive.spi.ParamConverterBuildItem; -import io.quarkus.security.AuthenticationCompletionException; -import io.quarkus.security.AuthenticationFailedException; -import io.quarkus.security.AuthenticationRedirectException; -import io.quarkus.security.ForbiddenException; -import io.quarkus.security.UnauthorizedException; /** * Processor that handles scanning for types and turning them into build items @@ -65,80 +67,80 @@ public class ResteasyReactiveScanningProcessor { @BuildStep - public void scanForFilters(CombinedIndexBuildItem combinedIndexBuildItem, + public ResourceInterceptorsContributorBuildItem scanForInterceptors(CombinedIndexBuildItem combinedIndexBuildItem, + ApplicationResultBuildItem applicationResultBuildItem) { + return new ResourceInterceptorsContributorBuildItem(new Consumer() { + @Override + public void accept(ResourceInterceptors interceptors) { + ResteasyReactiveInterceptorScanner.scanForInterceptors(interceptors, combinedIndexBuildItem.getIndex(), + applicationResultBuildItem.getResult()); + } + }); + } + + @BuildStep + public ExceptionMappersBuildItem scanForExceptionMappers(CombinedIndexBuildItem combinedIndexBuildItem, ApplicationResultBuildItem applicationResultBuildItem, - BuildProducer requestFilterBuildItemBuildProducer, - BuildProducer responseFilterBuildItemBuildProducer) { - IndexView index = combinedIndexBuildItem.getIndex(); - //the quarkus version of these filters will not be in the index - //so you need an explicit check for both - Collection containerResponseFilters = new HashSet<>(index - .getAllKnownImplementors(ResteasyReactiveDotNames.CONTAINER_RESPONSE_FILTER)); - containerResponseFilters.addAll(index - .getAllKnownImplementors(ResteasyReactiveDotNames.QUARKUS_REST_CONTAINER_RESPONSE_FILTER)); - Collection containerRequestFilters = new HashSet<>(index - .getAllKnownImplementors(ResteasyReactiveDotNames.CONTAINER_REQUEST_FILTER)); - containerRequestFilters.addAll(index - .getAllKnownImplementors(ResteasyReactiveDotNames.QUARKUS_REST_CONTAINER_REQUEST_FILTER)); - for (ClassInfo filterClass : containerRequestFilters) { - ResteasyReactiveCommonProcessor.handleDiscoveredInterceptor(applicationResultBuildItem, - requestFilterBuildItemBuildProducer, index, filterClass, - ContainerRequestFilterBuildItem.Builder::new); + BuildProducer additionalBeanBuildItemBuildProducer, + List mappers) { + AdditionalBeanBuildItem.Builder beanBuilder = AdditionalBeanBuildItem.builder().setUnremovable(); + ExceptionMapping exceptions = ResteasyReactiveExceptionMappingScanner + .scanForExceptionMappers(combinedIndexBuildItem.getComputingIndex(), applicationResultBuildItem.getResult()); + for (Map.Entry, ResourceExceptionMapper> i : exceptions.getMappers() + .entrySet()) { + beanBuilder.addBeanClass(i.getValue().getClassName()); } - for (ClassInfo filterClass : containerResponseFilters) { - ResteasyReactiveCommonProcessor.handleDiscoveredInterceptor(applicationResultBuildItem, - responseFilterBuildItemBuildProducer, index, filterClass, - ContainerResponseFilterBuildItem.Builder::new); + for (ExceptionMapperBuildItem additionalExceptionMapper : mappers) { + if (additionalExceptionMapper.isRegisterAsBean()) { + beanBuilder.addBeanClass(additionalExceptionMapper.getClassName()); + } + int priority = Priorities.USER; + if (additionalExceptionMapper.getPriority() != null) { + priority = additionalExceptionMapper.getPriority(); + } + ResourceExceptionMapper mapper = new ResourceExceptionMapper<>(); + mapper.setPriority(priority); + mapper.setClassName(additionalExceptionMapper.getClassName()); + try { + exceptions.addExceptionMapper((Class) Class.forName(additionalExceptionMapper.getHandledExceptionName(), false, + Thread.currentThread().getContextClassLoader()), mapper); + } catch (ClassNotFoundException e) { + throw new RuntimeException( + "Unable to load handled exception type " + additionalExceptionMapper.getHandledExceptionName(), e); + } } + additionalBeanBuildItemBuildProducer.produce(beanBuilder.build()); + return new ExceptionMappersBuildItem(exceptions); } @BuildStep - public void scanForExceptionMappers(CombinedIndexBuildItem combinedIndexBuildItem, + public ParamConverterProvidersBuildItem scanForParamConverters(CombinedIndexBuildItem combinedIndexBuildItem, + BuildProducer additionalBeanBuildItemBuildProducer, ApplicationResultBuildItem applicationResultBuildItem, - BuildProducer exceptionMapperBuildItemBuildProducer) { + List paramConverterBuildItems) { - IndexView index = combinedIndexBuildItem.getComputingIndex(); - Collection exceptionMappers = index - .getAllKnownImplementors(ResteasyReactiveDotNames.EXCEPTION_MAPPER); - for (ClassInfo mapperClass : exceptionMappers) { - ApplicationResultBuildItem.KeepProviderResult keepProviderResult = applicationResultBuildItem - .keepProvider(mapperClass); - if (keepProviderResult != ApplicationResultBuildItem.KeepProviderResult.DISCARD) { - List typeParameters = JandexUtil.resolveTypeParameters(mapperClass.name(), - ResteasyReactiveDotNames.EXCEPTION_MAPPER, - index); - DotName handledExceptionDotName = typeParameters.get(0).name(); - AnnotationInstance priorityInstance = mapperClass.classAnnotation(ResteasyReactiveDotNames.PRIORITY); - int priority = Priorities.USER; - if (priorityInstance != null) { - priority = priorityInstance.value().asInt(); - } - exceptionMapperBuildItemBuildProducer.produce(new ExceptionMapperBuildItem(mapperClass.name().toString(), - handledExceptionDotName.toString(), priority, true)); + AdditionalBeanBuildItem.Builder beanBuilder = AdditionalBeanBuildItem.builder().setUnremovable(); + ParamConverterProviders paramConverterProviders = ResteasyReactiveParamConverterScanner + .scanForParamConverters(combinedIndexBuildItem.getComputingIndex(), applicationResultBuildItem.getResult()); + for (ResourceParamConverterProvider i : paramConverterProviders.getParamConverterProviders()) { + beanBuilder.addBeanClass(i.getClassName()); + } + for (ParamConverterBuildItem additionalParamConverter : paramConverterBuildItems) { + if (additionalParamConverter.isRegisterAsBean()) { + beanBuilder.addBeanClass(additionalParamConverter.getClassName()); + } + int priority = Priorities.USER; + if (additionalParamConverter.getPriority() != null) { + priority = additionalParamConverter.getPriority(); } + ResourceParamConverterProvider provider = new ResourceParamConverterProvider(); + provider.setPriority(priority); + provider.setClassName(additionalParamConverter.getClassName()); + paramConverterProviders.addParamConverterProviders(provider); } + additionalBeanBuildItemBuildProducer.produce(beanBuilder.build()); + return new ParamConverterProvidersBuildItem(paramConverterProviders); - // built-ins - exceptionMapperBuildItemBuildProducer.produce(new ExceptionMapperBuildItem( - AuthenticationCompletionExceptionMapper.class.getName(), - AuthenticationCompletionException.class.getName(), - Priorities.USER, false)); - exceptionMapperBuildItemBuildProducer.produce(new ExceptionMapperBuildItem( - AuthenticationFailedExceptionMapper.class.getName(), - AuthenticationFailedException.class.getName(), - Priorities.USER + 1, false)); - exceptionMapperBuildItemBuildProducer.produce(new ExceptionMapperBuildItem( - AuthenticationRedirectExceptionMapper.class.getName(), - AuthenticationRedirectException.class.getName(), - Priorities.USER, false)); - exceptionMapperBuildItemBuildProducer.produce(new ExceptionMapperBuildItem( - ForbiddenExceptionMapper.class.getName(), - ForbiddenException.class.getName(), - Priorities.USER + 1, false)); - exceptionMapperBuildItemBuildProducer.produce(new ExceptionMapperBuildItem( - UnauthorizedExceptionMapper.class.getName(), - UnauthorizedException.class.getName(), - Priorities.USER + 1, false)); } @BuildStep @@ -146,16 +148,11 @@ public void scanForDynamicFeatures(CombinedIndexBuildItem combinedIndexBuildItem ApplicationResultBuildItem applicationResultBuildItem, BuildProducer dynamicFeatureBuildItemBuildProducer) { IndexView index = combinedIndexBuildItem.getComputingIndex(); - Collection dynamicFeatures = index - .getAllKnownImplementors(ResteasyReactiveDotNames.DYNAMIC_FEATURE); - - for (ClassInfo dynamicFeatureClass : dynamicFeatures) { - ApplicationResultBuildItem.KeepProviderResult keepProviderResult = applicationResultBuildItem - .keepProvider(dynamicFeatureClass); - if (keepProviderResult != ApplicationResultBuildItem.KeepProviderResult.DISCARD) { - dynamicFeatureBuildItemBuildProducer - .produce(new DynamicFeatureBuildItem(dynamicFeatureClass.name().toString(), true)); - } + Set features = ResteasyReactiveFeatureScanner.scanForDynamicFeatures(index, + applicationResultBuildItem.getResult()); + for (String dynamicFeatureClass : features) { + dynamicFeatureBuildItemBuildProducer + .produce(new DynamicFeatureBuildItem(dynamicFeatureClass, true)); } } @@ -164,38 +161,44 @@ public void scanForFeatures(CombinedIndexBuildItem combinedIndexBuildItem, ApplicationResultBuildItem applicationResultBuildItem, BuildProducer featureBuildItemBuildProducer) { IndexView index = combinedIndexBuildItem.getComputingIndex(); - Collection dynamicFeatures = index - .getAllKnownImplementors(ResteasyReactiveDotNames.FEATURE); - - for (ClassInfo dynamicFeatureClass : dynamicFeatures) { - ApplicationResultBuildItem.KeepProviderResult keepProviderResult = applicationResultBuildItem - .keepProvider(dynamicFeatureClass); - if (keepProviderResult != ApplicationResultBuildItem.KeepProviderResult.DISCARD) { - featureBuildItemBuildProducer - .produce(new JaxrsFeatureBuildItem(dynamicFeatureClass.name().toString(), true)); - } + Set features = ResteasyReactiveFeatureScanner.scanForFeatures(index, applicationResultBuildItem.getResult()); + for (String feature : features) { + featureBuildItemBuildProducer + .produce(new JaxrsFeatureBuildItem(feature, true)); } } @BuildStep - public void scanForContextResolvers(CombinedIndexBuildItem combinedIndexBuildItem, + public ContextResolversBuildItem scanForContextResolvers(CombinedIndexBuildItem combinedIndexBuildItem, ApplicationResultBuildItem applicationResultBuildItem, - BuildProducer contextResolverBuildItemBuildProducer) { + BuildProducer additionalBeanBuildItemBuildProducer, + List additionalResolvers) { IndexView index = combinedIndexBuildItem.getComputingIndex(); - Collection contextResolvers = index - .getAllKnownImplementors(ResteasyReactiveDotNames.CONTEXT_RESOLVER); - - for (ClassInfo resolverClass : contextResolvers) { - ApplicationResultBuildItem.KeepProviderResult keepProviderResult = applicationResultBuildItem - .keepProvider(resolverClass); - if (keepProviderResult != ApplicationResultBuildItem.KeepProviderResult.DISCARD) { - List typeParameters = JandexUtil.resolveTypeParameters(resolverClass.name(), - ResteasyReactiveDotNames.CONTEXT_RESOLVER, - index); - contextResolverBuildItemBuildProducer.produce(new ContextResolverBuildItem(resolverClass.name().toString(), - typeParameters.get(0).name().toString(), getProducesMediaTypes(resolverClass), true)); + AdditionalBeanBuildItem.Builder beanBuilder = AdditionalBeanBuildItem.builder().setUnremovable(); + ContextResolvers resolvers = ResteasyReactiveContextResolverScanner.scanForContextResolvers(index, + applicationResultBuildItem.getResult()); + for (Map.Entry, List> entry : resolvers.getResolvers().entrySet()) { + for (ResourceContextResolver i : entry.getValue()) { + beanBuilder.addBeanClass(i.getClassName()); } } + for (ContextResolverBuildItem i : additionalResolvers) { + if (i.isRegisterAsBean()) { + beanBuilder.addBeanClass(i.getClassName()); + } + ResourceContextResolver resolver = new ResourceContextResolver(); + resolver.setClassName(i.getClassName()); + resolver.setMediaTypeStrings(i.getMediaTypes()); + try { + resolvers.addContextResolver((Class) Class.forName(i.getProvidedType(), false, + Thread.currentThread().getContextClassLoader()), resolver); + } catch (ClassNotFoundException e) { + throw new RuntimeException( + "Unable to load handled exception type " + i.getProvidedType(), e); + } + } + additionalBeanBuildItemBuildProducer.produce(beanBuilder.build()); + return new ContextResolversBuildItem(resolvers); } @BuildStep @@ -207,9 +210,9 @@ public void scanForParamConverters(CombinedIndexBuildItem combinedIndexBuildItem .getAllKnownImplementors(ResteasyReactiveDotNames.PARAM_CONVERTER_PROVIDER); for (ClassInfo converterClass : paramConverterProviders) { - ApplicationResultBuildItem.KeepProviderResult keepProviderResult = applicationResultBuildItem + ApplicationScanningResult.KeepProviderResult keepProviderResult = applicationResultBuildItem.getResult() .keepProvider(converterClass); - if (keepProviderResult != ApplicationResultBuildItem.KeepProviderResult.DISCARD) { + if (keepProviderResult != ApplicationScanningResult.KeepProviderResult.DISCARD) { AnnotationInstance priorityInstance = converterClass.classAnnotation(ResteasyReactiveDotNames.PRIORITY); paramConverterBuildItemBuildProducer.produce(new ParamConverterBuildItem(converterClass.name().toString(), priorityInstance != null ? priorityInstance.value().asInt() : Priorities.USER, true)); @@ -298,7 +301,7 @@ public void handleCustomAnnotatedMethods( } Set classLevelExceptionMappers = new HashSet<>(resourceScanningResultBuildItem - .map(ResourceScanningResultBuildItem::getClassLevelExceptionMappers).orElse(Collections.emptyList())); + .map(s -> s.getResult().getClassLevelExceptionMappers()).orElse(Collections.emptyList())); for (AnnotationInstance instance : index .getAnnotations(SERVER_EXCEPTION_MAPPER)) { if (instance.target().kind() != AnnotationTarget.Kind.METHOD) { @@ -326,11 +329,4 @@ public void handleCustomAnnotatedMethods( additionalBean.produce(additionalBeans.setUnremovable().setDefaultScope(DotNames.SINGLETON).build()); } - private List getProducesMediaTypes(ClassInfo classInfo) { - AnnotationInstance produces = classInfo.classAnnotation(ResteasyReactiveDotNames.PRODUCES); - if (produces == null) { - return Collections.emptyList(); - } - return Arrays.asList(produces.value().asStringArray()); - } } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/ResteasyReactiveRecorder.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/ResteasyReactiveRecorder.java index a438520c85bf3..20b39e62607e7 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/ResteasyReactiveRecorder.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/ResteasyReactiveRecorder.java @@ -14,7 +14,6 @@ import org.jboss.resteasy.reactive.common.model.ResourceContextResolver; import org.jboss.resteasy.reactive.common.model.ResourceExceptionMapper; import org.jboss.resteasy.reactive.server.core.BlockingOperationSupport; -import org.jboss.resteasy.reactive.server.core.ContextResolvers; import org.jboss.resteasy.reactive.server.core.CurrentRequestManager; import org.jboss.resteasy.reactive.server.core.Deployment; import org.jboss.resteasy.reactive.server.core.DeploymentInfo; @@ -24,10 +23,12 @@ import org.jboss.resteasy.reactive.server.core.startup.RuntimeDeploymentManager; import org.jboss.resteasy.reactive.server.handlers.RestInitialHandler; import org.jboss.resteasy.reactive.server.jaxrs.ProvidersImpl; +import org.jboss.resteasy.reactive.server.model.ContextResolvers; import org.jboss.resteasy.reactive.server.spi.EndpointInvoker; import org.jboss.resteasy.reactive.server.spi.ServerRestHandler; import org.jboss.resteasy.reactive.server.util.RuntimeResourceVisitor; import org.jboss.resteasy.reactive.server.util.ScoreSystem; +import org.jboss.resteasy.reactive.server.vertx.ResteasyReactiveVertxHandler; import org.jboss.resteasy.reactive.spi.BeanFactory; import org.jboss.resteasy.reactive.spi.ThreadSetupAction; @@ -115,12 +116,7 @@ closeTaskHandler, contextFactory, new ArcThreadSetupAction(beanContainer.request public Handler handler(RuntimeValue deploymentRuntimeValue) { Deployment deployment = deploymentRuntimeValue.getValue(); RestInitialHandler initialHandler = new RestInitialHandler(deployment); - return new Handler() { - @Override - public void handle(RoutingContext event) { - initialHandler.beginProcessing(event); - } - }; + return new ResteasyReactiveVertxHandler(initialHandler); } /** 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 49bb1ab4456e9..497199722a49c 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 @@ -83,6 +83,7 @@ import org.jboss.resteasy.reactive.common.model.ParameterType; import org.jboss.resteasy.reactive.common.model.ResourceClass; import org.jboss.resteasy.reactive.common.model.ResourceMethod; +import org.jboss.resteasy.reactive.common.util.ReflectionBeanFactoryCreator; import org.jboss.resteasy.reactive.common.util.URLUtils; import org.jboss.resteasy.reactive.spi.BeanFactory; @@ -880,7 +881,7 @@ public Set nameBindingNames(MethodInfo methodInfo, Set forClass) } public static abstract class Builder, B extends Builder, METHOD extends ResourceMethod> { - private Function> factoryCreator; + private Function> factoryCreator = new ReflectionBeanFactoryCreator(); private boolean defaultBlocking; private IndexView index; private Map existingConverters; diff --git a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/JandexUtil.java b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/JandexUtil.java index f87ebaca7bb28..879bb8088b9c8 100644 --- a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/JandexUtil.java +++ b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/JandexUtil.java @@ -1,6 +1,10 @@ package org.jboss.resteasy.reactive.common.processor; +import java.io.IOException; +import java.io.InputStream; import java.lang.reflect.Modifier; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -8,12 +12,16 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Consumer; +import java.util.stream.Stream; import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.ArrayType; import org.jboss.jandex.ClassInfo; import org.jboss.jandex.ClassType; import org.jboss.jandex.DotName; +import org.jboss.jandex.Index; import org.jboss.jandex.IndexView; +import org.jboss.jandex.Indexer; import org.jboss.jandex.ParameterizedType; import org.jboss.jandex.Type; import org.jboss.jandex.Type.Kind; @@ -29,6 +37,27 @@ public final class JandexUtil { private JandexUtil() { } + public static Index createIndex(Path path) { + Indexer indexer = new Indexer(); + try (Stream files = Files.walk(path)) { + files.forEach(new Consumer() { + @Override + public void accept(Path path) { + if (path.toString().endsWith(".class")) { + try (InputStream in = Files.newInputStream(path)) { + indexer.index(in); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + }); + } catch (IOException e) { + throw new RuntimeException(e); + } + return indexer.complete(); + } + /** * Returns the captured generic types of an interface given a class that at some point in the class * hierarchy implements the interface. @@ -266,7 +295,7 @@ private static ClassInfo fetchFromIndex(DotName dotName, IndexView index) { * Returns the enclosing class of the given annotation instance. For field or method annotations this * will return the enclosing class. For parameters, this will return the enclosing class of the enclosing * method. For classes it will return the class itself. - * + * * @param annotationInstance the annotation whose enclosing class to look up. * @return the enclosing class. */ @@ -290,7 +319,7 @@ public static ClassInfo getEnclosingClass(AnnotationInstance annotationInstance) /** * Returns true if the given Jandex ClassInfo is a subclass of the given parentName. Note that this will * not check interfaces. - * + * * @param index the index to use to look up super classes. * @param info the ClassInfo we want to check. * @param parentName the name of the superclass we want to find. diff --git a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/ResteasyReactiveDotNames.java b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/ResteasyReactiveDotNames.java index a5fe195a4c96c..e9fd824ec9bf3 100644 --- a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/ResteasyReactiveDotNames.java +++ b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/ResteasyReactiveDotNames.java @@ -206,6 +206,9 @@ public final class ResteasyReactiveDotNames { .createSimple("org.jboss.resteasy.reactive.server.spi.ResteasyReactiveContainerRequestFilter"); public static final DotName OBJECT = DotName.createSimple(Object.class.getName()); + // TODO: fix this hack by moving all the logic that handles this annotation to the server processor + public static final DotName SERVER_EXCEPTION_MAPPER = DotName + .createSimple("org.jboss.resteasy.reactive.server.ServerExceptionMapper"); // Types ignored for reflection used by the RESTEasy and SmallRye REST client extensions. private static final Set TYPES_IGNORED_FOR_REFLECTION = new HashSet<>(Arrays.asList( // javax.json diff --git a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/scanning/ApplicationScanningResult.java b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/scanning/ApplicationScanningResult.java new file mode 100644 index 0000000000000..9aa4dc568fcd8 --- /dev/null +++ b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/scanning/ApplicationScanningResult.java @@ -0,0 +1,73 @@ +package org.jboss.resteasy.reactive.common.processor.scanning; + +import java.util.Set; +import javax.ws.rs.core.Application; +import org.jboss.jandex.ClassInfo; +import org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames; + +public final class ApplicationScanningResult { + + final Set allowedClasses; + final Set singletonClasses; + final Set globalNameBindings; + final boolean filterClasses; + final Application application; + final ClassInfo selectedAppClass; + final boolean blocking; + + public ApplicationScanningResult(Set allowedClasses, Set singletonClasses, Set globalNameBindings, + boolean filterClasses, Application application, ClassInfo selectedAppClass, boolean blocking) { + this.allowedClasses = allowedClasses; + this.singletonClasses = singletonClasses; + this.globalNameBindings = globalNameBindings; + this.filterClasses = filterClasses; + this.application = application; + this.selectedAppClass = selectedAppClass; + this.blocking = blocking; + } + + public KeepProviderResult keepProvider(ClassInfo providerClass) { + if (filterClasses) { + // we don't care about provider annotations, they're manually registered (but for the server only) + return allowedClasses.contains(providerClass.name().toString()) ? KeepProviderResult.SERVER_ONLY + : KeepProviderResult.DISCARD; + } + return providerClass.classAnnotation(ResteasyReactiveDotNames.PROVIDER) != null ? KeepProviderResult.NORMAL + : KeepProviderResult.DISCARD; + } + + public Set getAllowedClasses() { + return allowedClasses; + } + + public Set getSingletonClasses() { + return singletonClasses; + } + + public Set getGlobalNameBindings() { + return globalNameBindings; + } + + public boolean isFilterClasses() { + return filterClasses; + } + + public Application getApplication() { + return application; + } + + public ClassInfo getSelectedAppClass() { + return selectedAppClass; + } + + public boolean isBlocking() { + return blocking; + } + + public enum KeepProviderResult { + NORMAL, + SERVER_ONLY, + DISCARD + } + +} diff --git a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/scanning/ResourceScanningResult.java b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/scanning/ResourceScanningResult.java new file mode 100644 index 0000000000000..ccdb28231fe64 --- /dev/null +++ b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/scanning/ResourceScanningResult.java @@ -0,0 +1,66 @@ +package org.jboss.resteasy.reactive.common.processor.scanning; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.DotName; +import org.jboss.jandex.MethodInfo; + +public final class ResourceScanningResult { + + final Map scannedResources; + final Map scannedResourcePaths; + final Map possibleSubResources; + final Map pathInterfaces; + final Map resourcesThatNeedCustomProducer; + final Set beanParams; + final Map httpAnnotationToMethod; + final List classLevelExceptionMappers; + + public ResourceScanningResult(Map scannedResources, Map scannedResourcePaths, + Map possibleSubResources, Map pathInterfaces, + Map resourcesThatNeedCustomProducer, + Set beanParams, Map httpAnnotationToMethod, List classLevelExceptionMappers) { + this.scannedResources = scannedResources; + this.scannedResourcePaths = scannedResourcePaths; + this.possibleSubResources = possibleSubResources; + this.pathInterfaces = pathInterfaces; + this.resourcesThatNeedCustomProducer = resourcesThatNeedCustomProducer; + this.beanParams = beanParams; + this.httpAnnotationToMethod = httpAnnotationToMethod; + this.classLevelExceptionMappers = classLevelExceptionMappers; + } + + public Map getScannedResources() { + return scannedResources; + } + + public Map getScannedResourcePaths() { + return scannedResourcePaths; + } + + public Map getPossibleSubResources() { + return possibleSubResources; + } + + public Map getPathInterfaces() { + return pathInterfaces; + } + + public Map getResourcesThatNeedCustomProducer() { + return resourcesThatNeedCustomProducer; + } + + public Set getBeanParams() { + return beanParams; + } + + public Map getHttpAnnotationToMethod() { + return httpAnnotationToMethod; + } + + public List getClassLevelExceptionMappers() { + return classLevelExceptionMappers; + } +} diff --git a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/scanning/ResteasyReactiveInterceptorScanner.java b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/scanning/ResteasyReactiveInterceptorScanner.java new file mode 100644 index 0000000000000..d456ef77e4567 --- /dev/null +++ b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/scanning/ResteasyReactiveInterceptorScanner.java @@ -0,0 +1,119 @@ +package org.jboss.resteasy.reactive.common.processor.scanning; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import java.util.function.Function; +import org.jboss.jandex.AnnotationInstance; +import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.IndexView; +import org.jboss.resteasy.reactive.common.model.InterceptorContainer; +import org.jboss.resteasy.reactive.common.model.PreMatchInterceptorContainer; +import org.jboss.resteasy.reactive.common.model.ResourceInterceptor; +import org.jboss.resteasy.reactive.common.model.ResourceInterceptors; +import org.jboss.resteasy.reactive.common.processor.NameBindingUtil; +import org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames; +import org.jboss.resteasy.reactive.common.util.ReflectionBeanFactoryCreator; +import org.jboss.resteasy.reactive.spi.BeanFactory; + +/** + * Scanner that is responsible for scanning for interceptor classes, such as container filters and IO interceptors + */ +public class ResteasyReactiveInterceptorScanner { + + private ResteasyReactiveInterceptorScanner() { + + } + + /** + * Creates a fully populated resource interceptors instance, that are created via reflection. + */ + public static ResourceInterceptors createResourceInterceptors(IndexView indexView, ApplicationScanningResult result) { + return createResourceInterceptors(indexView, result, new ReflectionBeanFactoryCreator()); + } + + /** + * Creates a fully populated resource interceptors instance, that are created via the provided factory creator + */ + public static ResourceInterceptors createResourceInterceptors(IndexView indexView, ApplicationScanningResult result, + Function> factoryCreator) { + ResourceInterceptors interceptors = new ResourceInterceptors(); + scanForInterceptors(interceptors, indexView, result); + scanForIOInterceptors(interceptors, indexView, result); + interceptors.initializeDefaultFactories(factoryCreator); + return interceptors; + } + + public static void scanForInterceptors(ResourceInterceptors interceptors, IndexView index, + ApplicationScanningResult applicationScanningResult) { + //the quarkus version of these filters will not be in the index + //so you need an explicit check for both + Collection containerResponseFilters = new HashSet<>(index + .getAllKnownImplementors(ResteasyReactiveDotNames.CONTAINER_RESPONSE_FILTER)); + containerResponseFilters.addAll(index + .getAllKnownImplementors(ResteasyReactiveDotNames.QUARKUS_REST_CONTAINER_RESPONSE_FILTER)); + Collection containerRequestFilters = new HashSet<>(index + .getAllKnownImplementors(ResteasyReactiveDotNames.CONTAINER_REQUEST_FILTER)); + containerRequestFilters.addAll(index + .getAllKnownImplementors(ResteasyReactiveDotNames.QUARKUS_REST_CONTAINER_REQUEST_FILTER)); + for (ClassInfo filterClass : containerRequestFilters) { + handleDiscoveredInterceptor(applicationScanningResult, interceptors.getContainerRequestFilters(), + index, filterClass); + } + for (ClassInfo filterClass : containerResponseFilters) { + handleDiscoveredInterceptor(applicationScanningResult, interceptors.getContainerResponseFilters(), + index, filterClass); + } + } + + public static void scanForIOInterceptors(ResourceInterceptors interceptors, IndexView index, + ApplicationScanningResult applicationScanningResult) { + Collection readerInterceptors = index + .getAllKnownImplementors(ResteasyReactiveDotNames.READER_INTERCEPTOR); + Collection writerInterceptors = index + .getAllKnownImplementors(ResteasyReactiveDotNames.WRITER_INTERCEPTOR); + + for (ClassInfo filterClass : writerInterceptors) { + handleDiscoveredInterceptor(applicationScanningResult, interceptors.getWriterInterceptors(), index, filterClass); + } + for (ClassInfo filterClass : readerInterceptors) { + handleDiscoveredInterceptor(applicationScanningResult, interceptors.getReaderInterceptors(), index, filterClass); + } + } + + private static void handleDiscoveredInterceptor( + ApplicationScanningResult applicationResultBuildItem, InterceptorContainer interceptorContainer, IndexView index, + ClassInfo filterClass) { + ApplicationScanningResult.KeepProviderResult keepProviderResult = applicationResultBuildItem.keepProvider(filterClass); + if (keepProviderResult != ApplicationScanningResult.KeepProviderResult.DISCARD) { + ResourceInterceptor interceptor = interceptorContainer.create(); + interceptor.setClassName(filterClass.name().toString()); + interceptor.setNameBindingNames(NameBindingUtil.nameBindingNames(index, filterClass)); + AnnotationInstance priorityInstance = filterClass.classAnnotation(ResteasyReactiveDotNames.PRIORITY); + if (priorityInstance != null) { + interceptor.setPriority(priorityInstance.value().asInt()); + } + if (interceptorContainer instanceof PreMatchInterceptorContainer + && filterClass.classAnnotation(ResteasyReactiveDotNames.PRE_MATCHING) != null) { + ((PreMatchInterceptorContainer) interceptorContainer).addPreMatchInterceptor(interceptor); + } else { + Set nameBindingNames = interceptor.getNameBindingNames(); + if (nameBindingNames.isEmpty() + || namePresent(nameBindingNames, applicationResultBuildItem.getGlobalNameBindings())) { + interceptorContainer.addGlobalRequestInterceptor(interceptor); + } else { + interceptorContainer.addNameRequestInterceptor(interceptor); + } + } + } + } + + private static boolean namePresent(Set nameBindingNames, Set globalNameBindings) { + for (String i : globalNameBindings) { + if (nameBindingNames.contains(i)) { + return true; + } + } + return false; + } +} diff --git a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/scanning/ResteasyReactiveScanner.java b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/scanning/ResteasyReactiveScanner.java new file mode 100644 index 0000000000000..4c58d58cd4cb1 --- /dev/null +++ b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/scanning/ResteasyReactiveScanner.java @@ -0,0 +1,220 @@ +package org.jboss.resteasy.reactive.common.processor.scanning; + +import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.DELETE; +import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.GET; +import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.HEAD; +import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.OPTIONS; +import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.PATCH; +import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.POST; +import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.PUT; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import javax.ws.rs.core.Application; +import org.jboss.jandex.AnnotationInstance; +import org.jboss.jandex.AnnotationTarget; +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.resteasy.reactive.common.processor.NameBindingUtil; +import org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames; + +public class ResteasyReactiveScanner { + + private static Map BUILTIN_HTTP_ANNOTATIONS_TO_METHOD = new HashMap<>(); + + static { + BUILTIN_HTTP_ANNOTATIONS_TO_METHOD.put(GET, "GET"); + BUILTIN_HTTP_ANNOTATIONS_TO_METHOD.put(POST, "POST"); + BUILTIN_HTTP_ANNOTATIONS_TO_METHOD.put(HEAD, "HEAD"); + BUILTIN_HTTP_ANNOTATIONS_TO_METHOD.put(PUT, "PUT"); + BUILTIN_HTTP_ANNOTATIONS_TO_METHOD.put(DELETE, "DELETE"); + BUILTIN_HTTP_ANNOTATIONS_TO_METHOD.put(PATCH, "PATCH"); + BUILTIN_HTTP_ANNOTATIONS_TO_METHOD.put(OPTIONS, "OPTIONS"); + BUILTIN_HTTP_ANNOTATIONS_TO_METHOD = Collections.unmodifiableMap(BUILTIN_HTTP_ANNOTATIONS_TO_METHOD); + } + + public static ApplicationScanningResult scanForApplicationClass(IndexView index) { + Collection applications = index + .getAllKnownSubclasses(ResteasyReactiveDotNames.APPLICATION); + Set allowedClasses = new HashSet<>(); + Set singletonClasses = new HashSet<>(); + Set globalNameBindings = new HashSet<>(); + boolean filterClasses = false; + Application application = null; + ClassInfo selectedAppClass = null; + boolean blocking = false; + for (ClassInfo applicationClassInfo : applications) { + if (selectedAppClass != null) { + throw new RuntimeException("More than one Application class: " + applications); + } + selectedAppClass = applicationClassInfo; + // FIXME: yell if there's more than one + String applicationClass = applicationClassInfo.name().toString(); + try { + Class appClass = Thread.currentThread().getContextClassLoader().loadClass(applicationClass); + application = (Application) appClass.getConstructor().newInstance(); + Set> classes = application.getClasses(); + if (!classes.isEmpty()) { + for (Class klass : classes) { + allowedClasses.add(klass.getName()); + } + filterClasses = true; + } + classes = application.getSingletons().stream().map(Object::getClass).collect(Collectors.toSet()); + if (!classes.isEmpty()) { + for (Class klass : classes) { + allowedClasses.add(klass.getName()); + singletonClasses.add(klass.getName()); + } + filterClasses = true; + } + } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException + | InvocationTargetException e) { + throw new RuntimeException("Unable to handle class: " + applicationClass, e); + } + if (applicationClassInfo.classAnnotation(ResteasyReactiveDotNames.NON_BLOCKING) != null) { + blocking = false; + } else if (applicationClassInfo.classAnnotation(ResteasyReactiveDotNames.NON_BLOCKING) != null) { + blocking = true; + } + } + if (selectedAppClass != null) { + globalNameBindings = NameBindingUtil.nameBindingNames(index, selectedAppClass); + } + return new ApplicationScanningResult(allowedClasses, singletonClasses, globalNameBindings, filterClasses, application, + selectedAppClass, blocking); + } + + public static ResourceScanningResult scanResources( + IndexView index) { + Collection paths = index.getAnnotations(ResteasyReactiveDotNames.PATH); + + Collection allPaths = new ArrayList<>(paths); + + if (allPaths.isEmpty()) { + // no detected @Path, bail out + return null; + } + + Map scannedResources = new HashMap<>(); + Map scannedResourcePaths = new HashMap<>(); + Map possibleSubResources = new HashMap<>(); + Map pathInterfaces = new HashMap<>(); + Map resourcesThatNeedCustomProducer = new HashMap<>(); + List methodExceptionMappers = new ArrayList<>(); + Set beanParams = new HashSet<>(); + + for (AnnotationInstance beanParamAnnotation : index.getAnnotations(ResteasyReactiveDotNames.BEAN_PARAM)) { + AnnotationTarget target = beanParamAnnotation.target(); + // FIXME: this isn't right wrt generics + switch (target.kind()) { + case FIELD: + beanParams.add(target.asField().type().toString()); + break; + case METHOD: + Type setterParamType = target.asMethod().parameters().get(0); + beanParams.add(setterParamType.toString()); + break; + case METHOD_PARAMETER: + MethodInfo method = target.asMethodParameter().method(); + int paramIndex = target.asMethodParameter().position(); + Type paramType = method.parameters().get(paramIndex); + beanParams.add(paramType.toString()); + break; + default: + break; + } + } + + for (AnnotationInstance annotation : allPaths) { + if (annotation.target().kind() == AnnotationTarget.Kind.CLASS) { + ClassInfo clazz = annotation.target().asClass(); + if (!Modifier.isInterface(clazz.flags())) { + scannedResources.put(clazz.name(), clazz); + scannedResourcePaths.put(clazz.name(), annotation.value().asString()); + } else { + pathInterfaces.put(clazz.name(), annotation.value().asString()); + } + MethodInfo ctor = hasJaxRsCtorParams(clazz); + if (ctor != null) { + resourcesThatNeedCustomProducer.put(clazz.name(), ctor); + } + List exceptionMapperAnnotationInstances = clazz.annotations() + .get(ResteasyReactiveDotNames.SERVER_EXCEPTION_MAPPER); + if (exceptionMapperAnnotationInstances != null) { + for (AnnotationInstance instance : exceptionMapperAnnotationInstances) { + if (instance.target().kind() != AnnotationTarget.Kind.METHOD) { + continue; + } + methodExceptionMappers.add(instance.target().asMethod()); + } + } + } + } + + for (Map.Entry i : pathInterfaces.entrySet()) { + for (ClassInfo clazz : index.getAllKnownImplementors(i.getKey())) { + if (!Modifier.isAbstract(clazz.flags())) { + if ((clazz.enclosingClass() == null || Modifier.isStatic(clazz.flags())) && + clazz.enclosingMethod() == null) { + if (!scannedResources.containsKey(clazz.name())) { + scannedResources.put(clazz.name(), clazz); + scannedResourcePaths.put(clazz.name(), i.getValue()); + } + } + } + } + } + + Map httpAnnotationToMethod = new HashMap<>(BUILTIN_HTTP_ANNOTATIONS_TO_METHOD); + Collection httpMethodInstances = index.getAnnotations(ResteasyReactiveDotNames.HTTP_METHOD); + for (AnnotationInstance httpMethodInstance : httpMethodInstances) { + if (httpMethodInstance.target().kind() != AnnotationTarget.Kind.CLASS) { + continue; + } + httpAnnotationToMethod.put(httpMethodInstance.target().asClass().name(), httpMethodInstance.value().asString()); + } + return new ResourceScanningResult(scannedResources, + scannedResourcePaths, possibleSubResources, pathInterfaces, resourcesThatNeedCustomProducer, beanParams, + httpAnnotationToMethod, methodExceptionMappers); + } + + private static MethodInfo hasJaxRsCtorParams(ClassInfo classInfo) { + List methods = classInfo.methods(); + List ctors = new ArrayList<>(); + for (MethodInfo method : methods) { + if (method.name().equals("")) { + ctors.add(method); + } + } + if (ctors.size() != 1) { // we only need to deal with a single ctor here + return null; + } + MethodInfo ctor = ctors.get(0); + if (ctor.parameters().size() == 0) { // default ctor - we don't need to do anything + return null; + } + + boolean needsHandling = false; + for (DotName dotName : ResteasyReactiveDotNames.RESOURCE_CTOR_PARAMS_THAT_NEED_HANDLING) { + if (ctor.hasAnnotation(dotName)) { + needsHandling = true; + break; + } + } + return needsHandling ? ctor : null; + } + +} diff --git a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/InterceptorContainer.java b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/InterceptorContainer.java index c890072b32f2f..64d2ac7e13025 100644 --- a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/InterceptorContainer.java +++ b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/InterceptorContainer.java @@ -3,6 +3,8 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.function.Function; +import org.jboss.resteasy.reactive.spi.BeanFactory; public class InterceptorContainer { @@ -34,6 +36,19 @@ public ResourceInterceptor create() { return new ResourceInterceptor<>(); } + public void initializeDefaultFactories(Function> factoryCreator) { + for (ResourceInterceptor i : globalResourceInterceptors) { + if (i.getFactory() == null) { + i.setFactory((BeanFactory) factoryCreator.apply(i.getClassName())); + } + } + for (ResourceInterceptor i : nameResourceInterceptors) { + if (i.getFactory() == null) { + i.setFactory((BeanFactory) factoryCreator.apply(i.getClassName())); + } + } + } + public static class Reversed extends InterceptorContainer { public ResourceInterceptor create() { diff --git a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/PreMatchInterceptorContainer.java b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/PreMatchInterceptorContainer.java index 3e865f2823656..05e015ee2e40b 100644 --- a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/PreMatchInterceptorContainer.java +++ b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/PreMatchInterceptorContainer.java @@ -3,6 +3,8 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.function.Function; +import org.jboss.resteasy.reactive.spi.BeanFactory; public class PreMatchInterceptorContainer extends InterceptorContainer { private final List> preMatchInterceptors = new ArrayList<>(); @@ -15,6 +17,15 @@ public List> getPreMatchInterceptors() { return preMatchInterceptors; } + public void initializeDefaultFactories(Function> factoryCreator) { + super.initializeDefaultFactories(factoryCreator); + for (ResourceInterceptor i : preMatchInterceptors) { + if (i.getFactory() == null) { + i.setFactory((BeanFactory) factoryCreator.apply(i.getClassName())); + } + } + } + @Override public void sort() { super.sort(); diff --git a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/ResourceContextResolver.java b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/ResourceContextResolver.java index 98d75c18eff61..1b57eb9fdc30d 100644 --- a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/ResourceContextResolver.java +++ b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/ResourceContextResolver.java @@ -10,6 +10,7 @@ public class ResourceContextResolver { private BeanFactory> factory; + private String className; private List mediaTypeStrings = new ArrayList<>(); private volatile List mediaTypes; @@ -21,6 +22,15 @@ public BeanFactory> getFactory() { return factory; } + public String getClassName() { + return className; + } + + public ResourceContextResolver setClassName(String className) { + this.className = className; + return this; + } + public ResourceContextResolver setMediaTypeStrings(List mediaTypeStrings) { this.mediaTypeStrings = mediaTypeStrings; return this; diff --git a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/ResourceExceptionMapper.java b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/ResourceExceptionMapper.java index 217c0b2b8e385..d5a72e7429ac9 100644 --- a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/ResourceExceptionMapper.java +++ b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/ResourceExceptionMapper.java @@ -7,7 +7,8 @@ public class ResourceExceptionMapper { private BeanFactory> factory; - private Integer priority = Priorities.USER; + private int priority = Priorities.USER; + private String className; public void setFactory(BeanFactory> factory) { this.factory = factory; @@ -17,11 +18,20 @@ public BeanFactory> getFactory() { return factory; } - public Integer getPriority() { + public int getPriority() { return priority; } - public void setPriority(Integer priority) { + public void setPriority(int priority) { this.priority = priority; } + + public String getClassName() { + return className; + } + + public ResourceExceptionMapper setClassName(String className) { + this.className = className; + return this; + } } diff --git a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/ResourceInterceptor.java b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/ResourceInterceptor.java index 1700ad5138643..8fbe64f043ca4 100644 --- a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/ResourceInterceptor.java +++ b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/ResourceInterceptor.java @@ -16,6 +16,8 @@ public class ResourceInterceptor */ private Set nameBindingNames = Collections.emptySet(); + private String className; + public void setFactory(BeanFactory factory) { this.factory = factory; } @@ -44,6 +46,15 @@ public void setNameBindingNames(Set nameBindingNames) { this.nameBindingNames = nameBindingNames; } + public String getClassName() { + return className; + } + + public ResourceInterceptor setClassName(String className) { + this.className = className; + return this; + } + // spec says that writer interceptors are sorted in ascending order @Override public int compareTo(ResourceInterceptor o) { diff --git a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/ResourceInterceptors.java b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/ResourceInterceptors.java index 691970ee3fca7..97e12ebc3e897 100644 --- a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/ResourceInterceptors.java +++ b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/ResourceInterceptors.java @@ -1,9 +1,11 @@ package org.jboss.resteasy.reactive.common.model; +import java.util.function.Function; import javax.ws.rs.container.ContainerRequestFilter; import javax.ws.rs.container.ContainerResponseFilter; import javax.ws.rs.ext.ReaderInterceptor; import javax.ws.rs.ext.WriterInterceptor; +import org.jboss.resteasy.reactive.spi.BeanFactory; public class ResourceInterceptors { @@ -50,6 +52,13 @@ public ResourceInterceptors setReaderInterceptors(InterceptorContainer> factoryCreator) { + containerRequestFilters.initializeDefaultFactories(factoryCreator); + containerResponseFilters.initializeDefaultFactories(factoryCreator); + readerInterceptors.initializeDefaultFactories(factoryCreator); + writerInterceptors.initializeDefaultFactories(factoryCreator); + } + // we sort this at build time as the order of the elements in the lists is retained in generated bytecode // therefore at runtime the elements are already properly sorted public ResourceInterceptors sort() { diff --git a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/ResourceParamConverterProvider.java b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/ResourceParamConverterProvider.java index fce02b9485105..0237c67b2ce1b 100644 --- a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/ResourceParamConverterProvider.java +++ b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/ResourceParamConverterProvider.java @@ -8,6 +8,7 @@ public class ResourceParamConverterProvider implements Comparable factory; private Integer priority = Priorities.USER; + private String className; public void setFactory(BeanFactory factory) { this.factory = factory; @@ -25,6 +26,15 @@ public void setPriority(Integer priority) { this.priority = priority; } + public String getClassName() { + return className; + } + + public ResourceParamConverterProvider setClassName(String className) { + this.className = className; + return this; + } + @Override public int compareTo(ResourceParamConverterProvider o) { return this.priority.compareTo(o.priority); diff --git a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/ResourceWriter.java b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/ResourceWriter.java index cdb49b2d72aab..281463f495d9b 100644 --- a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/ResourceWriter.java +++ b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/ResourceWriter.java @@ -22,8 +22,9 @@ public class ResourceWriter { private volatile ServerMediaType serverMediaType; private volatile MessageBodyWriter instance; - public void setFactory(BeanFactory> factory) { + public ResourceWriter setFactory(BeanFactory> factory) { this.factory = factory; + return this; } public BeanFactory> getFactory() { diff --git a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/util/ReflectionBeanFactory.java b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/util/ReflectionBeanFactory.java new file mode 100644 index 0000000000000..f1755ad72dac5 --- /dev/null +++ b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/util/ReflectionBeanFactory.java @@ -0,0 +1,32 @@ +package org.jboss.resteasy.reactive.common.util; + +import org.jboss.resteasy.reactive.spi.BeanFactory; + +public class ReflectionBeanFactory implements BeanFactory { + private final String className; + + public ReflectionBeanFactory(String className) { + this.className = className; + } + + @Override + public BeanInstance createInstance() { + try { + T instance = (T) Class.forName(className, false, Thread.currentThread().getContextClassLoader()) + .newInstance(); + return new BeanInstance() { + @Override + public T getInstance() { + return instance; + } + + @Override + public void close() { + + } + }; + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/util/ReflectionBeanFactoryCreator.java b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/util/ReflectionBeanFactoryCreator.java new file mode 100644 index 0000000000000..236ca4bf95e27 --- /dev/null +++ b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/util/ReflectionBeanFactoryCreator.java @@ -0,0 +1,12 @@ +package org.jboss.resteasy.reactive.common.util; + +import java.util.function.Function; +import org.jboss.resteasy.reactive.spi.BeanFactory; + +public class ReflectionBeanFactoryCreator implements Function> { + @Override + public BeanFactory apply(String className) { + return new ReflectionBeanFactory(className); + } + +} diff --git a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/spi/ThreadSetupAction.java b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/spi/ThreadSetupAction.java index 24a97170bb1a1..8f5e78ede8f66 100644 --- a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/spi/ThreadSetupAction.java +++ b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/spi/ThreadSetupAction.java @@ -11,4 +11,26 @@ interface ThreadState { void deactivate(); } + + ThreadSetupAction NOOP = new ThreadSetupAction() { + @Override + public ThreadState activateInitial() { + return new ThreadState() { + @Override + public void close() { + + } + + @Override + public void activate() { + + } + + @Override + public void deactivate() { + + } + }; + } + }; } diff --git a/independent-projects/resteasy-reactive/pom.xml b/independent-projects/resteasy-reactive/pom.xml index d22e9266ec670..f8c688be90263 100644 --- a/independent-projects/resteasy-reactive/pom.xml +++ b/independent-projects/resteasy-reactive/pom.xml @@ -52,6 +52,9 @@ 0.10.1 1.5.0 3.9.3 + 4.3.2 + 1.0.0.Final + 2.0.0.Final @@ -158,6 +161,13 @@ pom + + org.jboss.shrinkwrap + shrinkwrap-depchain + pom + test + 1.2.6 + org.apache.maven @@ -207,6 +217,46 @@ test + + io.rest-assured + rest-assured + ${rest-assured.version} + test + + + javax.activation + activation + + + javax.activation + javax.activation-api + + + + com.sun.xml.bind + jaxb-osgi + + + commons-logging + commons-logging + + + jakarta.xml.bind + jakarta.xml.bind-api + + + + + org.jboss.logging + commons-logging-jboss-logging + ${commons-logging-jboss-logging.version} + + + org.jboss.spec.javax.xml.bind + jboss-jaxb-api_2.3_spec + ${jboss-jaxb-api_2.3_spec.version} + test + diff --git a/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/ReflectionEndpointInvokerFactory.java b/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/ReflectionEndpointInvokerFactory.java new file mode 100644 index 0000000000000..3d2b37d740626 --- /dev/null +++ b/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/ReflectionEndpointInvokerFactory.java @@ -0,0 +1,79 @@ +package org.jboss.resteasy.reactive.server.processor; + +import java.lang.reflect.Method; +import java.util.List; +import java.util.function.Supplier; +import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.MethodInfo; +import org.jboss.jandex.PrimitiveType; +import org.jboss.jandex.Type; +import org.jboss.resteasy.reactive.common.model.ResourceMethod; +import org.jboss.resteasy.reactive.server.spi.EndpointInvoker; + +public class ReflectionEndpointInvokerFactory implements EndpointInvokerFactory { + + @Override + public Supplier create(ResourceMethod method, ClassInfo currentClass, + MethodInfo currentMethod) { + return new Supplier() { + @Override + public EndpointInvoker get() { + + try { + Class clazz = Class.forName(currentMethod.declaringClass().name().toString(), false, + Thread.currentThread().getContextClassLoader()); + Method meth = clazz.getDeclaredMethod(currentMethod.name(), toParamArray(currentMethod.parameters())); + return new EndpointInvoker() { + @Override + public Object invoke(Object instance, Object[] parameters) throws Exception { + return meth.invoke(instance, parameters); + } + }; + } catch (Exception e) { + throw new RuntimeException(e); + } + + } + + private Class[] toParamArray(List parameters) { + Class[] ret = new Class[parameters.size()]; + for (int i = 0; i < ret.length; ++i) { + ret[i] = toParam(parameters.get(i)); + } + return ret; + } + + private Class toParam(Type type) { + if (type.kind() == Type.Kind.PRIMITIVE) { + PrimitiveType prim = type.asPrimitiveType(); + switch (prim.primitive()) { + case INT: + return int.class; + case BYTE: + return byte.class; + case CHAR: + return char.class; + case LONG: + return long.class; + case FLOAT: + return float.class; + case SHORT: + return short.class; + case DOUBLE: + return double.class; + case BOOLEAN: + return boolean.class; + default: + throw new RuntimeException("Unknown type " + prim.primitive()); + } + } else { + try { + return Class.forName(type.name().toString(), false, Thread.currentThread().getContextClassLoader()); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + } + }; + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ServerEndpointIndexer.java b/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/ServerEndpointIndexer.java similarity index 59% rename from extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ServerEndpointIndexer.java rename to independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/ServerEndpointIndexer.java index 4608d6db57c0b..7108d3eeea1ba 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ServerEndpointIndexer.java +++ b/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/ServerEndpointIndexer.java @@ -1,4 +1,4 @@ -package io.quarkus.resteasy.reactive.server.deployment; +package org.jboss.resteasy.reactive.server.processor; import static javax.ws.rs.core.MediaType.APPLICATION_FORM_URLENCODED; import static javax.ws.rs.core.MediaType.APPLICATION_JSON; @@ -8,9 +8,8 @@ import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.JSONP_JSON_STRING; import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.JSONP_JSON_STRUCTURE; import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.JSONP_JSON_VALUE; -import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.STRING; -import java.util.Arrays; +import io.quarkus.gizmo.MethodCreator; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -18,12 +17,9 @@ import java.util.List; import java.util.Map; import java.util.Set; - import javax.ws.rs.core.MultivaluedMap; - import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.ClassInfo; -import org.jboss.jandex.ClassType; import org.jboss.jandex.DotName; import org.jboss.jandex.FieldInfo; import org.jboss.jandex.IndexView; @@ -36,56 +32,29 @@ import org.jboss.resteasy.reactive.common.processor.AdditionalReaders; import org.jboss.resteasy.reactive.common.processor.AdditionalWriters; import org.jboss.resteasy.reactive.common.processor.EndpointIndexer; -import org.jboss.resteasy.reactive.server.core.Deployment; -import org.jboss.resteasy.reactive.server.core.parameters.converters.GeneratedParameterConverter; +import org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames; import org.jboss.resteasy.reactive.server.core.parameters.converters.ListConverter; -import org.jboss.resteasy.reactive.server.core.parameters.converters.NoopParameterConverter; -import org.jboss.resteasy.reactive.server.core.parameters.converters.ParameterConverter; import org.jboss.resteasy.reactive.server.core.parameters.converters.ParameterConverterSupplier; import org.jboss.resteasy.reactive.server.core.parameters.converters.PathSegmentParamConverter; -import org.jboss.resteasy.reactive.server.core.parameters.converters.RuntimeResolvedConverter; import org.jboss.resteasy.reactive.server.core.parameters.converters.SetConverter; import org.jboss.resteasy.reactive.server.core.parameters.converters.SortedSetConverter; import org.jboss.resteasy.reactive.server.model.ServerMethodParameter; import org.jboss.resteasy.reactive.server.model.ServerResourceMethod; -import org.jboss.resteasy.reactive.server.processor.EndpointInvokerFactory; -import org.jboss.resteasy.reactive.server.processor.ServerIndexedParameter; import org.jboss.resteasy.reactive.server.providers.serialisers.ServerFormUrlEncodedProvider; import org.jboss.resteasy.reactive.server.providers.serialisers.jsonp.ServerJsonArrayHandler; import org.jboss.resteasy.reactive.server.providers.serialisers.jsonp.ServerJsonObjectHandler; import org.jboss.resteasy.reactive.server.providers.serialisers.jsonp.ServerJsonStructureHandler; import org.jboss.resteasy.reactive.server.providers.serialisers.jsonp.ServerJsonValueHandler; -import io.quarkus.arc.processor.DotNames; -import io.quarkus.deployment.GeneratedClassGizmoAdaptor; -import io.quarkus.deployment.annotations.BuildProducer; -import io.quarkus.deployment.builditem.BytecodeTransformerBuildItem; -import io.quarkus.deployment.builditem.GeneratedClassBuildItem; -import io.quarkus.gizmo.ClassCreator; -import io.quarkus.gizmo.MethodCreator; -import io.quarkus.gizmo.MethodDescriptor; -import io.quarkus.gizmo.ResultHandle; -import io.vertx.core.http.HttpServerRequest; -import io.vertx.core.http.HttpServerResponse; -import io.vertx.ext.web.RoutingContext; - public class ServerEndpointIndexer extends EndpointIndexer { private final MethodCreator initConverters; - private final BuildProducer generatedClassBuildItemBuildProducer; - private final BuildProducer bytecodeTransformerBuildProducer; protected final EndpointInvokerFactory endpointInvokerFactory; - private static final Set CONTEXT_TYPES = Collections.unmodifiableSet(new HashSet<>(Arrays.asList( - DotName.createSimple(HttpServerRequest.class.getName()), - DotName.createSimple(HttpServerResponse.class.getName()), - DotName.createSimple(RoutingContext.class.getName())))); - ServerEndpointIndexer(Builder builder) { + protected ServerEndpointIndexer(AbstractBuilder builder) { super(builder); - this.endpointInvokerFactory = builder.endpointInvokerFactory; this.initConverters = builder.initConverters; - this.generatedClassBuildItemBuildProducer = builder.generatedClassBuildItemBuildProducer; - this.bytecodeTransformerBuildProducer = builder.bytecodeTransformerBuildProducer; + this.endpointInvokerFactory = builder.endpointInvokerFactory; } protected void addWriterForType(AdditionalWriters additionalWriters, Type paramType) { @@ -103,10 +72,6 @@ protected void addWriterForType(AdditionalWriters additionalWriters, Type paramT } } - protected boolean isContextType(ClassType klass) { - return super.isContextType(klass) || CONTEXT_TYPES.contains(klass.name()); - } - protected void addReaderForType(AdditionalReaders additionalReaders, Type paramType) { DotName dotName = paramType.name(); if (dotName.equals(JSONP_JSON_NUMBER) @@ -127,139 +92,6 @@ protected ServerIndexedParameter createIndexedParam() { return new ServerIndexedParameter(); } - protected MethodParameter createMethodParameter(ClassInfo currentClassInfo, ClassInfo actualEndpointInfo, boolean encoded, - Type paramType, ServerIndexedParameter parameterResult, String name, String defaultValue, ParameterType type, - String elementType, boolean single) { - ParameterConverterSupplier converter = parameterResult.getConverter(); - return new ServerMethodParameter(name, - elementType, toClassName(paramType, currentClassInfo, actualEndpointInfo, index), type, single, - converter, defaultValue, parameterResult.isObtainedAsCollection(), encoded); - } - - protected void handleOtherParam(Map existingConverters, String errorLocation, boolean hasRuntimeConverters, - ServerIndexedParameter builder, String elementType) { - builder.setConverter(extractConverter(elementType, index, generatedClassBuildItemBuildProducer, - existingConverters, errorLocation, hasRuntimeConverters)); - } - - protected void handleMultiMapParam(AdditionalReaders additionalReaders, ServerIndexedParameter builder) { - additionalReaders.add(ServerFormUrlEncodedProvider.class, APPLICATION_FORM_URLENCODED, - MultivaluedMap.class); - } - - protected void handleSortedSetParam(Map existingConverters, String errorLocation, - boolean hasRuntimeConverters, ServerIndexedParameter builder, String elementType) { - ParameterConverterSupplier converter = extractConverter(elementType, index, generatedClassBuildItemBuildProducer, - existingConverters, errorLocation, hasRuntimeConverters); - builder.setConverter(new SortedSetConverter.SortedSetSupplier(converter)); - } - - protected void handleSetParam(Map existingConverters, String errorLocation, boolean hasRuntimeConverters, - ServerIndexedParameter builder, String elementType) { - ParameterConverterSupplier converter = extractConverter(elementType, index, generatedClassBuildItemBuildProducer, - existingConverters, errorLocation, hasRuntimeConverters); - builder.setConverter(new SetConverter.SetSupplier(converter)); - } - - protected void handleListParam(Map existingConverters, String errorLocation, boolean hasRuntimeConverters, - ServerIndexedParameter builder, String elementType) { - ParameterConverterSupplier converter = extractConverter(elementType, index, generatedClassBuildItemBuildProducer, - existingConverters, errorLocation, hasRuntimeConverters); - builder.setConverter(new ListConverter.ListSupplier(converter)); - } - - protected void handlePathSegmentParam(ServerIndexedParameter builder) { - builder.setConverter(new PathSegmentParamConverter.Supplier()); - } - - private ParameterConverterSupplier extractConverter(String elementType, IndexView indexView, - BuildProducer generatedClassBuildItemBuildProducer, - Map existingConverters, String errorLocation, boolean hasRuntimeConverters) { - if (elementType.equals(String.class.getName())) { - if (hasRuntimeConverters) - return new RuntimeResolvedConverter.Supplier().setDelegate(new NoopParameterConverter.Supplier()); - // String needs no conversion - return null; - } else if (existingConverters.containsKey(elementType)) { - String className = existingConverters.get(elementType); - ParameterConverterSupplier delegate; - if (className == null) - delegate = null; - else - delegate = new GeneratedParameterConverter().setClassName(className); - if (hasRuntimeConverters) - return new RuntimeResolvedConverter.Supplier().setDelegate(delegate); - if (delegate == null) - throw new RuntimeException("Failed to find converter for " + elementType); - return delegate; - } - - MethodDescriptor fromString = null; - MethodDescriptor valueOf = null; - MethodInfo stringCtor = null; - String primitiveWrapperType = primitiveTypes.get(elementType); - String prefix = ""; - if (primitiveWrapperType != null) { - valueOf = MethodDescriptor.ofMethod(primitiveWrapperType, "valueOf", primitiveWrapperType, String.class); - prefix = "io.quarkus.generated."; - } else { - ClassInfo type = indexView.getClassByName(DotName.createSimple(elementType)); - if (type != null) { - for (MethodInfo i : type.methods()) { - if (i.parameters().size() == 1) { - if (i.parameters().get(0).name().equals(STRING)) { - if (i.name().equals("")) { - stringCtor = i; - } else if (i.name().equals("valueOf")) { - valueOf = MethodDescriptor.of(i); - } else if (i.name().equals("fromString")) { - fromString = MethodDescriptor.of(i); - } - } - } - } - if (type.isEnum()) { - //spec weirdness, enums order is different - if (fromString != null) { - valueOf = null; - } - } - } - } - - String baseName; - ParameterConverterSupplier delegate; - if (stringCtor != null || valueOf != null || fromString != null) { - baseName = prefix + elementType + "$quarkusrestparamConverter$"; - try (ClassCreator classCreator = new ClassCreator( - new GeneratedClassGizmoAdaptor(generatedClassBuildItemBuildProducer, true), baseName, null, - Object.class.getName(), ParameterConverter.class.getName())) { - MethodCreator mc = classCreator.getMethodCreator("convert", Object.class, Object.class); - if (stringCtor != null) { - ResultHandle ret = mc.newInstance(stringCtor, mc.getMethodParam(0)); - mc.returnValue(ret); - } else if (valueOf != null) { - ResultHandle ret = mc.invokeStaticMethod(valueOf, mc.getMethodParam(0)); - mc.returnValue(ret); - } else if (fromString != null) { - ResultHandle ret = mc.invokeStaticMethod(fromString, mc.getMethodParam(0)); - mc.returnValue(ret); - } - } - delegate = new GeneratedParameterConverter().setClassName(baseName); - } else { - // let's not try this again - baseName = null; - delegate = null; - } - existingConverters.put(elementType, baseName); - if (hasRuntimeConverters) - return new RuntimeResolvedConverter.Supplier().setDelegate(delegate); - if (delegate == null) - throw new RuntimeException("Failed to find converter for " + elementType); - return delegate; - } - @Override protected ServerResourceMethod createResourceMethod() { return new ServerResourceMethod(); @@ -276,12 +108,9 @@ protected void handleAdditionalMethodProcessing(ServerResourceMethod method, Cla method.setMethodAnnotationNames(methodAnnotationNames); } - protected InjectableBean scanInjectableBean(ClassInfo currentClassInfo, - ClassInfo actualEndpointInfo, - Map existingConverters, - AdditionalReaders additionalReaders, - Map injectableBeans, - boolean hasRuntimeConverters) { + protected InjectableBean scanInjectableBean(ClassInfo currentClassInfo, ClassInfo actualEndpointInfo, + Map existingConverters, AdditionalReaders additionalReaders, + Map injectableBeans, boolean hasRuntimeConverters) { // do not scan a bean twice String currentTypeName = currentClassInfo.name().toString(); @@ -310,10 +139,7 @@ protected InjectableBean scanInjectableBean(ClassInfo currentClassInfo, fieldExtractors.put(field, result); } if (result.getConverter() != null) { - initConverters.invokeStaticMethod(MethodDescriptor.ofMethod(currentTypeName, - ClassInjectorTransformer.INIT_CONVERTER_METHOD_NAME + field.name(), - void.class, Deployment.class), - initConverters.getMethodParam(0)); + handleConverter(currentTypeName, field); } if (result.getType() == ParameterType.BEAN) { beanParamFields.put(field, result); @@ -336,7 +162,7 @@ protected InjectableBean scanInjectableBean(ClassInfo currentClassInfo, DotName superClassName = currentClassInfo.superName(); boolean superTypeIsInjectable = false; - if (superClassName != null && !superClassName.equals(DotNames.OBJECT)) { + if (superClassName != null && !superClassName.equals(ResteasyReactiveDotNames.OBJECT)) { ClassInfo superClass = index.getClassByName(superClassName); if (superClass != null) { InjectableBean superInjectableBean = scanInjectableBean(superClass, actualEndpointInfo, @@ -350,49 +176,96 @@ protected InjectableBean scanInjectableBean(ClassInfo currentClassInfo, } if (!fieldExtractors.isEmpty()) { - bytecodeTransformerBuildProducer.produce(new BytecodeTransformerBuildItem(currentTypeName, - new ClassInjectorTransformer(fieldExtractors, superTypeIsInjectable))); + handleFieldExtractors(currentTypeName, fieldExtractors, superTypeIsInjectable); } currentInjectableBean.setInjectionRequired(!fieldExtractors.isEmpty() || superTypeIsInjectable); return currentInjectableBean; } - public static final class Builder extends EndpointIndexer.Builder { + protected void handleFieldExtractors(String currentTypeName, Map fieldExtractors, + boolean superTypeIsInjectable) { + } + + protected void handleConverter(String currentTypeName, FieldInfo field) { + } + + protected MethodParameter createMethodParameter(ClassInfo currentClassInfo, ClassInfo actualEndpointInfo, boolean encoded, + Type paramType, ServerIndexedParameter parameterResult, String name, String defaultValue, ParameterType type, + String elementType, boolean single) { + ParameterConverterSupplier converter = parameterResult.getConverter(); + return new ServerMethodParameter(name, + elementType, toClassName(paramType, currentClassInfo, actualEndpointInfo, index), type, single, + converter, defaultValue, parameterResult.isObtainedAsCollection(), encoded); + } + + protected void handleOtherParam(Map existingConverters, String errorLocation, boolean hasRuntimeConverters, + ServerIndexedParameter builder, String elementType) { + builder.setConverter(extractConverter(elementType, index, + existingConverters, errorLocation, hasRuntimeConverters)); + } + + protected void handleMultiMapParam(AdditionalReaders additionalReaders, ServerIndexedParameter builder) { + additionalReaders.add(ServerFormUrlEncodedProvider.class, APPLICATION_FORM_URLENCODED, + MultivaluedMap.class); + } + + protected void handleSortedSetParam(Map existingConverters, String errorLocation, + boolean hasRuntimeConverters, ServerIndexedParameter builder, String elementType) { + ParameterConverterSupplier converter = extractConverter(elementType, index, + existingConverters, errorLocation, hasRuntimeConverters); + builder.setConverter(new SortedSetConverter.SortedSetSupplier(converter)); + } + + protected void handleSetParam(Map existingConverters, String errorLocation, boolean hasRuntimeConverters, + ServerIndexedParameter builder, String elementType) { + ParameterConverterSupplier converter = extractConverter(elementType, index, + existingConverters, errorLocation, hasRuntimeConverters); + builder.setConverter(new SetConverter.SetSupplier(converter)); + } + + protected void handleListParam(Map existingConverters, String errorLocation, boolean hasRuntimeConverters, + ServerIndexedParameter builder, String elementType) { + ParameterConverterSupplier converter = extractConverter(elementType, index, + existingConverters, errorLocation, hasRuntimeConverters); + builder.setConverter(new ListConverter.ListSupplier(converter)); + } + + protected void handlePathSegmentParam(ServerIndexedParameter builder) { + builder.setConverter(new PathSegmentParamConverter.Supplier()); + } + + protected ParameterConverterSupplier extractConverter(String elementType, IndexView indexView, + Map existingConverters, String errorLocation, boolean hasRuntimeConverters) { + return null; + } + + public static class AbstractBuilder> + extends EndpointIndexer.Builder { - private BuildProducer generatedClassBuildItemBuildProducer; - private BuildProducer bytecodeTransformerBuildProducer; private MethodCreator initConverters; - private EndpointInvokerFactory endpointInvokerFactory; + private EndpointInvokerFactory endpointInvokerFactory = new ReflectionEndpointInvokerFactory(); - public Builder setEndpointInvokerFactory(EndpointInvokerFactory endpointInvokerFactory) { + public B setEndpointInvokerFactory(EndpointInvokerFactory endpointInvokerFactory) { this.endpointInvokerFactory = endpointInvokerFactory; - return this; - } - - @Override - public ServerEndpointIndexer build() { - return new ServerEndpointIndexer(this); + return (B) this; } public MethodCreator getInitConverters() { return initConverters; } - public Builder setBytecodeTransformerBuildProducer( - BuildProducer bytecodeTransformerBuildProducer) { - this.bytecodeTransformerBuildProducer = bytecodeTransformerBuildProducer; - return this; + public B setInitConverters(MethodCreator initConverters) { + this.initConverters = initConverters; + return (B) this; } - public Builder setGeneratedClassBuildItemBuildProducer( - BuildProducer generatedClassBuildItemBuildProducer) { - this.generatedClassBuildItemBuildProducer = generatedClassBuildItemBuildProducer; - return this; + @Override + public ServerEndpointIndexer build() { + return new ServerEndpointIndexer(this); } + } + + public static class Builder extends AbstractBuilder { - public Builder setInitConverters(MethodCreator initConverters) { - this.initConverters = initConverters; - return this; - } } } diff --git a/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/scanning/ResteasyReactiveContextResolverScanner.java b/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/scanning/ResteasyReactiveContextResolverScanner.java new file mode 100644 index 0000000000000..5441090aafabc --- /dev/null +++ b/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/scanning/ResteasyReactiveContextResolverScanner.java @@ -0,0 +1,78 @@ +package org.jboss.resteasy.reactive.server.processor.scanning; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.function.Function; +import org.jboss.jandex.AnnotationInstance; +import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.DotName; +import org.jboss.jandex.IndexView; +import org.jboss.jandex.Type; +import org.jboss.resteasy.reactive.common.model.ResourceContextResolver; +import org.jboss.resteasy.reactive.common.processor.JandexUtil; +import org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames; +import org.jboss.resteasy.reactive.common.processor.scanning.ApplicationScanningResult; +import org.jboss.resteasy.reactive.common.util.ReflectionBeanFactoryCreator; +import org.jboss.resteasy.reactive.server.model.ContextResolvers; +import org.jboss.resteasy.reactive.spi.BeanFactory; + +/** + * Class that is responsible for scanning for exception mappers + */ +public class ResteasyReactiveContextResolverScanner { + + /** + * Creates a fully populated exception mapper instance, that are created via reflection. + */ + public static ContextResolvers createContextResolvers(IndexView indexView, ApplicationScanningResult result) { + return createContextResolvers(indexView, result, new ReflectionBeanFactoryCreator()); + } + + /** + * Creates a fully populated exception mapper instance, that are created via the provided factory creator + */ + public static ContextResolvers createContextResolvers(IndexView indexView, ApplicationScanningResult result, + Function> factoryCreator) { + ContextResolvers ret = scanForContextResolvers(indexView, result); + ret.initializeDefaultFactories(factoryCreator); + return ret; + } + + public static ContextResolvers scanForContextResolvers(IndexView index, ApplicationScanningResult result) { + + ContextResolvers contextResolvers = new ContextResolvers(); + Collection resolvers = index + .getAllKnownImplementors(ResteasyReactiveDotNames.CONTEXT_RESOLVER); + for (ClassInfo resolverClass : resolvers) { + ApplicationScanningResult.KeepProviderResult keepProviderResult = result + .keepProvider(resolverClass); + if (keepProviderResult != ApplicationScanningResult.KeepProviderResult.DISCARD) { + List typeParameters = JandexUtil.resolveTypeParameters(resolverClass.name(), + ResteasyReactiveDotNames.CONTEXT_RESOLVER, + index); + DotName typeParam = typeParameters.get(0).name(); + ResourceContextResolver mapper = new ResourceContextResolver(); + mapper.setClassName(resolverClass.name().toString()); + mapper.setMediaTypeStrings(getProducesMediaTypes(resolverClass)); + try { + Class contextType = Class.forName(typeParam.toString(), false, + Thread.currentThread().getContextClassLoader()); + contextResolvers.addContextResolver(contextType, mapper); + } catch (ClassNotFoundException e) { + throw new RuntimeException("Unable to load context type: " + typeParam); + } + } + } + return contextResolvers; + } + + private static List getProducesMediaTypes(ClassInfo classInfo) { + AnnotationInstance produces = classInfo.classAnnotation(ResteasyReactiveDotNames.PRODUCES); + if (produces == null) { + return Collections.emptyList(); + } + return Arrays.asList(produces.value().asStringArray()); + } +} diff --git a/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/scanning/ResteasyReactiveExceptionMappingScanner.java b/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/scanning/ResteasyReactiveExceptionMappingScanner.java new file mode 100644 index 0000000000000..7e5338fe3cf01 --- /dev/null +++ b/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/scanning/ResteasyReactiveExceptionMappingScanner.java @@ -0,0 +1,74 @@ +package org.jboss.resteasy.reactive.server.processor.scanning; + +import java.util.Collection; +import java.util.List; +import java.util.function.Function; +import javax.ws.rs.Priorities; +import org.jboss.jandex.AnnotationInstance; +import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.DotName; +import org.jboss.jandex.IndexView; +import org.jboss.jandex.Type; +import org.jboss.resteasy.reactive.common.model.ResourceExceptionMapper; +import org.jboss.resteasy.reactive.common.processor.JandexUtil; +import org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames; +import org.jboss.resteasy.reactive.common.processor.scanning.ApplicationScanningResult; +import org.jboss.resteasy.reactive.common.util.ReflectionBeanFactoryCreator; +import org.jboss.resteasy.reactive.server.core.ExceptionMapping; +import org.jboss.resteasy.reactive.spi.BeanFactory; + +/** + * Class that is responsible for scanning for exception mappers + */ +public class ResteasyReactiveExceptionMappingScanner { + + /** + * Creates a fully populated exception mapper instance, that are created via reflection. + */ + public static ExceptionMapping createExceptionMappers(IndexView indexView, ApplicationScanningResult result) { + return createExceptionMappers(indexView, result, new ReflectionBeanFactoryCreator()); + } + + /** + * Creates a fully populated exception mapper instance, that are created via the provided factory creator + */ + public static ExceptionMapping createExceptionMappers(IndexView indexView, ApplicationScanningResult result, + Function> factoryCreator) { + ExceptionMapping ret = scanForExceptionMappers(indexView, result); + ret.initializeDefaultFactories(factoryCreator); + return ret; + } + + public static ExceptionMapping scanForExceptionMappers(IndexView index, ApplicationScanningResult result) { + + ExceptionMapping exceptionMapping = new ExceptionMapping(); + Collection exceptionMappers = index + .getAllKnownImplementors(ResteasyReactiveDotNames.EXCEPTION_MAPPER); + for (ClassInfo mapperClass : exceptionMappers) { + ApplicationScanningResult.KeepProviderResult keepProviderResult = result + .keepProvider(mapperClass); + if (keepProviderResult != ApplicationScanningResult.KeepProviderResult.DISCARD) { + List typeParameters = JandexUtil.resolveTypeParameters(mapperClass.name(), + ResteasyReactiveDotNames.EXCEPTION_MAPPER, + index); + DotName handledExceptionDotName = typeParameters.get(0).name(); + AnnotationInstance priorityInstance = mapperClass.classAnnotation(ResteasyReactiveDotNames.PRIORITY); + int priority = Priorities.USER; + if (priorityInstance != null) { + priority = priorityInstance.value().asInt(); + } + ResourceExceptionMapper mapper = new ResourceExceptionMapper<>(); + mapper.setPriority(priority); + mapper.setClassName(mapperClass.name().toString()); + try { + Class mappedClass = Class.forName(handledExceptionDotName.toString(), false, + Thread.currentThread().getContextClassLoader()); + exceptionMapping.addExceptionMapper(mappedClass, mapper); + } catch (ClassNotFoundException e) { + throw new RuntimeException("Unable to load mapped exception: " + handledExceptionDotName); + } + } + } + return exceptionMapping; + } +} diff --git a/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/scanning/ResteasyReactiveFeatureScanner.java b/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/scanning/ResteasyReactiveFeatureScanner.java new file mode 100644 index 0000000000000..722b8f6055ba9 --- /dev/null +++ b/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/scanning/ResteasyReactiveFeatureScanner.java @@ -0,0 +1,95 @@ +package org.jboss.resteasy.reactive.server.processor.scanning; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import java.util.function.Function; +import javax.ws.rs.container.DynamicFeature; +import javax.ws.rs.core.Feature; +import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.IndexView; +import org.jboss.resteasy.reactive.common.model.ResourceDynamicFeature; +import org.jboss.resteasy.reactive.common.model.ResourceFeature; +import org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames; +import org.jboss.resteasy.reactive.common.processor.scanning.ApplicationScanningResult; +import org.jboss.resteasy.reactive.common.util.ReflectionBeanFactoryCreator; +import org.jboss.resteasy.reactive.server.model.DynamicFeatures; +import org.jboss.resteasy.reactive.server.model.Features; +import org.jboss.resteasy.reactive.spi.BeanFactory; + +/** + * Class that is responsible for scanning for features and dynamic features + */ +public class ResteasyReactiveFeatureScanner { + + /** + * Creates a fully populated resource features instance, that are created via reflection. + */ + public static Features createFeatures(IndexView indexView, ApplicationScanningResult result) { + return createFeatures(indexView, result, new ReflectionBeanFactoryCreator()); + } + + /** + * Creates a fully populated resource features instance, that are created via the provided factory creator + */ + public static Features createFeatures(IndexView indexView, ApplicationScanningResult result, + Function> factoryCreator) { + Features features = new Features(); + for (String i : scanForFeatures(indexView, result)) { + ResourceFeature resourceFeature = new ResourceFeature(); + resourceFeature.setFactory((BeanFactory) factoryCreator.apply(i)); + features.addFeature(resourceFeature); + } + return features; + } + + /** + * Creates a fully populated resource dynamic features instance, that are created via reflection. + */ + public static DynamicFeatures createDynamicFeatures(IndexView indexView, ApplicationScanningResult result) { + return createDynamicFeatures(indexView, result, new ReflectionBeanFactoryCreator()); + } + + /** + * Creates a fully populated resource features instance, that are created via the provided factory creator + */ + public static DynamicFeatures createDynamicFeatures(IndexView indexView, ApplicationScanningResult result, + Function> factoryCreator) { + DynamicFeatures features = new DynamicFeatures(); + for (String i : scanForDynamicFeatures(indexView, result)) { + ResourceDynamicFeature resourceFeature = new ResourceDynamicFeature(); + resourceFeature.setFactory((BeanFactory) factoryCreator.apply(i)); + features.addFeature(resourceFeature); + } + return features; + } + + public static Set scanForFeatures(IndexView index, ApplicationScanningResult applicationScanningResult) { + Collection features = index + .getAllKnownImplementors(ResteasyReactiveDotNames.FEATURE); + Set ret = new HashSet<>(); + for (ClassInfo featureClass : features) { + ApplicationScanningResult.KeepProviderResult keepProviderResult = applicationScanningResult + .keepProvider(featureClass); + if (keepProviderResult != ApplicationScanningResult.KeepProviderResult.DISCARD) { + ret.add(featureClass.name().toString()); + } + } + return ret; + } + + public static Set scanForDynamicFeatures(IndexView index, ApplicationScanningResult applicationScanningResult) { + Collection features = index + .getAllKnownImplementors(ResteasyReactiveDotNames.DYNAMIC_FEATURE); + Set ret = new HashSet<>(); + for (ClassInfo featureClass : features) { + ApplicationScanningResult.KeepProviderResult keepProviderResult = applicationScanningResult + .keepProvider(featureClass); + if (keepProviderResult != ApplicationScanningResult.KeepProviderResult.DISCARD) { + ret.add(featureClass.name().toString()); + } + } + return ret; + } + +} diff --git a/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/scanning/ResteasyReactiveParamConverterScanner.java b/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/scanning/ResteasyReactiveParamConverterScanner.java new file mode 100644 index 0000000000000..9b28b0dafde31 --- /dev/null +++ b/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/scanning/ResteasyReactiveParamConverterScanner.java @@ -0,0 +1,56 @@ +package org.jboss.resteasy.reactive.server.processor.scanning; + +import java.util.Collection; +import java.util.function.Function; +import javax.ws.rs.Priorities; +import org.jboss.jandex.AnnotationInstance; +import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.IndexView; +import org.jboss.resteasy.reactive.common.model.ResourceParamConverterProvider; +import org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames; +import org.jboss.resteasy.reactive.common.processor.scanning.ApplicationScanningResult; +import org.jboss.resteasy.reactive.common.util.ReflectionBeanFactoryCreator; +import org.jboss.resteasy.reactive.server.model.ParamConverterProviders; +import org.jboss.resteasy.reactive.spi.BeanFactory; + +/** + * Class that is responsible for scanning for param converters + */ +public class ResteasyReactiveParamConverterScanner { + + /** + * Creates a fully populated param converter instance, that are created via reflection. + */ + public static ParamConverterProviders createParamConverters(IndexView indexView, ApplicationScanningResult result) { + return createParamConverters(indexView, result, new ReflectionBeanFactoryCreator()); + } + + /** + * Creates a fully populated param converter instance, that are created via the provided factory creator + */ + public static ParamConverterProviders createParamConverters(IndexView indexView, ApplicationScanningResult result, + Function> factoryCreator) { + ParamConverterProviders ret = scanForParamConverters(indexView, result); + ret.initializeDefaultFactories(factoryCreator); + return ret; + } + + public static ParamConverterProviders scanForParamConverters(IndexView index, ApplicationScanningResult result) { + + Collection paramConverterProviders = index + .getAllKnownImplementors(ResteasyReactiveDotNames.PARAM_CONVERTER_PROVIDER); + ParamConverterProviders providers = new ParamConverterProviders(); + for (ClassInfo converterClass : paramConverterProviders) { + ApplicationScanningResult.KeepProviderResult keepProviderResult = result.keepProvider(converterClass); + if (keepProviderResult != ApplicationScanningResult.KeepProviderResult.DISCARD) { + AnnotationInstance priorityInstance = converterClass.classAnnotation(ResteasyReactiveDotNames.PRIORITY); + int priority = priorityInstance != null ? priorityInstance.value().asInt() : Priorities.USER; + ResourceParamConverterProvider provider = new ResourceParamConverterProvider(); + provider.setPriority(priority); + provider.setClassName(converterClass.name().toString()); + providers.addParamConverterProviders(provider); + } + } + return providers; + } +} diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/Deployment.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/Deployment.java index 94063cddecb85..07b7ce14bf683 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/Deployment.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/Deployment.java @@ -19,6 +19,8 @@ import org.jboss.resteasy.reactive.server.handlers.ResourceRequestFilterHandler; import org.jboss.resteasy.reactive.server.handlers.RestInitialHandler; import org.jboss.resteasy.reactive.server.mapping.RequestMapper; +import org.jboss.resteasy.reactive.server.model.ContextResolvers; +import org.jboss.resteasy.reactive.server.model.ParamConverterProviders; import org.jboss.resteasy.reactive.server.spi.ServerRestHandler; import org.jboss.resteasy.reactive.spi.BeanFactory.BeanInstance; import org.jboss.resteasy.reactive.spi.ThreadSetupAction; diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/DeploymentInfo.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/DeploymentInfo.java index 5153bfd9f48fd..f2da93423774d 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/DeploymentInfo.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/DeploymentInfo.java @@ -7,6 +7,10 @@ import org.jboss.resteasy.reactive.common.ResteasyReactiveConfig; import org.jboss.resteasy.reactive.common.model.ResourceClass; import org.jboss.resteasy.reactive.common.model.ResourceInterceptors; +import org.jboss.resteasy.reactive.server.model.ContextResolvers; +import org.jboss.resteasy.reactive.server.model.DynamicFeatures; +import org.jboss.resteasy.reactive.server.model.Features; +import org.jboss.resteasy.reactive.server.model.ParamConverterProviders; import org.jboss.resteasy.reactive.spi.BeanFactory; public class DeploymentInfo { diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ExceptionMapping.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ExceptionMapping.java index e25071c5a8d9c..1429465d40e59 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ExceptionMapping.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ExceptionMapping.java @@ -3,6 +3,7 @@ import java.io.IOException; import java.util.HashMap; import java.util.Map; +import java.util.function.Function; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Response; import javax.ws.rs.ext.ExceptionMapper; @@ -10,6 +11,7 @@ import org.jboss.resteasy.reactive.common.model.ResourceExceptionMapper; import org.jboss.resteasy.reactive.server.spi.ResteasyReactiveAsyncExceptionMapper; import org.jboss.resteasy.reactive.server.spi.ResteasyReactiveExceptionMapper; +import org.jboss.resteasy.reactive.spi.BeanFactory; public class ExceptionMapping { @@ -102,7 +104,25 @@ private ExceptionMapper doGetExceptionMapper(Class c } public void addExceptionMapper(Class exceptionClass, ResourceExceptionMapper mapper) { + ResourceExceptionMapper existing = mappers.get(exceptionClass); + if (existing != null) { + if (existing.getPriority() < mapper.getPriority()) { + //already a higher priority mapper + return; + } + } mappers.put(exceptionClass, mapper); } + public Map, ResourceExceptionMapper> getMappers() { + return mappers; + } + + public void initializeDefaultFactories(Function> factoryCreator) { + for (Map.Entry, ResourceExceptionMapper> entry : mappers.entrySet()) { + if (entry.getValue().getFactory() == null) { + entry.getValue().setFactory((BeanFactory) factoryCreator.apply(entry.getValue().getClassName())); + } + } + } } diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/converters/ListConverter.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/converters/ListConverter.java index 4cd892731674c..ea2cbf683b170 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/converters/ListConverter.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/converters/ListConverter.java @@ -5,7 +5,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import org.jboss.resteasy.reactive.server.core.ParamConverterProviders; +import org.jboss.resteasy.reactive.server.model.ParamConverterProviders; public class ListConverter implements ParameterConverter { diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/converters/NoopParameterConverter.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/converters/NoopParameterConverter.java index 72e95ba0c3633..5958994b519a2 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/converters/NoopParameterConverter.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/converters/NoopParameterConverter.java @@ -2,7 +2,7 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Type; -import org.jboss.resteasy.reactive.server.core.ParamConverterProviders; +import org.jboss.resteasy.reactive.server.model.ParamConverterProviders; public class NoopParameterConverter implements ParameterConverter { diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/converters/ParameterConverter.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/converters/ParameterConverter.java index 175bf78043ab5..64db5e3708bcd 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/converters/ParameterConverter.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/converters/ParameterConverter.java @@ -2,7 +2,7 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Type; -import org.jboss.resteasy.reactive.server.core.ParamConverterProviders; +import org.jboss.resteasy.reactive.server.model.ParamConverterProviders; public interface ParameterConverter { diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/converters/PathSegmentParamConverter.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/converters/PathSegmentParamConverter.java index 9a7f912392409..7b55e141e58dd 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/converters/PathSegmentParamConverter.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/converters/PathSegmentParamConverter.java @@ -3,7 +3,7 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Type; import org.jboss.resteasy.reactive.common.util.PathSegmentImpl; -import org.jboss.resteasy.reactive.server.core.ParamConverterProviders; +import org.jboss.resteasy.reactive.server.model.ParamConverterProviders; public class PathSegmentParamConverter implements ParameterConverter { @Override diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/converters/RuntimeParameterConverter.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/converters/RuntimeParameterConverter.java index f754bdb95a22e..269c26cac6c83 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/converters/RuntimeParameterConverter.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/converters/RuntimeParameterConverter.java @@ -3,7 +3,7 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Type; import javax.ws.rs.ext.ParamConverter; -import org.jboss.resteasy.reactive.server.core.ParamConverterProviders; +import org.jboss.resteasy.reactive.server.model.ParamConverterProviders; public class RuntimeParameterConverter implements ParameterConverter { diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/converters/RuntimeResolvedConverter.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/converters/RuntimeResolvedConverter.java index 158ad9147fbff..3eb4519c6a826 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/converters/RuntimeResolvedConverter.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/converters/RuntimeResolvedConverter.java @@ -5,7 +5,7 @@ import javax.ws.rs.ext.ParamConverter; import javax.ws.rs.ext.ParamConverterProvider; import org.jboss.resteasy.reactive.common.model.ResourceParamConverterProvider; -import org.jboss.resteasy.reactive.server.core.ParamConverterProviders; +import org.jboss.resteasy.reactive.server.model.ParamConverterProviders; public class RuntimeResolvedConverter implements ParameterConverter { diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/converters/SetConverter.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/converters/SetConverter.java index a67ab5b65a5e1..2edd9c8d278cd 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/converters/SetConverter.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/converters/SetConverter.java @@ -6,7 +6,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -import org.jboss.resteasy.reactive.server.core.ParamConverterProviders; +import org.jboss.resteasy.reactive.server.model.ParamConverterProviders; public class SetConverter implements ParameterConverter { diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/converters/SortedSetConverter.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/converters/SortedSetConverter.java index 06af26b81696a..b62e81b91342b 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/converters/SortedSetConverter.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/converters/SortedSetConverter.java @@ -6,7 +6,7 @@ import java.util.List; import java.util.SortedSet; import java.util.TreeSet; -import org.jboss.resteasy.reactive.server.core.ParamConverterProviders; +import org.jboss.resteasy.reactive.server.model.ParamConverterProviders; public class SortedSetConverter implements ParameterConverter { diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/startup/RuntimeDeploymentManager.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/startup/RuntimeDeploymentManager.java index fc2d3fc402f2c..c81ca638278c9 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/startup/RuntimeDeploymentManager.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/startup/RuntimeDeploymentManager.java @@ -24,8 +24,6 @@ import org.jboss.resteasy.reactive.server.core.Deployment; import org.jboss.resteasy.reactive.server.core.DeploymentInfo; import org.jboss.resteasy.reactive.server.core.ExceptionMapping; -import org.jboss.resteasy.reactive.server.core.Features; -import org.jboss.resteasy.reactive.server.core.ParamConverterProviders; import org.jboss.resteasy.reactive.server.core.RequestContextFactory; import org.jboss.resteasy.reactive.server.core.ServerSerialisers; import org.jboss.resteasy.reactive.server.core.serialization.DynamicEntityWriter; @@ -40,6 +38,8 @@ import org.jboss.resteasy.reactive.server.mapping.RequestMapper; import org.jboss.resteasy.reactive.server.mapping.RuntimeResource; import org.jboss.resteasy.reactive.server.mapping.URITemplate; +import org.jboss.resteasy.reactive.server.model.Features; +import org.jboss.resteasy.reactive.server.model.ParamConverterProviders; import org.jboss.resteasy.reactive.server.model.ServerResourceMethod; import org.jboss.resteasy.reactive.server.spi.ServerRestHandler; import org.jboss.resteasy.reactive.spi.BeanFactory; diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/startup/RuntimeInterceptorDeployment.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/startup/RuntimeInterceptorDeployment.java index 6675d93e33a76..8c62083d61fcf 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/startup/RuntimeInterceptorDeployment.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/startup/RuntimeInterceptorDeployment.java @@ -22,12 +22,12 @@ import org.jboss.resteasy.reactive.common.model.ResourceInterceptors; import org.jboss.resteasy.reactive.common.model.ResourceMethod; import org.jboss.resteasy.reactive.server.core.DeploymentInfo; -import org.jboss.resteasy.reactive.server.core.DynamicFeatures; import org.jboss.resteasy.reactive.server.handlers.InterceptorHandler; import org.jboss.resteasy.reactive.server.handlers.ResourceRequestFilterHandler; import org.jboss.resteasy.reactive.server.handlers.ResourceResponseFilterHandler; import org.jboss.resteasy.reactive.server.jaxrs.DynamicFeatureContext; import org.jboss.resteasy.reactive.server.jaxrs.DynamicFeatureResourceInfo; +import org.jboss.resteasy.reactive.server.model.DynamicFeatures; import org.jboss.resteasy.reactive.server.spi.ServerRestHandler; import org.jboss.resteasy.reactive.spi.BeanFactory; diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/startup/RuntimeResourceDeployment.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/startup/RuntimeResourceDeployment.java index c4b413114b55d..b56cb6e9fb05b 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/startup/RuntimeResourceDeployment.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/startup/RuntimeResourceDeployment.java @@ -31,7 +31,6 @@ import org.jboss.resteasy.reactive.common.util.ServerMediaType; import org.jboss.resteasy.reactive.common.util.types.TypeSignatureParser; import org.jboss.resteasy.reactive.server.core.DeploymentInfo; -import org.jboss.resteasy.reactive.server.core.ParamConverterProviders; import org.jboss.resteasy.reactive.server.core.ServerSerialisers; import org.jboss.resteasy.reactive.server.core.parameters.AsyncResponseExtractor; import org.jboss.resteasy.reactive.server.core.parameters.BeanParamExtractor; @@ -71,6 +70,7 @@ import org.jboss.resteasy.reactive.server.handlers.VariableProducesHandler; import org.jboss.resteasy.reactive.server.mapping.RuntimeResource; import org.jboss.resteasy.reactive.server.mapping.URITemplate; +import org.jboss.resteasy.reactive.server.model.ParamConverterProviders; import org.jboss.resteasy.reactive.server.model.ServerMethodParameter; import org.jboss.resteasy.reactive.server.model.ServerResourceMethod; import org.jboss.resteasy.reactive.server.spi.EndpointInvoker; diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ContextResolvers.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/model/ContextResolvers.java similarity index 82% rename from independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ContextResolvers.java rename to independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/model/ContextResolvers.java index 1864d4e14ec1d..81aba864a25c9 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ContextResolvers.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/model/ContextResolvers.java @@ -1,15 +1,17 @@ -package org.jboss.resteasy.reactive.server.core; +package org.jboss.resteasy.reactive.server.model; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.Function; import javax.ws.rs.core.MediaType; import javax.ws.rs.ext.ContextResolver; import org.jboss.resteasy.reactive.common.model.ResourceContextResolver; import org.jboss.resteasy.reactive.common.util.MediaTypeHelper; import org.jboss.resteasy.reactive.server.jaxrs.ContextResolverDelegate; +import org.jboss.resteasy.reactive.spi.BeanFactory; public class ContextResolvers { @@ -77,4 +79,18 @@ public ContextResolver getContextResolver(Class clazz, MediaType media } return null; } + + public Map, List> getResolvers() { + return resolvers; + } + + public void initializeDefaultFactories(Function> factoryCreator) { + for (Map.Entry, List> entry : resolvers.entrySet()) { + for (ResourceContextResolver i : entry.getValue()) { + if (i.getFactory() == null) { + i.setFactory((BeanFactory) factoryCreator.apply(i.getClassName())); + } + } + } + } } diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/DynamicFeatures.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/model/DynamicFeatures.java similarity index 80% rename from independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/DynamicFeatures.java rename to independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/model/DynamicFeatures.java index ca3e9156a3328..87556a1eec4e5 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/DynamicFeatures.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/model/DynamicFeatures.java @@ -1,9 +1,12 @@ -package org.jboss.resteasy.reactive.server.core; +package org.jboss.resteasy.reactive.server.model; import java.util.ArrayList; import java.util.List; import org.jboss.resteasy.reactive.common.model.ResourceDynamicFeature; +/** + * Container for {@link javax.ws.rs.container.DynamicFeature} + */ public class DynamicFeatures { private final List resourceDynamicFeatures = new ArrayList<>(); diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/Features.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/model/Features.java similarity index 89% rename from independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/Features.java rename to independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/model/Features.java index 3438e3b231f6b..ecffc98b6652b 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/Features.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/model/Features.java @@ -1,4 +1,4 @@ -package org.jboss.resteasy.reactive.server.core; +package org.jboss.resteasy.reactive.server.model; import java.util.ArrayList; import java.util.List; diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ParamConverterProviders.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/model/ParamConverterProviders.java similarity index 58% rename from independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ParamConverterProviders.java rename to independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/model/ParamConverterProviders.java index 2cb1e3bd6e6c0..d2c3dd95a8f25 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ParamConverterProviders.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/model/ParamConverterProviders.java @@ -1,9 +1,12 @@ -package org.jboss.resteasy.reactive.server.core; +package org.jboss.resteasy.reactive.server.model; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.function.Function; +import javax.ws.rs.ext.ParamConverterProvider; import org.jboss.resteasy.reactive.common.model.ResourceParamConverterProvider; +import org.jboss.resteasy.reactive.spi.BeanFactory; public class ParamConverterProviders { @@ -20,4 +23,10 @@ public List getParamConverterProviders() { public void sort() { Collections.sort(paramConverterProviders); } + + public void initializeDefaultFactories(Function> factoryCreator) { + for (ResourceParamConverterProvider i : paramConverterProviders) { + i.setFactory((BeanFactory) factoryCreator.apply(i.getClassName())); + } + } } diff --git a/independent-projects/resteasy-reactive/server/vertx/pom.xml b/independent-projects/resteasy-reactive/server/vertx/pom.xml index be8e1112466f7..c65cce73ecb44 100644 --- a/independent-projects/resteasy-reactive/server/vertx/pom.xml +++ b/independent-projects/resteasy-reactive/server/vertx/pom.xml @@ -31,6 +31,17 @@ org.jboss.spec.javax.ws.rs jboss-jaxrs-api_2.1_spec + + org.jboss.shrinkwrap + shrinkwrap-depchain + pom + test + + + io.quarkus.resteasy.reactive + resteasy-reactive-processor + test + jakarta.annotation @@ -41,6 +52,18 @@ junit-jupiter test + + io.rest-assured + rest-assured + + + org.jboss.logging + commons-logging-jboss-logging + + + org.jboss.spec.javax.xml.bind + jboss-jaxb-api_2.3_spec + org.jboss.logging diff --git a/independent-projects/resteasy-reactive/server/vertx/src/main/java/org/jboss/resteasy/reactive/server/vertx/ResteasyReactiveVertxHandler.java b/independent-projects/resteasy-reactive/server/vertx/src/main/java/org/jboss/resteasy/reactive/server/vertx/ResteasyReactiveVertxHandler.java new file mode 100644 index 0000000000000..3ff1635e2aa96 --- /dev/null +++ b/independent-projects/resteasy-reactive/server/vertx/src/main/java/org/jboss/resteasy/reactive/server/vertx/ResteasyReactiveVertxHandler.java @@ -0,0 +1,19 @@ +package org.jboss.resteasy.reactive.server.vertx; + +import io.vertx.core.Handler; +import io.vertx.ext.web.RoutingContext; +import org.jboss.resteasy.reactive.server.handlers.RestInitialHandler; + +public class ResteasyReactiveVertxHandler implements Handler { + + private final RestInitialHandler handler; + + public ResteasyReactiveVertxHandler(RestInitialHandler handler) { + this.handler = handler; + } + + @Override + public void handle(RoutingContext event) { + handler.beginProcessing(event); + } +} diff --git a/independent-projects/resteasy-reactive/server/vertx/src/main/java/org/jboss/resteasy/reactive/server/vertx/VertxRequestContextFactory.java b/independent-projects/resteasy-reactive/server/vertx/src/main/java/org/jboss/resteasy/reactive/server/vertx/VertxRequestContextFactory.java new file mode 100644 index 0000000000000..25a0bcf19e93b --- /dev/null +++ b/independent-projects/resteasy-reactive/server/vertx/src/main/java/org/jboss/resteasy/reactive/server/vertx/VertxRequestContextFactory.java @@ -0,0 +1,19 @@ +package org.jboss.resteasy.reactive.server.vertx; + +import io.vertx.ext.web.RoutingContext; +import org.jboss.resteasy.reactive.server.core.Deployment; +import org.jboss.resteasy.reactive.server.core.RequestContextFactory; +import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext; +import org.jboss.resteasy.reactive.server.jaxrs.ProvidersImpl; +import org.jboss.resteasy.reactive.server.spi.ServerRestHandler; +import org.jboss.resteasy.reactive.spi.ThreadSetupAction; + +public class VertxRequestContextFactory implements RequestContextFactory { + @Override + public ResteasyReactiveRequestContext createContext(Deployment deployment, + ProvidersImpl providers, Object context, ThreadSetupAction requestContext, + ServerRestHandler[] handlerChain, ServerRestHandler[] abortHandlerChain) { + return new VertxResteasyReactiveRequestContext(deployment, providers, (RoutingContext) context, + requestContext, handlerChain, abortHandlerChain); + } +} diff --git a/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/HelloResource.java b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/HelloResource.java new file mode 100644 index 0000000000000..cd6b1cd99b363 --- /dev/null +++ b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/HelloResource.java @@ -0,0 +1,15 @@ +package org.jboss.resteasy.reactive.server.vertx.test; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.QueryParam; + +@Path("/hello") +public class HelloResource { + + @GET + public String hello(@QueryParam("name") String name) { + return "hello " + name; + } + +} diff --git a/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/SimpleVertxResteasyReactiveTest.java b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/SimpleVertxResteasyReactiveTest.java new file mode 100644 index 0000000000000..f37529f031047 --- /dev/null +++ b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/SimpleVertxResteasyReactiveTest.java @@ -0,0 +1,32 @@ +package org.jboss.resteasy.reactive.server.vertx.test; + +import static org.hamcrest.Matchers.equalTo; + +import io.restassured.RestAssured; +import java.util.function.Supplier; +import org.jboss.resteasy.reactive.server.vertx.test.framework.ResteasyReactiveUnitTest; +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; + +public class SimpleVertxResteasyReactiveTest { + + @RegisterExtension + static ResteasyReactiveUnitTest test = new ResteasyReactiveUnitTest() + .setArchiveProducer(new Supplier() { + @Override + public JavaArchive get() { + return ShrinkWrap.create(JavaArchive.class) + .addClass(HelloResource.class); + } + }); + + @Test + public void helloWorldTest() { + RestAssured.get("/hello?name=Stu") + .then() + .body(equalTo("hello Stu")); + } + +} diff --git a/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/framework/InMemoryLogHandler.java b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/framework/InMemoryLogHandler.java new file mode 100644 index 0000000000000..b1dde96f45bc1 --- /dev/null +++ b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/framework/InMemoryLogHandler.java @@ -0,0 +1,44 @@ +package org.jboss.resteasy.reactive.server.vertx.test.framework; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogRecord; + +public class InMemoryLogHandler extends Handler { + + private final Predicate predicate; + + public InMemoryLogHandler(Predicate predicate) { + this.predicate = predicate; + } + + final List records = new ArrayList<>(); + + @Override + public void publish(LogRecord record) { + if (predicate.test(record)) { + records.add(record); + } + } + + @Override + public void flush() { + } + + @Override + public Level getLevel() { + return Level.FINE; + } + + @Override + public void close() throws SecurityException { + this.records.clear(); + } + + void clearRecords() { + this.records.clear(); + } +} diff --git a/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/framework/ResteasyReactiveUnitTest.java b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/framework/ResteasyReactiveUnitTest.java new file mode 100644 index 0000000000000..793241518e827 --- /dev/null +++ b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/framework/ResteasyReactiveUnitTest.java @@ -0,0 +1,340 @@ +package org.jboss.resteasy.reactive.server.vertx.test.framework; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import io.vertx.core.Vertx; +import io.vertx.core.http.HttpServer; +import io.vertx.ext.web.Route; +import io.vertx.ext.web.Router; +import java.io.Closeable; +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.logging.Handler; +import java.util.logging.LogManager; +import java.util.logging.LogRecord; +import java.util.logging.Logger; +import javax.ws.rs.core.Application; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.ext.MessageBodyWriter; +import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.DotName; +import org.jboss.jandex.Index; +import org.jboss.resteasy.reactive.common.ResteasyReactiveConfig; +import org.jboss.resteasy.reactive.common.model.ResourceClass; +import org.jboss.resteasy.reactive.common.model.ResourceWriter; +import org.jboss.resteasy.reactive.common.processor.JandexUtil; +import org.jboss.resteasy.reactive.common.processor.scanning.ApplicationScanningResult; +import org.jboss.resteasy.reactive.common.processor.scanning.ResourceScanningResult; +import org.jboss.resteasy.reactive.common.processor.scanning.ResteasyReactiveInterceptorScanner; +import org.jboss.resteasy.reactive.common.processor.scanning.ResteasyReactiveScanner; +import org.jboss.resteasy.reactive.server.core.Deployment; +import org.jboss.resteasy.reactive.server.core.DeploymentInfo; +import org.jboss.resteasy.reactive.server.core.ServerSerialisers; +import org.jboss.resteasy.reactive.server.core.startup.RuntimeDeploymentManager; +import org.jboss.resteasy.reactive.server.handlers.RestInitialHandler; +import org.jboss.resteasy.reactive.server.processor.ServerEndpointIndexer; +import org.jboss.resteasy.reactive.server.processor.scanning.ResteasyReactiveContextResolverScanner; +import org.jboss.resteasy.reactive.server.processor.scanning.ResteasyReactiveExceptionMappingScanner; +import org.jboss.resteasy.reactive.server.processor.scanning.ResteasyReactiveFeatureScanner; +import org.jboss.resteasy.reactive.server.processor.scanning.ResteasyReactiveParamConverterScanner; +import org.jboss.resteasy.reactive.server.providers.serialisers.ServerStringMessageBodyHandler; +import org.jboss.resteasy.reactive.server.vertx.ResteasyReactiveVertxHandler; +import org.jboss.resteasy.reactive.server.vertx.VertxRequestContextFactory; +import org.jboss.resteasy.reactive.spi.BeanFactory; +import org.jboss.resteasy.reactive.spi.ThreadSetupAction; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.exporter.ExplodedExporter; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.ExtensionContext; + +public class ResteasyReactiveUnitTest implements BeforeAllCallback, AfterAllCallback { + + private static final Logger rootLogger; + private Handler[] originalHandlers; + + static { + System.setProperty("java.util.logging.manager", "org.jboss.logmanager.LogManager"); + rootLogger = (Logger) LogManager.getLogManager().getLogger(""); + } + + private Path deploymentDir; + private Consumer assertException; + private Supplier archiveProducer; + + private Consumer> assertLogRecords; + + private Timer timeoutTimer; + private volatile TimerTask timeoutTask; + private InMemoryLogHandler inMemoryLogHandler = new InMemoryLogHandler((r) -> false); + + static Vertx vertx; + static HttpServer httpServer; + static Router router; + static Route route; + static Executor executor = Executors.newFixedThreadPool(10); + + List closeTasks = new ArrayList<>(); + + public ResteasyReactiveUnitTest setExpectedException(Class expectedException) { + return assertException(t -> { + Throwable i = t; + boolean found = false; + while (i != null) { + if (i.getClass().getName().equals(expectedException.getName())) { + found = true; + break; + } + i = i.getCause(); + } + + assertTrue(found, "Build failed with wrong exception, expected " + expectedException + " but got " + t); + }); + } + + public ResteasyReactiveUnitTest() { + } + + public ResteasyReactiveUnitTest assertException(Consumer assertException) { + if (this.assertException != null) { + throw new IllegalStateException("Don't set the asserted or excepted exception twice" + + " to avoid shadowing out the first call."); + } + this.assertException = assertException; + return this; + } + + public Supplier getArchiveProducer() { + return archiveProducer; + } + + public ResteasyReactiveUnitTest setArchiveProducer(Supplier archiveProducer) { + Objects.requireNonNull(archiveProducer); + this.archiveProducer = archiveProducer; + return this; + } + + public ResteasyReactiveUnitTest assertLogRecords(Consumer> assertLogRecords) { + if (this.assertLogRecords != null) { + throw new IllegalStateException("Don't set the a log record assertion twice" + + " to avoid shadowing out the first call."); + } + this.assertLogRecords = assertLogRecords; + return this; + } + + private void exportArchive(Path deploymentDir, Class testClass) { + try { + JavaArchive archive = getArchiveProducerOrDefault(); + Class c = testClass; + archive.addClasses(c.getClasses()); + while (c != Object.class) { + archive.addClass(c); + c = c.getSuperclass(); + } + archive.as(ExplodedExporter.class).exportExplodedInto(deploymentDir.toFile()); + + } catch (Exception e) { + throw new RuntimeException("Unable to create the archive", e); + } + } + + private JavaArchive getArchiveProducerOrDefault() { + if (archiveProducer == null) { + return ShrinkWrap.create(JavaArchive.class); + } else { + return archiveProducer.get(); + } + } + + @Override + public void beforeAll(ExtensionContext extensionContext) throws Exception { + originalHandlers = rootLogger.getHandlers(); + + timeoutTask = new TimerTask() { + @Override + public void run() { + System.err.println("Test has been running for more than 5 minutes, thread dump is:"); + for (Map.Entry i : Thread.getAllStackTraces().entrySet()) { + System.err.println("\n"); + System.err.println(i.toString()); + System.err.println("\n"); + for (StackTraceElement j : i.getValue()) { + System.err.println(j); + } + } + } + }; + timeoutTimer = new Timer("Test thread dump timer"); + timeoutTimer.schedule(timeoutTask, 1000 * 60 * 5); + ExtensionContext.Store store = extensionContext.getRoot().getStore(ExtensionContext.Namespace.GLOBAL); + + Class testClass = extensionContext.getRequiredTestClass(); + + deploymentDir = Files.createTempDirectory("quarkus-unit-test"); + + exportArchive(deploymentDir, testClass); + + if (vertx == null) { + vertx = Vertx.vertx(); + HttpServer server = vertx.createHttpServer(); + router = Router.router(vertx); + server.requestHandler(router).listen(8080); + store.put(ResteasyReactiveUnitTest.class.getName(), new ExtensionContext.Store.CloseableResource() { + @Override + public void close() throws Throwable { + server.close(); + vertx.close(); + } + }); + } + + Index index = JandexUtil.createIndex(deploymentDir); + ApplicationScanningResult applicationScanningResult = ResteasyReactiveScanner.scanForApplicationClass(index); + ResourceScanningResult resources = ResteasyReactiveScanner.scanResources(index); + if (resources == null) { + throw new RuntimeException("no JAX-RS resources found"); + } + ServerEndpointIndexer serverEndpointIndexer = new ServerEndpointIndexer.Builder() + .setIndex(index) + .setScannedResourcePaths(resources.getScannedResourcePaths()) + .setClassLevelExceptionMappers(new HashMap<>()) + .setInjectableBeans(new HashMap<>()) + .setConfig(new ResteasyReactiveConfig(10000, true)) + .setHttpAnnotationToMethod(resources.getHttpAnnotationToMethod()) + .build(); + + List resourceClasses = new ArrayList<>(); + List subResourceClasses = new ArrayList<>(); + for (Map.Entry i : resources.getScannedResources().entrySet()) { + ResourceClass res = serverEndpointIndexer.createEndpoints(i.getValue()); + resourceClasses.add(res); + } + for (Map.Entry i : resources.getPossibleSubResources().entrySet()) { + ResourceClass res = serverEndpointIndexer.createEndpoints(i.getValue()); + subResourceClasses.add(res); + } + + ServerSerialisers serialisers = new ServerSerialisers(); + serialisers.addWriter(String.class, new ResourceWriter() + .setMediaTypeStrings(Collections.singletonList(MediaType.WILDCARD)) + .setFactory(new BeanFactory>() { + @Override + public BeanInstance> createInstance() { + return new BeanInstance>() { + @Override + public MessageBodyWriter getInstance() { + return new ServerStringMessageBodyHandler(); + } + + @Override + public void close() { + + } + }; + } + })); + DeploymentInfo info = new DeploymentInfo() + .setApplicationPath("/") + .setFeatures(ResteasyReactiveFeatureScanner.createFeatures(index, applicationScanningResult)) + .setInterceptors( + ResteasyReactiveInterceptorScanner.createResourceInterceptors(index, applicationScanningResult)) + .setDynamicFeatures(ResteasyReactiveFeatureScanner.createDynamicFeatures(index, applicationScanningResult)) + .setParamConverterProviders( + ResteasyReactiveParamConverterScanner.createParamConverters(index, applicationScanningResult)) + .setSerialisers(serialisers) + .setExceptionMapping( + ResteasyReactiveExceptionMappingScanner.createExceptionMappers(index, applicationScanningResult)) + .setResourceClasses(resourceClasses) + .setCtxResolvers( + ResteasyReactiveContextResolverScanner.createContextResolvers(index, applicationScanningResult)) + .setLocatableResourceClasses(subResourceClasses) + .setApplicationSupplier(new Supplier() { + @Override + public Application get() { + if (applicationScanningResult.getSelectedAppClass() == null) { + return new Application(); + } else { + try { + return (Application) Class + .forName(applicationScanningResult.getSelectedAppClass().name().toString(), false, + Thread.currentThread().getContextClassLoader()) + .newInstance(); + } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + } + }); + RuntimeDeploymentManager runtimeDeploymentManager = new RuntimeDeploymentManager(info, () -> executor, + closeable -> closeTasks.add(closeable), new VertxRequestContextFactory(), ThreadSetupAction.NOOP, "/"); + Deployment deployment = runtimeDeploymentManager.deploy(); + RestInitialHandler initialHandler = new RestInitialHandler(deployment); + router.route().handler(new ResteasyReactiveVertxHandler(initialHandler)); + + } + + @Override + public void afterAll(ExtensionContext extensionContext) throws Exception { + if (assertLogRecords != null) { + assertLogRecords.accept(inMemoryLogHandler.records); + } + //rootLogger.setHandlers(originalHandlers); + inMemoryLogHandler.clearRecords(); + + System.clearProperty("test.url"); + timeoutTask.cancel(); + timeoutTask = null; + timeoutTimer = null; + if (deploymentDir != null) { + deleteDirectory(deploymentDir); + } + + } + + public static void deleteDirectory(final Path directory) throws IOException { + if (!Files.isDirectory(directory)) { + return; + } + Files.walkFileTree(directory, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + try { + Files.delete(file); + } catch (IOException e) { + // ignored + } + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + try { + Files.delete(dir); + } catch (IOException e) { + // ignored + } + return FileVisitResult.CONTINUE; + } + + }); + } + +} From 599e19207f3532ab9caebf0bfaaa416d12d6f13c Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Wed, 9 Dec 2020 19:30:39 +0100 Subject: [PATCH 39/62] Add .env to .gitignore in generated projects --- .../quarkus-jbang/project/quarkus-jbang/base/..gitignore | 2 ++ .../codestarts/quarkus/core/project/quarkus/base/..gitignore | 3 +++ .../src/main/resources/templates/gitignore.ftl | 4 ++++ 3 files changed, 9 insertions(+) diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/project/quarkus-jbang/base/..gitignore b/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/project/quarkus-jbang/base/..gitignore index ca0fab1daa709..3cf482a1da8dc 100644 --- a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/project/quarkus-jbang/base/..gitignore +++ b/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/project/quarkus-jbang/base/..gitignore @@ -28,3 +28,5 @@ nb-configuration.xml *.orig *.rej +# Local environment +.env diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/project/quarkus/base/..gitignore b/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/project/quarkus/base/..gitignore index aff36ab1807a0..3cf482a1da8dc 100644 --- a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/project/quarkus/base/..gitignore +++ b/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/core/project/quarkus/base/..gitignore @@ -27,3 +27,6 @@ nb-configuration.xml # patch *.orig *.rej + +# Local environment +.env diff --git a/devtools/platform-descriptor-json/src/main/resources/templates/gitignore.ftl b/devtools/platform-descriptor-json/src/main/resources/templates/gitignore.ftl index 667fae3d7021e..38db6efa816ad 100644 --- a/devtools/platform-descriptor-json/src/main/resources/templates/gitignore.ftl +++ b/devtools/platform-descriptor-json/src/main/resources/templates/gitignore.ftl @@ -27,4 +27,8 @@ nb-configuration.xml # patch *.orig *.rej + +# Local environment +.env + ${additional_gitignore_entries} From 044b67877c19b50e47d27ecd542f605b63c026b5 Mon Sep 17 00:00:00 2001 From: Martin Kouba Date: Thu, 10 Dec 2020 09:27:54 +0100 Subject: [PATCH 40/62] Qute type-safe templates: fix parameter assignability check --- .../qute/deployment/QuteProcessor.java | 25 +++++++++++-------- .../io/quarkus/qute/deployment/TypeInfos.java | 2 +- .../typesafe/ValidationSuccessTest.java | 12 ++++++--- .../runtime/devmode/QuteErrorPageSetup.java | 2 +- 4 files changed, 26 insertions(+), 15 deletions(-) diff --git a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java index d0af2dd94e24b..749ed9cf348eb 100644 --- a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java +++ b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java @@ -566,9 +566,13 @@ static Match validateNestedExpressions(TemplateAnalysis templateAnalysis, ClassI Info info = iterator.next(); if (match.clazz != null) { // By default, we only consider properties - Set membersUsed = implicitClassToMembersUsed.computeIfAbsent(match.clazz, c -> new HashSet<>()); + Set membersUsed = implicitClassToMembersUsed.get(match.clazz); + if (membersUsed == null) { + membersUsed = new HashSet<>(); + implicitClassToMembersUsed.put(match.clazz, membersUsed); + } AnnotationTarget member = null; - // First try to find java members + // First try to find a java member if (info.isVirtualMethod()) { member = findMethod(info.part.asVirtualMethod(), match.clazz, expression, index, templateIdToPathFun, results); @@ -581,9 +585,10 @@ static Match validateNestedExpressions(TemplateAnalysis templateAnalysis, ClassI membersUsed.add(member.kind() == Kind.FIELD ? member.asField().name() : member.asMethod().name()); } } - // Java member not found - try extension methods if (member == null) { - member = findTemplateExtensionMethod(info, match.clazz, templateExtensionMethods, expression, index, + // Then try to find an etension method + member = findTemplateExtensionMethod(info, match.clazz, templateExtensionMethods, expression, + index, templateIdToPathFun, results); } @@ -1219,7 +1224,8 @@ private static AnnotationTarget findTemplateExtensionMethod(Info info, ClassInfo continue; } List parameters = extensionMethod.getMethod().parameters(); - if (parameters.size() > 1 && !info.isVirtualMethod()) { + int realParamSize = parameters.size() - (TemplateExtension.ANY.equals(extensionMethod.getMatchName()) ? 2 : 1); + if (realParamSize > 0 && !info.isVirtualMethod()) { // If method accepts additional params the info must be a virtual method continue; } @@ -1228,7 +1234,6 @@ private static AnnotationTarget findTemplateExtensionMethod(Info info, ClassInfo VirtualMethodPart virtualMethod = info.part.asVirtualMethod(); boolean isVarArgs = ValueResolverGenerator.isVarArgs(extensionMethod.getMethod()); int lastParamIdx = parameters.size() - 1; - int realParamSize = parameters.size() - (TemplateExtension.ANY.equals(extensionMethod.getMatchName()) ? 2 : 1); if (isVarArgs) { // For varargs methods match the minimal number of params @@ -1259,8 +1264,8 @@ private static AnnotationTarget findTemplateExtensionMethod(Info info, ClassInfo } else { paramType = parameters.get(idx); } - if (!Types.isAssignableFrom(result.type, - paramType, index)) { + if (!Types.isAssignableFrom(paramType, + result.type, index)) { matches = false; break; } @@ -1376,8 +1381,8 @@ private static AnnotationTarget findMethod(VirtualMethodPart virtualMethod, Clas } else { paramType = parameters.get(idx); } - if (!Types.isAssignableFrom(result.type, - paramType, index)) { + if (!Types.isAssignableFrom(paramType, + result.type, index)) { matches = false; break; } diff --git a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/TypeInfos.java b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/TypeInfos.java index fe04f9a76d7e9..1f4aba5bcd58f 100644 --- a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/TypeInfos.java +++ b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/TypeInfos.java @@ -109,7 +109,7 @@ private static Type resolveType(String value) { String[] parts = value.substring(angleIdx + 1, value.length() - 1).split(","); Type[] arguments = new Type[parts.length]; for (int i = 0; i < arguments.length; i++) { - arguments[i] = resolveType(parts[i]); + arguments[i] = resolveType(parts[i].trim()); } return ParameterizedType.create(rawName, arguments, null); } diff --git a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/typesafe/ValidationSuccessTest.java b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/typesafe/ValidationSuccessTest.java index 001e438b7f296..971e140a808cf 100644 --- a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/typesafe/ValidationSuccessTest.java +++ b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/typesafe/ValidationSuccessTest.java @@ -2,6 +2,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; +import java.util.Collections; + import javax.inject.Inject; import org.jboss.shrinkwrap.api.ShrinkWrap; @@ -22,6 +24,7 @@ public class ValidationSuccessTest { .addAsResource(new StringAsset("{@io.quarkus.qute.deployment.typesafe.Movie movie}" + "{@java.lang.Long age}" + "{@java.lang.String surname}" + + "{@java.util.Map Date: Mon, 7 Dec 2020 16:35:13 +0100 Subject: [PATCH 41/62] Fix the name of the Hibernate Search ORM + Elasticsearch extension in build items MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yoann Rodière --- .../elasticsearch/HibernateSearchElasticsearchProcessor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/HibernateSearchElasticsearchProcessor.java b/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/HibernateSearchElasticsearchProcessor.java index 8894fe3bdbaec..eae21d7c0f3e0 100644 --- a/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/HibernateSearchElasticsearchProcessor.java +++ b/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/HibernateSearchElasticsearchProcessor.java @@ -47,7 +47,7 @@ class HibernateSearchElasticsearchProcessor { - private static final String HIBERNATE_SEARCH_ELASTICSEARCH = "Hibernate Search Elasticsearch"; + private static final String HIBERNATE_SEARCH_ELASTICSEARCH = "Hibernate Search ORM + Elasticsearch"; HibernateSearchElasticsearchBuildTimeConfig buildTimeConfig; From 30d666ce63e64a3abad07c7228773f2fddc930d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Mon, 7 Dec 2020 17:31:01 +0100 Subject: [PATCH 42/62] Factorize two nearly identical PersistenceProviderResolvers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yoann Rodière --- .../orm/runtime/PersistenceProviderSetup.java | 3 +- ...SingletonPersistenceProviderResolver.java} | 6 ++-- ...teReactivePersistenceProviderResolver.java | 29 ------------------- .../ReactivePersistenceProviderSetup.java | 4 ++- 4 files changed, 8 insertions(+), 34 deletions(-) rename extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/{FastBootHibernatePersistenceProviderResolver.java => SingletonPersistenceProviderResolver.java} (59%) delete mode 100644 extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/FastBootHibernateReactivePersistenceProviderResolver.java diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/PersistenceProviderSetup.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/PersistenceProviderSetup.java index e88ea06807a0c..9830ed6f79595 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/PersistenceProviderSetup.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/PersistenceProviderSetup.java @@ -13,6 +13,7 @@ public static void registerStaticInitPersistenceProvider() { public static void registerRuntimePersistenceProvider(HibernateOrmRuntimeConfig hibernateOrmRuntimeConfig) { javax.persistence.spi.PersistenceProviderResolverHolder - .setPersistenceProviderResolver(new FastBootHibernatePersistenceProviderResolver(hibernateOrmRuntimeConfig)); + .setPersistenceProviderResolver(new SingletonPersistenceProviderResolver( + new FastBootHibernatePersistenceProvider(hibernateOrmRuntimeConfig))); } } diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/FastBootHibernatePersistenceProviderResolver.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/SingletonPersistenceProviderResolver.java similarity index 59% rename from extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/FastBootHibernatePersistenceProviderResolver.java rename to extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/SingletonPersistenceProviderResolver.java index a8bd595fdfbf5..2deb6703713f7 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/FastBootHibernatePersistenceProviderResolver.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/SingletonPersistenceProviderResolver.java @@ -6,12 +6,12 @@ import javax.persistence.spi.PersistenceProvider; import javax.persistence.spi.PersistenceProviderResolver; -final class FastBootHibernatePersistenceProviderResolver implements PersistenceProviderResolver { +public final class SingletonPersistenceProviderResolver implements PersistenceProviderResolver { private final List persistenceProviders; - public FastBootHibernatePersistenceProviderResolver(HibernateOrmRuntimeConfig hibernateOrmRuntimeConfig) { - persistenceProviders = Collections.singletonList(new FastBootHibernatePersistenceProvider(hibernateOrmRuntimeConfig)); + public SingletonPersistenceProviderResolver(PersistenceProvider singleton) { + persistenceProviders = Collections.singletonList(singleton); } @Override diff --git a/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/FastBootHibernateReactivePersistenceProviderResolver.java b/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/FastBootHibernateReactivePersistenceProviderResolver.java deleted file mode 100644 index 66d868af64bd0..0000000000000 --- a/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/FastBootHibernateReactivePersistenceProviderResolver.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.quarkus.hibernate.reactive.runtime; - -import java.util.Collections; -import java.util.List; - -import javax.persistence.spi.PersistenceProvider; -import javax.persistence.spi.PersistenceProviderResolver; - -import io.quarkus.hibernate.orm.runtime.HibernateOrmRuntimeConfig; - -final class FastBootHibernateReactivePersistenceProviderResolver implements PersistenceProviderResolver { - - private final List persistenceProviders; - - public FastBootHibernateReactivePersistenceProviderResolver(HibernateOrmRuntimeConfig hibernateOrmRuntimeConfig) { - persistenceProviders = Collections - .singletonList(new FastBootHibernateReactivePersistenceProvider(hibernateOrmRuntimeConfig)); - } - - @Override - public List getPersistenceProviders() { - return persistenceProviders; - } - - @Override - public void clearCachedProviders() { - // done! - } -} diff --git a/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/ReactivePersistenceProviderSetup.java b/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/ReactivePersistenceProviderSetup.java index 5e97db766f0ff..aa8004fd9bd14 100644 --- a/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/ReactivePersistenceProviderSetup.java +++ b/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/ReactivePersistenceProviderSetup.java @@ -1,6 +1,7 @@ package io.quarkus.hibernate.reactive.runtime; import io.quarkus.hibernate.orm.runtime.HibernateOrmRuntimeConfig; +import io.quarkus.hibernate.orm.runtime.SingletonPersistenceProviderResolver; public final class ReactivePersistenceProviderSetup { @@ -16,7 +17,8 @@ public static void registerStaticInitPersistenceProvider() { public static void registerRuntimePersistenceProvider(HibernateOrmRuntimeConfig hibernateOrmRuntimeConfig) { javax.persistence.spi.PersistenceProviderResolverHolder .setPersistenceProviderResolver( - new FastBootHibernateReactivePersistenceProviderResolver(hibernateOrmRuntimeConfig)); + new SingletonPersistenceProviderResolver( + new FastBootHibernateReactivePersistenceProvider(hibernateOrmRuntimeConfig))); } } From 10ef1589c25c0feb4d380962fa09da64067c252f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Mon, 7 Dec 2020 17:48:04 +0100 Subject: [PATCH 43/62] Register Hibernate ORM integration listeners through build items MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ... instead of dumping them to a static List field via recorders. It's not really "better", but it should make the following changes clearer. Signed-off-by: Yoann Rodière --- .../deployment/HibernateEnversProcessor.java | 11 ++- .../envers/HibernateEnversRecorder.java | 14 +-- .../orm/deployment/HibernateOrmProcessor.java | 18 ++-- .../PersistenceUnitDescriptorBuildItem.java | 7 +- .../HibernateOrmIntegrationBuildItem.java | 26 ----- ...IntegrationRuntimeConfiguredBuildItem.java | 22 ++++- ...mIntegrationStaticConfiguredBuildItem.java | 45 +++++++++ .../FastBootHibernatePersistenceProvider.java | 14 ++- .../orm/runtime/HibernateOrmRecorder.java | 6 +- .../orm/runtime/PersistenceProviderSetup.java | 13 ++- .../runtime/boot/FastBootMetadataBuilder.java | 23 ++++- .../QuarkusPersistenceUnitDefinition.java | 26 ++++- ...nateOrmIntegrationRuntimeInitListener.java | 9 ++ ...nateOrmIntegrationStaticInitListener.java} | 3 +- .../integration/HibernateOrmIntegrations.java | 36 ------- .../HibernateReactiveProcessor.java | 18 ++-- ...tHibernateReactivePersistenceProvider.java | 17 +++- .../runtime/HibernateReactiveRecorder.java | 9 +- .../ReactivePersistenceProviderSetup.java | 9 +- ...HibernateSearchElasticsearchProcessor.java | 26 ++--- .../ElasticsearchVersionSubstitution.java | 17 ++++ .../HibernateSearchElasticsearchRecorder.java | 98 +++++++++++-------- 22 files changed, 293 insertions(+), 174 deletions(-) delete mode 100644 extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/integration/HibernateOrmIntegrationBuildItem.java create mode 100644 extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/integration/HibernateOrmIntegrationStaticConfiguredBuildItem.java create mode 100644 extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/integration/HibernateOrmIntegrationRuntimeInitListener.java rename extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/integration/{HibernateOrmIntegrationListener.java => HibernateOrmIntegrationStaticInitListener.java} (75%) delete mode 100644 extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/integration/HibernateOrmIntegrations.java create mode 100644 extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/ElasticsearchVersionSubstitution.java diff --git a/extensions/hibernate-envers/deployment/src/main/java/io/quarkus/hibernate/envers/deployment/HibernateEnversProcessor.java b/extensions/hibernate-envers/deployment/src/main/java/io/quarkus/hibernate/envers/deployment/HibernateEnversProcessor.java index 9604afb0b264e..73b5cafd696d4 100644 --- a/extensions/hibernate-envers/deployment/src/main/java/io/quarkus/hibernate/envers/deployment/HibernateEnversProcessor.java +++ b/extensions/hibernate-envers/deployment/src/main/java/io/quarkus/hibernate/envers/deployment/HibernateEnversProcessor.java @@ -16,9 +16,12 @@ import io.quarkus.hibernate.envers.HibernateEnversBuildTimeConfig; import io.quarkus.hibernate.envers.HibernateEnversRecorder; import io.quarkus.hibernate.orm.deployment.AdditionalJpaModelBuildItem; +import io.quarkus.hibernate.orm.deployment.integration.HibernateOrmIntegrationStaticConfiguredBuildItem; public final class HibernateEnversProcessor { + private static final String HIBERNATE_ENVERS = "Hibernate Envers"; + @BuildStep FeatureBuildItem feature() { return new FeatureBuildItem(Feature.HIBERNATE_ENVERS); @@ -49,9 +52,11 @@ public void registerEnversReflections(BuildProducer re @BuildStep @Record(ExecutionTime.STATIC_INIT) - public void applyConfig(HibernateEnversRecorder recorder, - HibernateEnversBuildTimeConfig buildTimeConfig) { - recorder.registerHibernateEnversIntegration(buildTimeConfig); + public void applyConfig(HibernateEnversRecorder recorder, HibernateEnversBuildTimeConfig buildTimeConfig, + BuildProducer integrationProducer) { + integrationProducer.produce( + new HibernateOrmIntegrationStaticConfiguredBuildItem(HIBERNATE_ENVERS, + recorder.createStaticInitListener(buildTimeConfig))); } @BuildStep diff --git a/extensions/hibernate-envers/runtime/src/main/java/io/quarkus/hibernate/envers/HibernateEnversRecorder.java b/extensions/hibernate-envers/runtime/src/main/java/io/quarkus/hibernate/envers/HibernateEnversRecorder.java index e08d2aa60f937..29d9a3386adbd 100644 --- a/extensions/hibernate-envers/runtime/src/main/java/io/quarkus/hibernate/envers/HibernateEnversRecorder.java +++ b/extensions/hibernate-envers/runtime/src/main/java/io/quarkus/hibernate/envers/HibernateEnversRecorder.java @@ -6,19 +6,17 @@ import org.hibernate.boot.spi.BootstrapContext; import org.hibernate.envers.configuration.EnversSettings; -import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationListener; -import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrations; +import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationStaticInitListener; import io.quarkus.runtime.annotations.Recorder; @Recorder public class HibernateEnversRecorder { - public void registerHibernateEnversIntegration(HibernateEnversBuildTimeConfig buildTimeConfig) { - HibernateOrmIntegrations.registerListener(new HibernateEnversIntegrationListener(buildTimeConfig)); + public HibernateOrmIntegrationStaticInitListener createStaticInitListener(HibernateEnversBuildTimeConfig buildTimeConfig) { + return new HibernateEnversIntegrationListener(buildTimeConfig); } - private static final class HibernateEnversIntegrationListener implements HibernateOrmIntegrationListener { - + private static final class HibernateEnversIntegrationListener implements HibernateOrmIntegrationStaticInitListener { private HibernateEnversBuildTimeConfig buildTimeConfig; private HibernateEnversIntegrationListener(HibernateEnversBuildTimeConfig buildTimeConfig) { @@ -38,9 +36,5 @@ public static void addConfig(BiConsumer propertyCollector, S public void onMetadataInitialized(Metadata metadata, BootstrapContext bootstrapContext, BiConsumer propertyCollector) { } - - @Override - public void contributeRuntimeProperties(BiConsumer propertyCollector) { - } } } diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java index 28527979f0850..519671fd710f5 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java @@ -108,8 +108,8 @@ import io.quarkus.deployment.util.IoUtil; import io.quarkus.deployment.util.ServiceUtil; import io.quarkus.hibernate.orm.PersistenceUnit; -import io.quarkus.hibernate.orm.deployment.integration.HibernateOrmIntegrationBuildItem; import io.quarkus.hibernate.orm.deployment.integration.HibernateOrmIntegrationRuntimeConfiguredBuildItem; +import io.quarkus.hibernate.orm.deployment.integration.HibernateOrmIntegrationStaticConfiguredBuildItem; import io.quarkus.hibernate.orm.runtime.HibernateOrmRecorder; import io.quarkus.hibernate.orm.runtime.HibernateOrmRuntimeConfig; import io.quarkus.hibernate.orm.runtime.JPAConfig; @@ -121,6 +121,7 @@ import io.quarkus.hibernate.orm.runtime.boot.scan.QuarkusScanner; import io.quarkus.hibernate.orm.runtime.dialect.QuarkusH2Dialect; import io.quarkus.hibernate.orm.runtime.dialect.QuarkusPostgreSQL10Dialect; +import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationStaticInitListener; import io.quarkus.hibernate.orm.runtime.proxies.PreGeneratedProxies; import io.quarkus.hibernate.orm.runtime.tenant.DataSourceTenantConnectionResolver; import io.quarkus.hibernate.orm.runtime.tenant.TenantConnectionResolver; @@ -384,7 +385,7 @@ public void build(RecorderContext recorderContext, HibernateOrmRecorder recorder JpaEntitiesBuildItem domainObjects, List nonJpaModelBuildItems, List persistenceUnitDescriptorBuildItems, - List integrations, //Used to make sure ORM integrations are performed before this item + List integrationBuildItems, ProxyDefinitionsBuildItem proxyDefinitions, BuildProducer feature, BuildProducer beanContainerListener) throws Exception { @@ -418,9 +419,12 @@ public void build(RecorderContext recorderContext, HibernateOrmRecorder recorder integratorClasses.add((Class) recorderContext.classProxy(integratorClassName)); } + List integrationStaticInitListeners = HibernateOrmIntegrationStaticConfiguredBuildItem + .collectListeners(integrationBuildItems); + List finalStagePUDescriptors = new ArrayList<>(); for (PersistenceUnitDescriptorBuildItem pud : persistenceUnitDescriptorBuildItems) { - finalStagePUDescriptors.add(pud.asOutputPersistenceUnitDefinition()); + finalStagePUDescriptors.add(pud.asOutputPersistenceUnitDefinition(integrationStaticInitListeners)); } //Make it possible to record the QuarkusPersistenceUnitDefinition as bytecode: @@ -523,10 +527,11 @@ public void build(HibernateOrmRecorder recorder, HibernateOrmConfig hibernateOrm @BuildStep @Record(RUNTIME_INIT) public PersistenceProviderSetUpBuildItem setupPersistenceProvider(HibernateOrmRecorder recorder, - Capabilities capabilities, - HibernateOrmRuntimeConfig hibernateOrmRuntimeConfig) { + Capabilities capabilities, HibernateOrmRuntimeConfig hibernateOrmRuntimeConfig, + List integrationBuildItems) { if (capabilities.isMissing(Capability.HIBERNATE_REACTIVE)) { - recorder.setupPersistenceProvider(hibernateOrmRuntimeConfig); + recorder.setupPersistenceProvider(hibernateOrmRuntimeConfig, + HibernateOrmIntegrationRuntimeConfiguredBuildItem.collectListeners(integrationBuildItems)); } return new PersistenceProviderSetUpBuildItem(); @@ -537,7 +542,6 @@ public PersistenceProviderSetUpBuildItem setupPersistenceProvider(HibernateOrmRe public ServiceStartBuildItem startPersistenceUnits(HibernateOrmRecorder recorder, BeanContainerBuildItem beanContainer, List dataSourcesConfigured, JpaEntitiesBuildItem jpaEntities, List nonJpaModels, - List integrationsRuntimeConfigured, List schemaReadyBuildItem, List persistenceProviderSetUp) throws Exception { if (hasEntities(jpaEntities, nonJpaModels)) { diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/PersistenceUnitDescriptorBuildItem.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/PersistenceUnitDescriptorBuildItem.java index 714cae62b24a3..4b5622eef151c 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/PersistenceUnitDescriptorBuildItem.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/PersistenceUnitDescriptorBuildItem.java @@ -1,6 +1,7 @@ package io.quarkus.hibernate.orm.deployment; import java.util.Collection; +import java.util.List; import org.hibernate.MultiTenancyStrategy; import org.hibernate.jpa.boot.internal.ParsedPersistenceXmlDescriptor; @@ -8,6 +9,7 @@ import io.quarkus.builder.item.MultiBuildItem; import io.quarkus.datasource.common.runtime.DataSourceUtil; import io.quarkus.hibernate.orm.runtime.boot.QuarkusPersistenceUnitDefinition; +import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationStaticInitListener; /** * Not to be confused with PersistenceXmlDescriptorBuildItem, which holds @@ -72,8 +74,9 @@ public String getMultiTenancySchemaDataSource() { return multiTenancySchemaDataSource; } - public QuarkusPersistenceUnitDefinition asOutputPersistenceUnitDefinition() { + public QuarkusPersistenceUnitDefinition asOutputPersistenceUnitDefinition( + List integrationStaticInitListeners) { return new QuarkusPersistenceUnitDefinition(descriptor, dataSource, multiTenancyStrategy, isReactive, - fromPersistenceXml, enversIsPresent); + fromPersistenceXml, integrationStaticInitListeners, enversIsPresent); } } diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/integration/HibernateOrmIntegrationBuildItem.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/integration/HibernateOrmIntegrationBuildItem.java deleted file mode 100644 index 1345a4454c760..0000000000000 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/integration/HibernateOrmIntegrationBuildItem.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.quarkus.hibernate.orm.deployment.integration; - -import io.quarkus.builder.item.MultiBuildItem; - -public final class HibernateOrmIntegrationBuildItem extends MultiBuildItem { - - private final String name; - - public HibernateOrmIntegrationBuildItem(String name) { - if (name == null) { - throw new IllegalArgumentException("name cannot be null"); - } - this.name = name; - } - - public String getName() { - return name; - } - - @Override - public String toString() { - return new StringBuilder().append(HibernateOrmIntegrationBuildItem.class.getSimpleName()) - .append(" [").append(name).append("]") - .toString(); - } -} diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/integration/HibernateOrmIntegrationRuntimeConfiguredBuildItem.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/integration/HibernateOrmIntegrationRuntimeConfiguredBuildItem.java index 508cb8e04ac51..2c363837c0782 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/integration/HibernateOrmIntegrationRuntimeConfiguredBuildItem.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/integration/HibernateOrmIntegrationRuntimeConfiguredBuildItem.java @@ -1,26 +1,46 @@ package io.quarkus.hibernate.orm.deployment.integration; +import java.util.ArrayList; +import java.util.List; + import io.quarkus.builder.item.MultiBuildItem; +import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationRuntimeInitListener; public final class HibernateOrmIntegrationRuntimeConfiguredBuildItem extends MultiBuildItem { private final String name; + private final HibernateOrmIntegrationRuntimeInitListener listener; - public HibernateOrmIntegrationRuntimeConfiguredBuildItem(String name) { + public HibernateOrmIntegrationRuntimeConfiguredBuildItem(String name, + HibernateOrmIntegrationRuntimeInitListener listener) { if (name == null) { throw new IllegalArgumentException("name cannot be null"); } this.name = name; + this.listener = listener; } public String getName() { return name; } + public HibernateOrmIntegrationRuntimeInitListener getListener() { + return listener; + } + @Override public String toString() { return new StringBuilder().append(HibernateOrmIntegrationRuntimeConfiguredBuildItem.class.getSimpleName()) .append(" [").append(name).append("]") .toString(); } + + public static List collectListeners( + List items) { + List listeners = new ArrayList<>(); + for (HibernateOrmIntegrationRuntimeConfiguredBuildItem item : items) { + listeners.add(item.getListener()); + } + return listeners; + } } diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/integration/HibernateOrmIntegrationStaticConfiguredBuildItem.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/integration/HibernateOrmIntegrationStaticConfiguredBuildItem.java new file mode 100644 index 0000000000000..c4cb07d1d7c54 --- /dev/null +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/integration/HibernateOrmIntegrationStaticConfiguredBuildItem.java @@ -0,0 +1,45 @@ +package io.quarkus.hibernate.orm.deployment.integration; + +import java.util.ArrayList; +import java.util.List; + +import io.quarkus.builder.item.MultiBuildItem; +import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationStaticInitListener; + +public final class HibernateOrmIntegrationStaticConfiguredBuildItem extends MultiBuildItem { + + private final String name; + private final HibernateOrmIntegrationStaticInitListener listener; + + public HibernateOrmIntegrationStaticConfiguredBuildItem(String name, HibernateOrmIntegrationStaticInitListener listener) { + if (name == null) { + throw new IllegalArgumentException("name cannot be null"); + } + this.name = name; + this.listener = listener; + } + + public String getName() { + return name; + } + + public HibernateOrmIntegrationStaticInitListener getListener() { + return listener; + } + + @Override + public String toString() { + return new StringBuilder().append(HibernateOrmIntegrationStaticConfiguredBuildItem.class.getSimpleName()) + .append(" [").append(name).append("]") + .toString(); + } + + public static List collectListeners( + List items) { + List listeners = new ArrayList<>(); + for (HibernateOrmIntegrationStaticConfiguredBuildItem item : items) { + listeners.add(item.getListener()); + } + return listeners; + } +} diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/FastBootHibernatePersistenceProvider.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/FastBootHibernatePersistenceProvider.java index d6739807660a5..9e7ff745c350e 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/FastBootHibernatePersistenceProvider.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/FastBootHibernatePersistenceProvider.java @@ -25,7 +25,7 @@ import io.quarkus.hibernate.orm.runtime.RuntimeSettings.Builder; import io.quarkus.hibernate.orm.runtime.boot.FastBootEntityManagerFactoryBuilder; import io.quarkus.hibernate.orm.runtime.boot.registry.PreconfiguredServiceRegistryBuilder; -import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrations; +import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationRuntimeInitListener; import io.quarkus.hibernate.orm.runtime.recording.PrevalidatedQuarkusMetadata; import io.quarkus.hibernate.orm.runtime.recording.RecordedState; @@ -42,9 +42,12 @@ public final class FastBootHibernatePersistenceProvider implements PersistencePr private final ProviderUtil providerUtil = new ProviderUtil(); private final HibernateOrmRuntimeConfig hibernateOrmRuntimeConfig; + private final List integrationRuntimeInitListeners; - public FastBootHibernatePersistenceProvider(HibernateOrmRuntimeConfig hibernateOrmRuntimeConfig) { + public FastBootHibernatePersistenceProvider(HibernateOrmRuntimeConfig hibernateOrmRuntimeConfig, + List integrationRuntimeInitListeners) { this.hibernateOrmRuntimeConfig = hibernateOrmRuntimeConfig; + this.integrationRuntimeInitListeners = integrationRuntimeInitListeners; } @SuppressWarnings("rawtypes") @@ -173,7 +176,12 @@ private EntityManagerFactoryBuilder getEntityManagerFactoryBuilderOrNull(String injectRuntimeConfiguration(persistenceUnitName, hibernateOrmRuntimeConfig, runtimeSettingsBuilder); } - HibernateOrmIntegrations.contributeRuntimeProperties((k, v) -> runtimeSettingsBuilder.put(k, v)); + for (HibernateOrmIntegrationRuntimeInitListener listener : integrationRuntimeInitListeners) { + if (listener == null) { + continue; + } + listener.contributeRuntimeProperties(runtimeSettingsBuilder::put); + } // Allow detection of driver/database capabilities on runtime init (was disabled during static init) runtimeSettingsBuilder.put("hibernate.temp.use_jdbc_metadata_defaults", "true"); diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRecorder.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRecorder.java index ab7a869abda0c..65bed40166b56 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRecorder.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRecorder.java @@ -17,6 +17,7 @@ import io.quarkus.arc.runtime.BeanContainer; import io.quarkus.arc.runtime.BeanContainerListener; import io.quarkus.hibernate.orm.runtime.boot.QuarkusPersistenceUnitDefinition; +import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationRuntimeInitListener; import io.quarkus.hibernate.orm.runtime.proxies.PreGeneratedProxies; import io.quarkus.hibernate.orm.runtime.session.ForwardingSession; import io.quarkus.hibernate.orm.runtime.tenant.DataSourceTenantConnectionResolver; @@ -44,8 +45,9 @@ public void callHibernateFeatureInit(boolean enabled) { Hibernate.featureInit(enabled); } - public void setupPersistenceProvider(HibernateOrmRuntimeConfig hibernateOrmRuntimeConfig) { - PersistenceProviderSetup.registerRuntimePersistenceProvider(hibernateOrmRuntimeConfig); + public void setupPersistenceProvider(HibernateOrmRuntimeConfig hibernateOrmRuntimeConfig, + List integrationRuntimeInitListeners) { + PersistenceProviderSetup.registerRuntimePersistenceProvider(hibernateOrmRuntimeConfig, integrationRuntimeInitListeners); } public BeanContainerListener initMetadata(List parsedPersistenceXmlDescriptors, diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/PersistenceProviderSetup.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/PersistenceProviderSetup.java index 9830ed6f79595..c4816707b74ed 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/PersistenceProviderSetup.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/PersistenceProviderSetup.java @@ -1,5 +1,9 @@ package io.quarkus.hibernate.orm.runtime; +import java.util.List; + +import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationRuntimeInitListener; + public final class PersistenceProviderSetup { private PersistenceProviderSetup() { @@ -11,9 +15,10 @@ public static void registerStaticInitPersistenceProvider() { .setPersistenceProviderResolver(new StaticInitHibernatePersistenceProviderResolver()); } - public static void registerRuntimePersistenceProvider(HibernateOrmRuntimeConfig hibernateOrmRuntimeConfig) { - javax.persistence.spi.PersistenceProviderResolverHolder - .setPersistenceProviderResolver(new SingletonPersistenceProviderResolver( - new FastBootHibernatePersistenceProvider(hibernateOrmRuntimeConfig))); + public static void registerRuntimePersistenceProvider(HibernateOrmRuntimeConfig hibernateOrmRuntimeConfig, + List integrationRuntimeInitListeners) { + javax.persistence.spi.PersistenceProviderResolverHolder.setPersistenceProviderResolver( + new SingletonPersistenceProviderResolver( + new FastBootHibernatePersistenceProvider(hibernateOrmRuntimeConfig, integrationRuntimeInitListeners))); } } diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/FastBootMetadataBuilder.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/FastBootMetadataBuilder.java index 5aa9a89d754b8..6dd84cfab1d95 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/FastBootMetadataBuilder.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/FastBootMetadataBuilder.java @@ -71,7 +71,7 @@ import io.quarkus.hibernate.orm.runtime.BuildTimeSettings; import io.quarkus.hibernate.orm.runtime.IntegrationSettings; -import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrations; +import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationStaticInitListener; import io.quarkus.hibernate.orm.runtime.proxies.PreGeneratedProxies; import io.quarkus.hibernate.orm.runtime.proxies.ProxyDefinitions; import io.quarkus.hibernate.orm.runtime.recording.PrevalidatedQuarkusMetadata; @@ -100,18 +100,20 @@ public class FastBootMetadataBuilder { private final MultiTenancyStrategy multiTenancyStrategy; private final boolean isReactive; private final boolean fromPersistenceXml; + private final List integrationStaticInitListeners; private final boolean isEnversPresent; @SuppressWarnings("unchecked") public FastBootMetadataBuilder(final QuarkusPersistenceUnitDefinition puDefinition, Scanner scanner, Collection> additionalIntegrators, PreGeneratedProxies preGeneratedProxies) { this.persistenceUnit = puDefinition.getActualHibernateDescriptor(); - this.isEnversPresent = puDefinition.isEnversPresent(); this.dataSource = puDefinition.getDataSource(); this.isReactive = puDefinition.isReactive(); this.fromPersistenceXml = puDefinition.isFromPersistenceXml(); this.additionalIntegrators = additionalIntegrators; this.preGeneratedProxies = preGeneratedProxies; + this.integrationStaticInitListeners = puDefinition.getIntegrationStaticInitListeners(); + this.isEnversPresent = puDefinition.isEnversPresent(); // Copying semantics from: new EntityManagerFactoryBuilderImpl( unit, // integration, instance ); @@ -298,7 +300,12 @@ private MergedSettings mergeSettings(PersistenceUnitDescriptor persistenceUnit) cfg.put(org.hibernate.cfg.AvailableSettings.CACHE_REGION_FACTORY, QuarkusInfinispanRegionFactory.class.getName()); - HibernateOrmIntegrations.contributeBootProperties((k, v) -> cfg.put(k, v)); + for (HibernateOrmIntegrationStaticInitListener listener : integrationStaticInitListeners) { + if (listener == null) { + continue; + } + listener.contributeBootProperties(cfg::put); + } return mergedSettings; } @@ -311,8 +318,14 @@ public RecordedState build() { ); IntegrationSettings.Builder integrationSettingsBuilder = new IntegrationSettings.Builder(); - HibernateOrmIntegrations.onMetadataInitialized(fullMeta, metamodelBuilder.getBootstrapContext(), - (k, v) -> integrationSettingsBuilder.put(k, v)); + + for (HibernateOrmIntegrationStaticInitListener listener : integrationStaticInitListeners) { + if (listener == null) { + continue; + } + listener.onMetadataInitialized(fullMeta, metamodelBuilder.getBootstrapContext(), + integrationSettingsBuilder::put); + } Dialect dialect = extractDialect(); PrevalidatedQuarkusMetadata storeableMetadata = trimBootstrapMetadata(fullMeta); diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/QuarkusPersistenceUnitDefinition.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/QuarkusPersistenceUnitDefinition.java index 10c0cb447c03d..4bc4b5f4ee991 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/QuarkusPersistenceUnitDefinition.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/QuarkusPersistenceUnitDefinition.java @@ -11,6 +11,7 @@ import org.hibernate.MultiTenancyStrategy; import org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor; +import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationStaticInitListener; import io.quarkus.runtime.ObjectSubstitution; /** @@ -24,11 +25,12 @@ public final class QuarkusPersistenceUnitDefinition { private final MultiTenancyStrategy multitenancyStrategy; private final boolean isReactive; private final boolean fromPersistenceXml; + private final List integrationStaticInitListeners; private final boolean enversPresent; public QuarkusPersistenceUnitDefinition(PersistenceUnitDescriptor persistenceUnitDescriptor, String dataSource, MultiTenancyStrategy multitenancyStrategy, boolean isReactive, boolean fromPersistenceXml, - boolean enversPresent) { + List integrationStaticInitListeners, boolean enversPresent) { Objects.requireNonNull(persistenceUnitDescriptor); Objects.requireNonNull(multitenancyStrategy); this.actualHibernateDescriptor = LightPersistenceXmlDescriptor.validateAndReadFrom(persistenceUnitDescriptor); @@ -36,6 +38,7 @@ public QuarkusPersistenceUnitDefinition(PersistenceUnitDescriptor persistenceUni this.multitenancyStrategy = multitenancyStrategy; this.isReactive = isReactive; this.fromPersistenceXml = fromPersistenceXml; + this.integrationStaticInitListeners = integrationStaticInitListeners; this.enversPresent = enversPresent; } @@ -47,7 +50,7 @@ private QuarkusPersistenceUnitDefinition(LightPersistenceXmlDescriptor persisten MultiTenancyStrategy multitenancyStrategy, boolean isReactive, boolean fromPersistenceXml, - boolean enversPresent) { + List integrationStaticInitListeners, boolean enversPresent) { Objects.requireNonNull(persistenceUnitDescriptor); Objects.requireNonNull(dataSource); Objects.requireNonNull(multitenancyStrategy); @@ -56,6 +59,7 @@ private QuarkusPersistenceUnitDefinition(LightPersistenceXmlDescriptor persisten this.multitenancyStrategy = multitenancyStrategy; this.isReactive = isReactive; this.fromPersistenceXml = fromPersistenceXml; + this.integrationStaticInitListeners = integrationStaticInitListeners; this.enversPresent = enversPresent; } @@ -84,6 +88,10 @@ public boolean isFromPersistenceXml() { return fromPersistenceXml; } + public List getIntegrationStaticInitListeners() { + return integrationStaticInitListeners; + } + public boolean isEnversPresent() { return enversPresent; } @@ -106,6 +114,7 @@ public static class Serialized { private SharedCacheMode puSharedCachemode; private List puManagedClassNames; private Properties puProperties; + private List integrationStaticInitListeners; private boolean enversIsPresent; //All standard getters and setters generated by IDE: @@ -206,6 +215,15 @@ public void setPuProperties(Properties puProperties) { this.puProperties = puProperties; } + public List getIntegrationStaticInitListeners() { + return integrationStaticInitListeners; + } + + public void setIntegrationStaticInitListeners( + List integrationStaticInitListeners) { + this.integrationStaticInitListeners = integrationStaticInitListeners; + } + public boolean isEnversIsPresent() { return enversIsPresent; } @@ -235,6 +253,7 @@ public Serialized serialize(final QuarkusPersistenceUnitDefinition obj) { s.setReactive(obj.isReactive); s.setFromPersistenceXml(obj.isFromPersistenceXml()); s.setEnversIsPresent(obj.isEnversPresent()); + s.setIntegrationStaticInitListeners(obj.getIntegrationStaticInitListeners()); return s; } @@ -245,7 +264,8 @@ public QuarkusPersistenceUnitDefinition deserialize(Serialized obj) { obj.puValidationMode, obj.puSharedCachemode, obj.puManagedClassNames, obj.puProperties); return new QuarkusPersistenceUnitDefinition(xmlDescriptor, obj.getDataSource(), obj.getMultitenancyStrategy(), - obj.isReactive(), obj.isFromPersistenceXml(), obj.isEnversIsPresent()); + obj.isReactive(), obj.isFromPersistenceXml(), obj.getIntegrationStaticInitListeners(), + obj.isEnversIsPresent()); } } diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/integration/HibernateOrmIntegrationRuntimeInitListener.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/integration/HibernateOrmIntegrationRuntimeInitListener.java new file mode 100644 index 0000000000000..c870dd8885bf8 --- /dev/null +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/integration/HibernateOrmIntegrationRuntimeInitListener.java @@ -0,0 +1,9 @@ +package io.quarkus.hibernate.orm.runtime.integration; + +import java.util.function.BiConsumer; + +public interface HibernateOrmIntegrationRuntimeInitListener { + + void contributeRuntimeProperties(BiConsumer propertyCollector); + +} diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/integration/HibernateOrmIntegrationListener.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/integration/HibernateOrmIntegrationStaticInitListener.java similarity index 75% rename from extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/integration/HibernateOrmIntegrationListener.java rename to extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/integration/HibernateOrmIntegrationStaticInitListener.java index 94ede2a244536..ec63842decc48 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/integration/HibernateOrmIntegrationListener.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/integration/HibernateOrmIntegrationStaticInitListener.java @@ -5,12 +5,11 @@ import org.hibernate.boot.Metadata; import org.hibernate.boot.spi.BootstrapContext; -public interface HibernateOrmIntegrationListener { +public interface HibernateOrmIntegrationStaticInitListener { void contributeBootProperties(BiConsumer propertyCollector); void onMetadataInitialized(Metadata metadata, BootstrapContext bootstrapContext, BiConsumer propertyCollector); - void contributeRuntimeProperties(BiConsumer propertyCollector); } diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/integration/HibernateOrmIntegrations.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/integration/HibernateOrmIntegrations.java deleted file mode 100644 index f7b9c23e7af14..0000000000000 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/integration/HibernateOrmIntegrations.java +++ /dev/null @@ -1,36 +0,0 @@ -package io.quarkus.hibernate.orm.runtime.integration; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.BiConsumer; - -import org.hibernate.boot.Metadata; -import org.hibernate.boot.spi.BootstrapContext; - -public class HibernateOrmIntegrations { - - private static final List LISTENERS = new ArrayList<>(); - - public static void registerListener(HibernateOrmIntegrationListener listener) { - LISTENERS.add(listener); - } - - public static void contributeBootProperties(BiConsumer propertyCollector) { - for (HibernateOrmIntegrationListener listener : LISTENERS) { - listener.contributeBootProperties(propertyCollector); - } - } - - public static void onMetadataInitialized(Metadata metadata, BootstrapContext bootstrapContext, - BiConsumer propertyCollector) { - for (HibernateOrmIntegrationListener listener : LISTENERS) { - listener.onMetadataInitialized(metadata, bootstrapContext, propertyCollector); - } - } - - public static void contributeRuntimeProperties(BiConsumer propertyCollector) { - for (HibernateOrmIntegrationListener listener : LISTENERS) { - listener.contributeRuntimeProperties(propertyCollector); - } - } -} diff --git a/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveProcessor.java b/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveProcessor.java index 1e2e09c7be7c2..66b0c50c08ed0 100644 --- a/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveProcessor.java +++ b/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveProcessor.java @@ -157,18 +157,20 @@ public void buildReactivePersistenceUnit( } @BuildStep - @Record(RUNTIME_INIT) - PersistenceProviderSetUpBuildItem setUpPersistenceProviderAndWaitForVertxPool( - HibernateReactiveRecorder recorder, - List vertxPool, - HibernateOrmRuntimeConfig hibernateOrmRuntimeConfig, + void waitForVertxPool(List vertxPool, BuildProducer runtimeConfigured) { - recorder.initializePersistenceProvider(hibernateOrmRuntimeConfig); - // Define a dependency on VertxPoolBuildItem to ensure that any Pool instances are available // when HibernateORM starts its persistence units - runtimeConfigured.produce(new HibernateOrmIntegrationRuntimeConfiguredBuildItem(HIBERNATE_REACTIVE)); + runtimeConfigured.produce(new HibernateOrmIntegrationRuntimeConfiguredBuildItem(HIBERNATE_REACTIVE, null)); + } + @BuildStep + @Record(RUNTIME_INIT) + PersistenceProviderSetUpBuildItem setUpPersistenceProviderAndWaitForVertxPool(HibernateReactiveRecorder recorder, + HibernateOrmRuntimeConfig hibernateOrmRuntimeConfig, + List integrationBuildItems) { + recorder.initializePersistenceProvider(hibernateOrmRuntimeConfig, + HibernateOrmIntegrationRuntimeConfiguredBuildItem.collectListeners(integrationBuildItems)); return new PersistenceProviderSetUpBuildItem(); } diff --git a/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/FastBootHibernateReactivePersistenceProvider.java b/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/FastBootHibernateReactivePersistenceProvider.java index 11bd8ce76de9e..2184687904969 100644 --- a/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/FastBootHibernateReactivePersistenceProvider.java +++ b/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/FastBootHibernateReactivePersistenceProvider.java @@ -31,7 +31,7 @@ import io.quarkus.hibernate.orm.runtime.PersistenceUnitsHolder; import io.quarkus.hibernate.orm.runtime.RuntimeSettings; import io.quarkus.hibernate.orm.runtime.RuntimeSettings.Builder; -import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrations; +import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationRuntimeInitListener; import io.quarkus.hibernate.orm.runtime.recording.PrevalidatedQuarkusMetadata; import io.quarkus.hibernate.orm.runtime.recording.RecordedState; import io.quarkus.hibernate.reactive.runtime.boot.FastBootReactiveEntityManagerFactoryBuilder; @@ -55,9 +55,12 @@ public final class FastBootHibernateReactivePersistenceProvider implements Persi private volatile FastBootHibernatePersistenceProvider delegate; private final HibernateOrmRuntimeConfig hibernateOrmRuntimeConfig; + private final List integrationRuntimeInitListeners; - public FastBootHibernateReactivePersistenceProvider(HibernateOrmRuntimeConfig hibernateOrmRuntimeConfig) { + public FastBootHibernateReactivePersistenceProvider(HibernateOrmRuntimeConfig hibernateOrmRuntimeConfig, + List integrationRuntimeInitListeners) { this.hibernateOrmRuntimeConfig = hibernateOrmRuntimeConfig; + this.integrationRuntimeInitListeners = integrationRuntimeInitListeners; } @Override @@ -136,7 +139,12 @@ private EntityManagerFactoryBuilder getEntityManagerFactoryBuilderOrNull(String injectRuntimeConfiguration(persistenceUnitName, hibernateOrmRuntimeConfig, runtimeSettingsBuilder); } - HibernateOrmIntegrations.contributeRuntimeProperties((k, v) -> runtimeSettingsBuilder.put(k, v)); + for (HibernateOrmIntegrationRuntimeInitListener listener : integrationRuntimeInitListeners) { + if (listener == null) { + continue; + } + listener.contributeRuntimeProperties(runtimeSettingsBuilder::put); + } RuntimeSettings runtimeSettings = runtimeSettingsBuilder.build(); @@ -281,7 +289,8 @@ private FastBootHibernatePersistenceProvider getJdbcHibernatePersistenceProvider synchronized (this) { localDelegate = this.delegate; if (localDelegate == null) { - this.delegate = localDelegate = new FastBootHibernatePersistenceProvider(hibernateOrmRuntimeConfig); + this.delegate = localDelegate = new FastBootHibernatePersistenceProvider(hibernateOrmRuntimeConfig, + integrationRuntimeInitListeners); } } } diff --git a/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/HibernateReactiveRecorder.java b/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/HibernateReactiveRecorder.java index 0c36773170cc0..33aa769ddcd0a 100644 --- a/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/HibernateReactiveRecorder.java +++ b/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/HibernateReactiveRecorder.java @@ -1,6 +1,9 @@ package io.quarkus.hibernate.reactive.runtime; +import java.util.List; + import io.quarkus.hibernate.orm.runtime.HibernateOrmRuntimeConfig; +import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationRuntimeInitListener; import io.quarkus.runtime.annotations.Recorder; @Recorder @@ -15,8 +18,10 @@ public void callHibernateReactiveFeatureInit(boolean enabled) { HibernateReactive.featureInit(enabled); } - public void initializePersistenceProvider(HibernateOrmRuntimeConfig hibernateOrmRuntimeConfig) { - ReactivePersistenceProviderSetup.registerRuntimePersistenceProvider(hibernateOrmRuntimeConfig); + public void initializePersistenceProvider(HibernateOrmRuntimeConfig hibernateOrmRuntimeConfig, + List integrationRuntimeInitListeners) { + ReactivePersistenceProviderSetup.registerRuntimePersistenceProvider(hibernateOrmRuntimeConfig, + integrationRuntimeInitListeners); } } diff --git a/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/ReactivePersistenceProviderSetup.java b/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/ReactivePersistenceProviderSetup.java index aa8004fd9bd14..496c4b2013bf4 100644 --- a/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/ReactivePersistenceProviderSetup.java +++ b/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/ReactivePersistenceProviderSetup.java @@ -1,7 +1,10 @@ package io.quarkus.hibernate.reactive.runtime; +import java.util.List; + import io.quarkus.hibernate.orm.runtime.HibernateOrmRuntimeConfig; import io.quarkus.hibernate.orm.runtime.SingletonPersistenceProviderResolver; +import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationRuntimeInitListener; public final class ReactivePersistenceProviderSetup { @@ -14,11 +17,13 @@ public static void registerStaticInitPersistenceProvider() { .setPersistenceProviderResolver(new StaticInitHibernateReactivePersistenceProviderResolver()); } - public static void registerRuntimePersistenceProvider(HibernateOrmRuntimeConfig hibernateOrmRuntimeConfig) { + public static void registerRuntimePersistenceProvider(HibernateOrmRuntimeConfig hibernateOrmRuntimeConfig, + List integrationRuntimeInitListeners) { javax.persistence.spi.PersistenceProviderResolverHolder .setPersistenceProviderResolver( new SingletonPersistenceProviderResolver( - new FastBootHibernateReactivePersistenceProvider(hibernateOrmRuntimeConfig))); + new FastBootHibernateReactivePersistenceProvider(hibernateOrmRuntimeConfig, + integrationRuntimeInitListeners))); } } diff --git a/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/HibernateSearchElasticsearchProcessor.java b/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/HibernateSearchElasticsearchProcessor.java index eae21d7c0f3e0..4d80b677f0b7f 100644 --- a/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/HibernateSearchElasticsearchProcessor.java +++ b/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/HibernateSearchElasticsearchProcessor.java @@ -12,6 +12,7 @@ import java.util.Map.Entry; import java.util.Set; +import org.hibernate.search.backend.elasticsearch.ElasticsearchVersion; import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.AnnotationTarget; import org.jboss.jandex.AnnotationTarget.Kind; @@ -38,8 +39,10 @@ import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; import io.quarkus.deployment.configuration.ConfigurationError; import io.quarkus.deployment.logging.LogCleanupFilterBuildItem; -import io.quarkus.hibernate.orm.deployment.integration.HibernateOrmIntegrationBuildItem; +import io.quarkus.deployment.recording.RecorderContext; import io.quarkus.hibernate.orm.deployment.integration.HibernateOrmIntegrationRuntimeConfiguredBuildItem; +import io.quarkus.hibernate.orm.deployment.integration.HibernateOrmIntegrationStaticConfiguredBuildItem; +import io.quarkus.hibernate.search.orm.elasticsearch.runtime.ElasticsearchVersionSubstitution; import io.quarkus.hibernate.search.orm.elasticsearch.runtime.HibernateSearchElasticsearchBuildTimeConfig; import io.quarkus.hibernate.search.orm.elasticsearch.runtime.HibernateSearchElasticsearchBuildTimeConfig.ElasticsearchBackendBuildTimeConfig; import io.quarkus.hibernate.search.orm.elasticsearch.runtime.HibernateSearchElasticsearchRecorder; @@ -59,11 +62,11 @@ void setupLogFilters(BuildProducer filters) { @BuildStep @Record(ExecutionTime.STATIC_INIT) - public void build(HibernateSearchElasticsearchRecorder recorder, + public void build(RecorderContext recorderContext, HibernateSearchElasticsearchRecorder recorder, CombinedIndexBuildItem combinedIndexBuildItem, BuildProducer reflectiveClass, - BuildProducer integrations, - BuildProducer feature) throws Exception { + BuildProducer integrations, + BuildProducer feature) { feature.produce(new FeatureBuildItem(Feature.HIBERNATE_SEARCH_ELASTICSEARCH)); IndexView index = combinedIndexBuildItem.getIndex(); @@ -84,14 +87,16 @@ public void build(HibernateSearchElasticsearchRecorder recorder, checkConfig(buildTimeConfig, defaultBackendIsUsed); + // Make it possible to record the ElasticsearchVersion as bytecode: + recorderContext.registerSubstitution(ElasticsearchVersion.class, + String.class, ElasticsearchVersionSubstitution.class); + // Register the Hibernate Search integration - integrations.produce(new HibernateOrmIntegrationBuildItem(HIBERNATE_SEARCH_ELASTICSEARCH)); + integrations.produce(new HibernateOrmIntegrationStaticConfiguredBuildItem(HIBERNATE_SEARCH_ELASTICSEARCH, + recorder.createStaticInitListener(buildTimeConfig))); // Register the required reflection declarations registerReflection(index, reflectiveClass); - - // Register the Hibernate Search integration listener - recorder.registerHibernateSearchIntegration(buildTimeConfig); } @BuildStep @@ -99,9 +104,8 @@ public void build(HibernateSearchElasticsearchRecorder recorder, void setRuntimeConfig(HibernateSearchElasticsearchRecorder recorder, HibernateSearchElasticsearchRuntimeConfig runtimeConfig, BuildProducer runtimeConfigured) { - recorder.setRuntimeConfig(runtimeConfig); - - runtimeConfigured.produce(new HibernateOrmIntegrationRuntimeConfiguredBuildItem(HIBERNATE_SEARCH_ELASTICSEARCH)); + runtimeConfigured.produce(new HibernateOrmIntegrationRuntimeConfiguredBuildItem(HIBERNATE_SEARCH_ELASTICSEARCH, + recorder.createRuntimeInitListener(runtimeConfig))); } private static void checkConfig(HibernateSearchElasticsearchBuildTimeConfig buildTimeConfig, diff --git a/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/ElasticsearchVersionSubstitution.java b/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/ElasticsearchVersionSubstitution.java new file mode 100644 index 0000000000000..1bd9dfd6e900d --- /dev/null +++ b/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/ElasticsearchVersionSubstitution.java @@ -0,0 +1,17 @@ +package io.quarkus.hibernate.search.orm.elasticsearch.runtime; + +import org.hibernate.search.backend.elasticsearch.ElasticsearchVersion; + +import io.quarkus.runtime.ObjectSubstitution; + +public class ElasticsearchVersionSubstitution implements ObjectSubstitution { + @Override + public String serialize(ElasticsearchVersion obj) { + return obj.toString(); + } + + @Override + public ElasticsearchVersion deserialize(String obj) { + return ElasticsearchVersion.of(obj); + } +} diff --git a/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRecorder.java b/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRecorder.java index ac068c4cb8f86..c2198d672b271 100644 --- a/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRecorder.java +++ b/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRecorder.java @@ -28,8 +28,8 @@ import org.hibernate.search.mapper.orm.session.SearchSession; import io.quarkus.arc.Arc; -import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationListener; -import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrations; +import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationRuntimeInitListener; +import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationStaticInitListener; import io.quarkus.hibernate.search.orm.elasticsearch.runtime.HibernateSearchElasticsearchBuildTimeConfig.ElasticsearchBackendBuildTimeConfig; import io.quarkus.hibernate.search.orm.elasticsearch.runtime.HibernateSearchElasticsearchBuildTimeConfig.ElasticsearchIndexBuildTimeConfig; import io.quarkus.hibernate.search.orm.elasticsearch.runtime.HibernateSearchElasticsearchRuntimeConfig.ElasticsearchBackendRuntimeConfig; @@ -39,14 +39,14 @@ @Recorder public class HibernateSearchElasticsearchRecorder { - private static HibernateSearchElasticsearchRuntimeConfig runtimeConfig; - - public void registerHibernateSearchIntegration(HibernateSearchElasticsearchBuildTimeConfig buildTimeConfig) { - HibernateOrmIntegrations.registerListener(new HibernateSearchIntegrationListener(buildTimeConfig)); + public HibernateOrmIntegrationStaticInitListener createStaticInitListener( + HibernateSearchElasticsearchBuildTimeConfig buildTimeConfig) { + return new HibernateSearchIntegrationStaticInitListener(buildTimeConfig); } - public void setRuntimeConfig(HibernateSearchElasticsearchRuntimeConfig runtimeConfig) { - HibernateSearchElasticsearchRecorder.runtimeConfig = runtimeConfig; + public HibernateOrmIntegrationRuntimeInitListener createRuntimeInitListener( + HibernateSearchElasticsearchRuntimeConfig runtimeConfig) { + return new HibernateSearchIntegrationRuntimeInitListener(runtimeConfig); } public Supplier searchMappingSupplier(String persistenceUnitName, boolean isDefaultPersistenceUnit) { @@ -81,11 +81,12 @@ public SearchSession get() { }; } - private static final class HibernateSearchIntegrationListener implements HibernateOrmIntegrationListener { + private static final class HibernateSearchIntegrationStaticInitListener + implements HibernateOrmIntegrationStaticInitListener { private final HibernateSearchElasticsearchBuildTimeConfig buildTimeConfig; - private HibernateSearchIntegrationListener(HibernateSearchElasticsearchBuildTimeConfig buildTimeConfig) { + private HibernateSearchIntegrationStaticInitListener(HibernateSearchElasticsearchBuildTimeConfig buildTimeConfig) { this.buildTimeConfig = buildTimeConfig; } @@ -113,6 +114,48 @@ public void onMetadataInitialized(Metadata metadata, BootstrapContext bootstrapC booter.preBoot(propertyCollector); } + private void contributeBackendBuildTimeProperties(BiConsumer propertyCollector, String backendName, + ElasticsearchBackendBuildTimeConfig elasticsearchBackendConfig) { + addBackendConfig(propertyCollector, backendName, BackendSettings.TYPE, + ElasticsearchBackendSettings.TYPE_NAME); + addBackendConfig(propertyCollector, backendName, ElasticsearchBackendSettings.VERSION, + elasticsearchBackendConfig.version); + addBackendConfig(propertyCollector, backendName, + ElasticsearchBackendSettings.LAYOUT_STRATEGY, + elasticsearchBackendConfig.layout.strategy, + Optional::isPresent, c -> c.get().getName()); + + // Index defaults at the backend level + contributeBackendIndexBuildTimeProperties(propertyCollector, backendName, null, + elasticsearchBackendConfig.indexDefaults); + + // Per-index properties + for (Entry indexConfigEntry : elasticsearchBackendConfig.indexes + .entrySet()) { + String indexName = indexConfigEntry.getKey(); + ElasticsearchIndexBuildTimeConfig indexConfig = indexConfigEntry.getValue(); + contributeBackendIndexBuildTimeProperties(propertyCollector, backendName, indexName, indexConfig); + } + } + + private void contributeBackendIndexBuildTimeProperties(BiConsumer propertyCollector, + String backendName, String indexName, ElasticsearchIndexBuildTimeConfig indexConfig) { + addBackendIndexConfig(propertyCollector, backendName, indexName, + ElasticsearchIndexSettings.ANALYSIS_CONFIGURER, + indexConfig.analysis.configurer, + Optional::isPresent, c -> c.get().getName()); + } + } + + private static final class HibernateSearchIntegrationRuntimeInitListener + implements HibernateOrmIntegrationRuntimeInitListener { + + private final HibernateSearchElasticsearchRuntimeConfig runtimeConfig; + + private HibernateSearchIntegrationRuntimeInitListener(HibernateSearchElasticsearchRuntimeConfig runtimeConfig) { + this.runtimeConfig = runtimeConfig; + } + @Override public void contributeRuntimeProperties(BiConsumer propertyCollector) { addConfig(propertyCollector, @@ -131,7 +174,8 @@ public void contributeRuntimeProperties(BiConsumer propertyColle HibernateOrmMapperSettings.QUERY_LOADING_FETCH_SIZE, runtimeConfig.queryLoading.fetchSize); - contributeBackendRuntimeProperties(propertyCollector, null, runtimeConfig.defaultBackend); + contributeBackendRuntimeProperties(propertyCollector, null, + runtimeConfig.defaultBackend); for (Entry backendEntry : runtimeConfig.namedBackends.backends .entrySet()) { @@ -139,30 +183,6 @@ public void contributeRuntimeProperties(BiConsumer propertyColle } } - private void contributeBackendBuildTimeProperties(BiConsumer propertyCollector, String backendName, - ElasticsearchBackendBuildTimeConfig elasticsearchBackendConfig) { - addBackendConfig(propertyCollector, backendName, BackendSettings.TYPE, - ElasticsearchBackendSettings.TYPE_NAME); - addBackendConfig(propertyCollector, backendName, ElasticsearchBackendSettings.VERSION, - elasticsearchBackendConfig.version); - addBackendConfig(propertyCollector, backendName, - ElasticsearchBackendSettings.LAYOUT_STRATEGY, - elasticsearchBackendConfig.layout.strategy, - Optional::isPresent, c -> c.get().getName()); - - // Index defaults at the backend level - contributeBackendIndexBuildTimeProperties(propertyCollector, backendName, null, - elasticsearchBackendConfig.indexDefaults); - - // Per-index properties - for (Entry indexConfigEntry : elasticsearchBackendConfig.indexes - .entrySet()) { - String indexName = indexConfigEntry.getKey(); - ElasticsearchIndexBuildTimeConfig indexConfig = indexConfigEntry.getValue(); - contributeBackendIndexBuildTimeProperties(propertyCollector, backendName, indexName, indexConfig); - } - } - private void contributeBackendRuntimeProperties(BiConsumer propertyCollector, String backendName, ElasticsearchBackendRuntimeConfig elasticsearchBackendConfig) { addBackendConfig(propertyCollector, backendName, ElasticsearchBackendSettings.HOSTS, @@ -206,14 +226,6 @@ private void contributeBackendRuntimeProperties(BiConsumer prope } } - private void contributeBackendIndexBuildTimeProperties(BiConsumer propertyCollector, - String backendName, String indexName, ElasticsearchIndexBuildTimeConfig indexConfig) { - addBackendIndexConfig(propertyCollector, backendName, indexName, - ElasticsearchIndexSettings.ANALYSIS_CONFIGURER, - indexConfig.analysis.configurer, - Optional::isPresent, c -> c.get().getName()); - } - private void contributeBackendIndexRuntimeProperties(BiConsumer propertyCollector, String backendName, String indexName, ElasticsearchIndexRuntimeConfig indexConfig) { addBackendIndexConfig(propertyCollector, backendName, indexName, From 36392b8ca59b908179526fb50893acf5e75e4c3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Mon, 7 Dec 2020 18:21:51 +0100 Subject: [PATCH 44/62] Separate HibernateOrmIntegration build items per persistence unit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yoann Rodière --- .../deployment/HibernateEnversProcessor.java | 10 ++++++--- .../orm/deployment/HibernateOrmProcessor.java | 6 ++++-- ...IntegrationRuntimeConfiguredBuildItem.java | 20 ++++++++++++++---- ...mIntegrationStaticConfiguredBuildItem.java | 21 +++++++++++++++---- .../FastBootHibernatePersistenceProvider.java | 7 ++++--- .../orm/runtime/HibernateOrmRecorder.java | 3 ++- .../orm/runtime/PersistenceProviderSetup.java | 3 ++- .../HibernateReactiveProcessor.java | 10 ++++++--- ...tHibernateReactivePersistenceProvider.java | 7 ++++--- .../runtime/HibernateReactiveRecorder.java | 3 ++- .../ReactivePersistenceProviderSetup.java | 3 ++- ...HibernateSearchElasticsearchProcessor.java | 17 +++++++++++---- 12 files changed, 80 insertions(+), 30 deletions(-) diff --git a/extensions/hibernate-envers/deployment/src/main/java/io/quarkus/hibernate/envers/deployment/HibernateEnversProcessor.java b/extensions/hibernate-envers/deployment/src/main/java/io/quarkus/hibernate/envers/deployment/HibernateEnversProcessor.java index 73b5cafd696d4..58c1ef05b5571 100644 --- a/extensions/hibernate-envers/deployment/src/main/java/io/quarkus/hibernate/envers/deployment/HibernateEnversProcessor.java +++ b/extensions/hibernate-envers/deployment/src/main/java/io/quarkus/hibernate/envers/deployment/HibernateEnversProcessor.java @@ -16,6 +16,7 @@ import io.quarkus.hibernate.envers.HibernateEnversBuildTimeConfig; import io.quarkus.hibernate.envers.HibernateEnversRecorder; import io.quarkus.hibernate.orm.deployment.AdditionalJpaModelBuildItem; +import io.quarkus.hibernate.orm.deployment.PersistenceUnitDescriptorBuildItem; import io.quarkus.hibernate.orm.deployment.integration.HibernateOrmIntegrationStaticConfiguredBuildItem; public final class HibernateEnversProcessor { @@ -53,10 +54,13 @@ public void registerEnversReflections(BuildProducer re @BuildStep @Record(ExecutionTime.STATIC_INIT) public void applyConfig(HibernateEnversRecorder recorder, HibernateEnversBuildTimeConfig buildTimeConfig, + List persistenceUnitDescriptorBuildItems, BuildProducer integrationProducer) { - integrationProducer.produce( - new HibernateOrmIntegrationStaticConfiguredBuildItem(HIBERNATE_ENVERS, - recorder.createStaticInitListener(buildTimeConfig))); + for (PersistenceUnitDescriptorBuildItem puDescriptor : persistenceUnitDescriptorBuildItems) { + integrationProducer.produce( + new HibernateOrmIntegrationStaticConfiguredBuildItem(HIBERNATE_ENVERS, + puDescriptor.getPersistenceUnitName(), recorder.createStaticInitListener(buildTimeConfig))); + } } @BuildStep diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java index 519671fd710f5..2da2ba32f73c4 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java @@ -419,12 +419,14 @@ public void build(RecorderContext recorderContext, HibernateOrmRecorder recorder integratorClasses.add((Class) recorderContext.classProxy(integratorClassName)); } - List integrationStaticInitListeners = HibernateOrmIntegrationStaticConfiguredBuildItem + Map> integrationStaticInitListeners = HibernateOrmIntegrationStaticConfiguredBuildItem .collectListeners(integrationBuildItems); List finalStagePUDescriptors = new ArrayList<>(); for (PersistenceUnitDescriptorBuildItem pud : persistenceUnitDescriptorBuildItems) { - finalStagePUDescriptors.add(pud.asOutputPersistenceUnitDefinition(integrationStaticInitListeners)); + finalStagePUDescriptors.add( + pud.asOutputPersistenceUnitDefinition(integrationStaticInitListeners + .getOrDefault(pud.getPersistenceUnitName(), Collections.emptyList()))); } //Make it possible to record the QuarkusPersistenceUnitDefinition as bytecode: diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/integration/HibernateOrmIntegrationRuntimeConfiguredBuildItem.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/integration/HibernateOrmIntegrationRuntimeConfiguredBuildItem.java index 2c363837c0782..f052331736329 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/integration/HibernateOrmIntegrationRuntimeConfiguredBuildItem.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/integration/HibernateOrmIntegrationRuntimeConfiguredBuildItem.java @@ -1,7 +1,9 @@ package io.quarkus.hibernate.orm.deployment.integration; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import io.quarkus.builder.item.MultiBuildItem; import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationRuntimeInitListener; @@ -9,14 +11,19 @@ public final class HibernateOrmIntegrationRuntimeConfiguredBuildItem extends MultiBuildItem { private final String name; + private final String persistenceUnitName; private final HibernateOrmIntegrationRuntimeInitListener listener; public HibernateOrmIntegrationRuntimeConfiguredBuildItem(String name, - HibernateOrmIntegrationRuntimeInitListener listener) { + String persistenceUnitName, HibernateOrmIntegrationRuntimeInitListener listener) { if (name == null) { throw new IllegalArgumentException("name cannot be null"); } this.name = name; + if (persistenceUnitName == null) { + throw new IllegalArgumentException("persistenceUnitName cannot be null"); + } + this.persistenceUnitName = persistenceUnitName; this.listener = listener; } @@ -24,6 +31,10 @@ public String getName() { return name; } + public String getPersistenceUnitName() { + return persistenceUnitName; + } + public HibernateOrmIntegrationRuntimeInitListener getListener() { return listener; } @@ -35,11 +46,12 @@ public String toString() { .toString(); } - public static List collectListeners( + public static Map> collectListeners( List items) { - List listeners = new ArrayList<>(); + Map> listeners = new HashMap<>(); for (HibernateOrmIntegrationRuntimeConfiguredBuildItem item : items) { - listeners.add(item.getListener()); + listeners.computeIfAbsent(item.getPersistenceUnitName(), ignored -> new ArrayList<>()) + .add(item.getListener()); } return listeners; } diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/integration/HibernateOrmIntegrationStaticConfiguredBuildItem.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/integration/HibernateOrmIntegrationStaticConfiguredBuildItem.java index c4cb07d1d7c54..880083e88c23b 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/integration/HibernateOrmIntegrationStaticConfiguredBuildItem.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/integration/HibernateOrmIntegrationStaticConfiguredBuildItem.java @@ -1,7 +1,9 @@ package io.quarkus.hibernate.orm.deployment.integration; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import io.quarkus.builder.item.MultiBuildItem; import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationStaticInitListener; @@ -9,13 +11,19 @@ public final class HibernateOrmIntegrationStaticConfiguredBuildItem extends MultiBuildItem { private final String name; + private final String persistenceUnitName; private final HibernateOrmIntegrationStaticInitListener listener; - public HibernateOrmIntegrationStaticConfiguredBuildItem(String name, HibernateOrmIntegrationStaticInitListener listener) { + public HibernateOrmIntegrationStaticConfiguredBuildItem(String name, String persistenceUnitName, + HibernateOrmIntegrationStaticInitListener listener) { if (name == null) { throw new IllegalArgumentException("name cannot be null"); } this.name = name; + if (persistenceUnitName == null) { + throw new IllegalArgumentException("persistenceUnitName cannot be null"); + } + this.persistenceUnitName = persistenceUnitName; this.listener = listener; } @@ -23,6 +31,10 @@ public String getName() { return name; } + public String getPersistenceUnitName() { + return persistenceUnitName; + } + public HibernateOrmIntegrationStaticInitListener getListener() { return listener; } @@ -34,11 +46,12 @@ public String toString() { .toString(); } - public static List collectListeners( + public static Map> collectListeners( List items) { - List listeners = new ArrayList<>(); + Map> listeners = new HashMap<>(); for (HibernateOrmIntegrationStaticConfiguredBuildItem item : items) { - listeners.add(item.getListener()); + listeners.computeIfAbsent(item.getPersistenceUnitName(), ignored -> new ArrayList<>()) + .add(item.getListener()); } return listeners; } diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/FastBootHibernatePersistenceProvider.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/FastBootHibernatePersistenceProvider.java index 9e7ff745c350e..16dcda026f031 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/FastBootHibernatePersistenceProvider.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/FastBootHibernatePersistenceProvider.java @@ -42,10 +42,10 @@ public final class FastBootHibernatePersistenceProvider implements PersistencePr private final ProviderUtil providerUtil = new ProviderUtil(); private final HibernateOrmRuntimeConfig hibernateOrmRuntimeConfig; - private final List integrationRuntimeInitListeners; + private final Map> integrationRuntimeInitListeners; public FastBootHibernatePersistenceProvider(HibernateOrmRuntimeConfig hibernateOrmRuntimeConfig, - List integrationRuntimeInitListeners) { + Map> integrationRuntimeInitListeners) { this.hibernateOrmRuntimeConfig = hibernateOrmRuntimeConfig; this.integrationRuntimeInitListeners = integrationRuntimeInitListeners; } @@ -176,7 +176,8 @@ private EntityManagerFactoryBuilder getEntityManagerFactoryBuilderOrNull(String injectRuntimeConfiguration(persistenceUnitName, hibernateOrmRuntimeConfig, runtimeSettingsBuilder); } - for (HibernateOrmIntegrationRuntimeInitListener listener : integrationRuntimeInitListeners) { + for (HibernateOrmIntegrationRuntimeInitListener listener : integrationRuntimeInitListeners + .getOrDefault(persistenceUnitName, Collections.emptyList())) { if (listener == null) { continue; } diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRecorder.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRecorder.java index 65bed40166b56..421ea9487ab45 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRecorder.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRecorder.java @@ -3,6 +3,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.function.Supplier; @@ -46,7 +47,7 @@ public void callHibernateFeatureInit(boolean enabled) { } public void setupPersistenceProvider(HibernateOrmRuntimeConfig hibernateOrmRuntimeConfig, - List integrationRuntimeInitListeners) { + Map> integrationRuntimeInitListeners) { PersistenceProviderSetup.registerRuntimePersistenceProvider(hibernateOrmRuntimeConfig, integrationRuntimeInitListeners); } diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/PersistenceProviderSetup.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/PersistenceProviderSetup.java index c4816707b74ed..c3718f92661d1 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/PersistenceProviderSetup.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/PersistenceProviderSetup.java @@ -1,6 +1,7 @@ package io.quarkus.hibernate.orm.runtime; import java.util.List; +import java.util.Map; import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationRuntimeInitListener; @@ -16,7 +17,7 @@ public static void registerStaticInitPersistenceProvider() { } public static void registerRuntimePersistenceProvider(HibernateOrmRuntimeConfig hibernateOrmRuntimeConfig, - List integrationRuntimeInitListeners) { + Map> integrationRuntimeInitListeners) { javax.persistence.spi.PersistenceProviderResolverHolder.setPersistenceProviderResolver( new SingletonPersistenceProviderResolver( new FastBootHibernatePersistenceProvider(hibernateOrmRuntimeConfig, integrationRuntimeInitListeners))); diff --git a/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveProcessor.java b/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveProcessor.java index 66b0c50c08ed0..ad74617d6e757 100644 --- a/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveProcessor.java +++ b/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveProcessor.java @@ -158,10 +158,14 @@ public void buildReactivePersistenceUnit( @BuildStep void waitForVertxPool(List vertxPool, + List persistenceUnitDescriptorBuildItems, BuildProducer runtimeConfigured) { - // Define a dependency on VertxPoolBuildItem to ensure that any Pool instances are available - // when HibernateORM starts its persistence units - runtimeConfigured.produce(new HibernateOrmIntegrationRuntimeConfiguredBuildItem(HIBERNATE_REACTIVE, null)); + for (PersistenceUnitDescriptorBuildItem puDescriptor : persistenceUnitDescriptorBuildItems) { + // Define a dependency on VertxPoolBuildItem to ensure that any Pool instances are available + // when HibernateORM starts its persistence units + runtimeConfigured.produce(new HibernateOrmIntegrationRuntimeConfiguredBuildItem(HIBERNATE_REACTIVE, + puDescriptor.getPersistenceUnitName(), null)); + } } @BuildStep diff --git a/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/FastBootHibernateReactivePersistenceProvider.java b/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/FastBootHibernateReactivePersistenceProvider.java index 2184687904969..02c6e5233549d 100644 --- a/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/FastBootHibernateReactivePersistenceProvider.java +++ b/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/FastBootHibernateReactivePersistenceProvider.java @@ -55,10 +55,10 @@ public final class FastBootHibernateReactivePersistenceProvider implements Persi private volatile FastBootHibernatePersistenceProvider delegate; private final HibernateOrmRuntimeConfig hibernateOrmRuntimeConfig; - private final List integrationRuntimeInitListeners; + private final Map> integrationRuntimeInitListeners; public FastBootHibernateReactivePersistenceProvider(HibernateOrmRuntimeConfig hibernateOrmRuntimeConfig, - List integrationRuntimeInitListeners) { + Map> integrationRuntimeInitListeners) { this.hibernateOrmRuntimeConfig = hibernateOrmRuntimeConfig; this.integrationRuntimeInitListeners = integrationRuntimeInitListeners; } @@ -139,7 +139,8 @@ private EntityManagerFactoryBuilder getEntityManagerFactoryBuilderOrNull(String injectRuntimeConfiguration(persistenceUnitName, hibernateOrmRuntimeConfig, runtimeSettingsBuilder); } - for (HibernateOrmIntegrationRuntimeInitListener listener : integrationRuntimeInitListeners) { + for (HibernateOrmIntegrationRuntimeInitListener listener : integrationRuntimeInitListeners + .getOrDefault(persistenceUnitName, Collections.emptyList())) { if (listener == null) { continue; } diff --git a/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/HibernateReactiveRecorder.java b/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/HibernateReactiveRecorder.java index 33aa769ddcd0a..075ce0b7f07a0 100644 --- a/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/HibernateReactiveRecorder.java +++ b/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/HibernateReactiveRecorder.java @@ -1,6 +1,7 @@ package io.quarkus.hibernate.reactive.runtime; import java.util.List; +import java.util.Map; import io.quarkus.hibernate.orm.runtime.HibernateOrmRuntimeConfig; import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationRuntimeInitListener; @@ -19,7 +20,7 @@ public void callHibernateReactiveFeatureInit(boolean enabled) { } public void initializePersistenceProvider(HibernateOrmRuntimeConfig hibernateOrmRuntimeConfig, - List integrationRuntimeInitListeners) { + Map> integrationRuntimeInitListeners) { ReactivePersistenceProviderSetup.registerRuntimePersistenceProvider(hibernateOrmRuntimeConfig, integrationRuntimeInitListeners); } diff --git a/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/ReactivePersistenceProviderSetup.java b/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/ReactivePersistenceProviderSetup.java index 496c4b2013bf4..728e44b4a02b6 100644 --- a/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/ReactivePersistenceProviderSetup.java +++ b/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/ReactivePersistenceProviderSetup.java @@ -1,6 +1,7 @@ package io.quarkus.hibernate.reactive.runtime; import java.util.List; +import java.util.Map; import io.quarkus.hibernate.orm.runtime.HibernateOrmRuntimeConfig; import io.quarkus.hibernate.orm.runtime.SingletonPersistenceProviderResolver; @@ -18,7 +19,7 @@ public static void registerStaticInitPersistenceProvider() { } public static void registerRuntimePersistenceProvider(HibernateOrmRuntimeConfig hibernateOrmRuntimeConfig, - List integrationRuntimeInitListeners) { + Map> integrationRuntimeInitListeners) { javax.persistence.spi.PersistenceProviderResolverHolder .setPersistenceProviderResolver( new SingletonPersistenceProviderResolver( diff --git a/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/HibernateSearchElasticsearchProcessor.java b/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/HibernateSearchElasticsearchProcessor.java index 4d80b677f0b7f..8b198451bae3d 100644 --- a/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/HibernateSearchElasticsearchProcessor.java +++ b/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/HibernateSearchElasticsearchProcessor.java @@ -40,6 +40,7 @@ import io.quarkus.deployment.configuration.ConfigurationError; import io.quarkus.deployment.logging.LogCleanupFilterBuildItem; import io.quarkus.deployment.recording.RecorderContext; +import io.quarkus.hibernate.orm.deployment.PersistenceUnitDescriptorBuildItem; import io.quarkus.hibernate.orm.deployment.integration.HibernateOrmIntegrationRuntimeConfiguredBuildItem; import io.quarkus.hibernate.orm.deployment.integration.HibernateOrmIntegrationStaticConfiguredBuildItem; import io.quarkus.hibernate.search.orm.elasticsearch.runtime.ElasticsearchVersionSubstitution; @@ -64,6 +65,7 @@ void setupLogFilters(BuildProducer filters) { @Record(ExecutionTime.STATIC_INIT) public void build(RecorderContext recorderContext, HibernateSearchElasticsearchRecorder recorder, CombinedIndexBuildItem combinedIndexBuildItem, + List persistenceUnitDescriptorBuildItems, BuildProducer reflectiveClass, BuildProducer integrations, BuildProducer feature) { @@ -92,8 +94,11 @@ public void build(RecorderContext recorderContext, HibernateSearchElasticsearchR String.class, ElasticsearchVersionSubstitution.class); // Register the Hibernate Search integration - integrations.produce(new HibernateOrmIntegrationStaticConfiguredBuildItem(HIBERNATE_SEARCH_ELASTICSEARCH, - recorder.createStaticInitListener(buildTimeConfig))); + for (PersistenceUnitDescriptorBuildItem puDescriptor : persistenceUnitDescriptorBuildItems) { + // TODO per-PU configuration + integrations.produce(new HibernateOrmIntegrationStaticConfiguredBuildItem(HIBERNATE_SEARCH_ELASTICSEARCH, + puDescriptor.getPersistenceUnitName(), recorder.createStaticInitListener(buildTimeConfig))); + } // Register the required reflection declarations registerReflection(index, reflectiveClass); @@ -103,9 +108,13 @@ public void build(RecorderContext recorderContext, HibernateSearchElasticsearchR @Record(ExecutionTime.RUNTIME_INIT) void setRuntimeConfig(HibernateSearchElasticsearchRecorder recorder, HibernateSearchElasticsearchRuntimeConfig runtimeConfig, + List persistenceUnitDescriptorBuildItems, BuildProducer runtimeConfigured) { - runtimeConfigured.produce(new HibernateOrmIntegrationRuntimeConfiguredBuildItem(HIBERNATE_SEARCH_ELASTICSEARCH, - recorder.createRuntimeInitListener(runtimeConfig))); + for (PersistenceUnitDescriptorBuildItem puDescriptor : persistenceUnitDescriptorBuildItems) { + // TODO per-PU configuration + runtimeConfigured.produce(new HibernateOrmIntegrationRuntimeConfiguredBuildItem(HIBERNATE_SEARCH_ELASTICSEARCH, + puDescriptor.getPersistenceUnitName(), recorder.createRuntimeInitListener(runtimeConfig))); + } } private static void checkConfig(HibernateSearchElasticsearchBuildTimeConfig buildTimeConfig, From 9b2386ae34707ea0803e6f35304695478ea789e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Mon, 7 Dec 2020 18:48:24 +0100 Subject: [PATCH 45/62] Per-PU configuration of Hibernate Search MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yoann Rodière --- ...ernateSearchElasticsearchCdiProcessor.java | 9 +- ...rchPersistenceUnitConfiguredBuildItem.java | 24 + ...HibernateSearchElasticsearchProcessor.java | 127 ++++-- .../test/configuration/IndexedEntity.java | 19 +- .../NoConfigIndexedEntityTest.java | 3 +- .../NoConfigNoIndexedEntityTest.java | 3 +- ...tion-multiple-persistence-units.properties | 22 +- .../application-nohsearchconfig.properties | 5 + ...ateSearchElasticsearchBuildTimeConfig.java | 101 +---- ...csearchBuildTimeConfigPersistenceUnit.java | 113 +++++ .../HibernateSearchElasticsearchRecorder.java | 32 +- ...rnateSearchElasticsearchRuntimeConfig.java | 414 +---------------- ...ticsearchRuntimeConfigPersistenceUnit.java | 426 ++++++++++++++++++ 13 files changed, 732 insertions(+), 566 deletions(-) create mode 100644 extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/HibernateSearchElasticsearchPersistenceUnitConfiguredBuildItem.java create mode 100644 extensions/hibernate-search-orm-elasticsearch/deployment/src/test/resources/application-nohsearchconfig.properties create mode 100644 extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchBuildTimeConfigPersistenceUnit.java create mode 100644 extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRuntimeConfigPersistenceUnit.java diff --git a/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/HibernateSearchElasticsearchCdiProcessor.java b/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/HibernateSearchElasticsearchCdiProcessor.java index 56ca658e0a2ad..ac67fc7ea0e44 100644 --- a/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/HibernateSearchElasticsearchCdiProcessor.java +++ b/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/HibernateSearchElasticsearchCdiProcessor.java @@ -9,7 +9,6 @@ import org.hibernate.search.mapper.orm.mapping.SearchMapping; import org.hibernate.search.mapper.orm.session.SearchSession; -import io.quarkus.agroal.spi.JdbcDataSourceBuildItem; import io.quarkus.arc.deployment.SyntheticBeanBuildItem; import io.quarkus.arc.processor.DotNames; import io.quarkus.deployment.annotations.BuildProducer; @@ -17,7 +16,6 @@ import io.quarkus.deployment.annotations.ExecutionTime; import io.quarkus.deployment.annotations.Record; import io.quarkus.hibernate.orm.PersistenceUnit; -import io.quarkus.hibernate.orm.deployment.PersistenceUnitDescriptorBuildItem; import io.quarkus.hibernate.orm.runtime.PersistenceUnitUtil; import io.quarkus.hibernate.search.orm.elasticsearch.runtime.HibernateSearchElasticsearchRecorder; @@ -26,11 +24,10 @@ public class HibernateSearchElasticsearchCdiProcessor { @Record(ExecutionTime.STATIC_INIT) @BuildStep void generateSearchBeans(HibernateSearchElasticsearchRecorder recorder, - List persistenceUnitDescriptors, - List jdbcDataSources, // just make sure the datasources are initialized + List configuredPersistenceUnits, BuildProducer syntheticBeanBuildItemBuildProducer) { - for (PersistenceUnitDescriptorBuildItem persistenceUnitDescriptor : persistenceUnitDescriptors) { - String persistenceUnitName = persistenceUnitDescriptor.getPersistenceUnitName(); + for (HibernateSearchElasticsearchPersistenceUnitConfiguredBuildItem persistenceUnit : configuredPersistenceUnits) { + String persistenceUnitName = persistenceUnit.getPersistenceUnitName(); boolean isDefaultPersistenceUnit = PersistenceUnitUtil.isDefaultPersistenceUnit(persistenceUnitName); syntheticBeanBuildItemBuildProducer diff --git a/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/HibernateSearchElasticsearchPersistenceUnitConfiguredBuildItem.java b/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/HibernateSearchElasticsearchPersistenceUnitConfiguredBuildItem.java new file mode 100644 index 0000000000000..e2328942be340 --- /dev/null +++ b/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/HibernateSearchElasticsearchPersistenceUnitConfiguredBuildItem.java @@ -0,0 +1,24 @@ +package io.quarkus.hibernate.search.orm.elasticsearch; + +import io.quarkus.builder.item.MultiBuildItem; + +public final class HibernateSearchElasticsearchPersistenceUnitConfiguredBuildItem extends MultiBuildItem { + + private final String persistenceUnitName; + + public HibernateSearchElasticsearchPersistenceUnitConfiguredBuildItem(String persistenceUnitName) { + if (persistenceUnitName == null) { + throw new IllegalArgumentException("persistenceUnitName cannot be null"); + } + this.persistenceUnitName = persistenceUnitName; + } + + public String getPersistenceUnitName() { + return persistenceUnitName; + } + + @Override + public String toString() { + return getClass().getSimpleName() + " [" + persistenceUnitName + "]"; + } +} diff --git a/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/HibernateSearchElasticsearchProcessor.java b/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/HibernateSearchElasticsearchProcessor.java index 8b198451bae3d..583eddd0b5884 100644 --- a/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/HibernateSearchElasticsearchProcessor.java +++ b/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/HibernateSearchElasticsearchProcessor.java @@ -7,8 +7,10 @@ import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Map.Entry; import java.util.Set; @@ -43,9 +45,11 @@ import io.quarkus.hibernate.orm.deployment.PersistenceUnitDescriptorBuildItem; import io.quarkus.hibernate.orm.deployment.integration.HibernateOrmIntegrationRuntimeConfiguredBuildItem; import io.quarkus.hibernate.orm.deployment.integration.HibernateOrmIntegrationStaticConfiguredBuildItem; +import io.quarkus.hibernate.orm.runtime.PersistenceUnitUtil; import io.quarkus.hibernate.search.orm.elasticsearch.runtime.ElasticsearchVersionSubstitution; import io.quarkus.hibernate.search.orm.elasticsearch.runtime.HibernateSearchElasticsearchBuildTimeConfig; -import io.quarkus.hibernate.search.orm.elasticsearch.runtime.HibernateSearchElasticsearchBuildTimeConfig.ElasticsearchBackendBuildTimeConfig; +import io.quarkus.hibernate.search.orm.elasticsearch.runtime.HibernateSearchElasticsearchBuildTimeConfigPersistenceUnit; +import io.quarkus.hibernate.search.orm.elasticsearch.runtime.HibernateSearchElasticsearchBuildTimeConfigPersistenceUnit.ElasticsearchBackendBuildTimeConfig; import io.quarkus.hibernate.search.orm.elasticsearch.runtime.HibernateSearchElasticsearchRecorder; import io.quarkus.hibernate.search.orm.elasticsearch.runtime.HibernateSearchElasticsearchRuntimeConfig; @@ -67,41 +71,69 @@ public void build(RecorderContext recorderContext, HibernateSearchElasticsearchR CombinedIndexBuildItem combinedIndexBuildItem, List persistenceUnitDescriptorBuildItems, BuildProducer reflectiveClass, + BuildProducer configuredPersistenceUnits, BuildProducer integrations, BuildProducer feature) { feature.produce(new FeatureBuildItem(Feature.HIBERNATE_SEARCH_ELASTICSEARCH)); IndexView index = combinedIndexBuildItem.getIndex(); - Collection indexedAnnotations = index.getAnnotations(INDEXED); - if (indexedAnnotations.isEmpty()) { + + // Make it possible to record the ElasticsearchVersion as bytecode: + recorderContext.registerSubstitution(ElasticsearchVersion.class, + String.class, ElasticsearchVersionSubstitution.class); + + for (PersistenceUnitDescriptorBuildItem puDescriptor : persistenceUnitDescriptorBuildItems) { + Collection indexedAnnotationsForPU = new ArrayList<>(); + for (AnnotationInstance indexedAnnotation : indexedAnnotations) { + String targetName = indexedAnnotation.target().asClass().name().toString(); + if (puDescriptor.getManagedClassNames().contains(targetName)) { + indexedAnnotationsForPU.add(indexedAnnotation); + } + } + buildForPersistenceUnit(recorder, indexedAnnotationsForPU, puDescriptor.getPersistenceUnitName(), reflectiveClass, + configuredPersistenceUnits, integrations); + } + + registerReflectionForClasses(index, reflectiveClass); + } + + private void buildForPersistenceUnit(HibernateSearchElasticsearchRecorder recorder, + Collection indexedAnnotationsForPU, String persistenceUnitName, + BuildProducer reflectiveClass, + BuildProducer configuredPersistenceUnits, + BuildProducer integrations) { + if (indexedAnnotationsForPU.isEmpty()) { // we don't have any indexed entity, we can bail out return; } + HibernateSearchElasticsearchBuildTimeConfigPersistenceUnit puConfig = PersistenceUnitUtil + .isDefaultPersistenceUnit(persistenceUnitName) + ? buildTimeConfig.defaultPersistenceUnit + : buildTimeConfig.persistenceUnits.get(persistenceUnitName); + boolean defaultBackendIsUsed = false; - for (AnnotationInstance indexedAnnotation : indexedAnnotations) { + for (AnnotationInstance indexedAnnotation : indexedAnnotationsForPU) { if (indexedAnnotation.value("backend") == null) { defaultBackendIsUsed = true; break; } } - checkConfig(buildTimeConfig, defaultBackendIsUsed); + checkConfig(persistenceUnitName, puConfig, defaultBackendIsUsed); - // Make it possible to record the ElasticsearchVersion as bytecode: - recorderContext.registerSubstitution(ElasticsearchVersion.class, - String.class, ElasticsearchVersionSubstitution.class); + configuredPersistenceUnits + .produce(new HibernateSearchElasticsearchPersistenceUnitConfiguredBuildItem(persistenceUnitName)); - // Register the Hibernate Search integration - for (PersistenceUnitDescriptorBuildItem puDescriptor : persistenceUnitDescriptorBuildItems) { - // TODO per-PU configuration - integrations.produce(new HibernateOrmIntegrationStaticConfiguredBuildItem(HIBERNATE_SEARCH_ELASTICSEARCH, - puDescriptor.getPersistenceUnitName(), recorder.createStaticInitListener(buildTimeConfig))); + if (puConfig == null) { + return; } - // Register the required reflection declarations - registerReflection(index, reflectiveClass); + integrations.produce(new HibernateOrmIntegrationStaticConfiguredBuildItem(HIBERNATE_SEARCH_ELASTICSEARCH, + persistenceUnitName, recorder.createStaticInitListener(puConfig))); + + registerReflectionForConfig(puConfig, reflectiveClass); } @BuildStep @@ -111,45 +143,60 @@ void setRuntimeConfig(HibernateSearchElasticsearchRecorder recorder, List persistenceUnitDescriptorBuildItems, BuildProducer runtimeConfigured) { for (PersistenceUnitDescriptorBuildItem puDescriptor : persistenceUnitDescriptorBuildItems) { - // TODO per-PU configuration runtimeConfigured.produce(new HibernateOrmIntegrationRuntimeConfiguredBuildItem(HIBERNATE_SEARCH_ELASTICSEARCH, - puDescriptor.getPersistenceUnitName(), recorder.createRuntimeInitListener(runtimeConfig))); + puDescriptor.getPersistenceUnitName(), + recorder.createRuntimeInitListener(runtimeConfig, puDescriptor.getPersistenceUnitName()))); } } - private static void checkConfig(HibernateSearchElasticsearchBuildTimeConfig buildTimeConfig, - boolean defaultBackendIsUsed) { + private static void checkConfig(String persistenceUnitName, + HibernateSearchElasticsearchBuildTimeConfigPersistenceUnit buildTimeConfig, boolean defaultBackendIsUsed) { + List propertyKeysWithNoVersion = new ArrayList<>(); if (defaultBackendIsUsed) { // we validate that the version is present for the default backend - if (!buildTimeConfig.defaultBackend.version.isPresent()) { - throw new ConfigurationError( - "The Elasticsearch version needs to be defined via the quarkus.hibernate-search-orm.elasticsearch.version property."); + if (buildTimeConfig == null || !buildTimeConfig.defaultBackend.version.isPresent()) { + propertyKeysWithNoVersion.add(elasticsearchVersionPropertyKey(persistenceUnitName, null)); } } // we validate that the version is present for all the named backends - List namedBackendsWithNoVersion = new ArrayList<>(); - for (Entry additionalBackendEntry : buildTimeConfig.namedBackends.backends - .entrySet()) { + Map backends = buildTimeConfig != null + ? buildTimeConfig.namedBackends.backends + : Collections.emptyMap(); + for (Entry additionalBackendEntry : backends.entrySet()) { if (!additionalBackendEntry.getValue().version.isPresent()) { - namedBackendsWithNoVersion.add(additionalBackendEntry.getKey()); + propertyKeysWithNoVersion + .add(elasticsearchVersionPropertyKey(persistenceUnitName, additionalBackendEntry.getKey())); } } - if (!namedBackendsWithNoVersion.isEmpty()) { - throw new ConfigurationError("The Elasticsearch version property needs to be defined for backends " - + String.join(", ", namedBackendsWithNoVersion)); + if (!propertyKeysWithNoVersion.isEmpty()) { + throw new ConfigurationError( + "The Elasticsearch version needs to be defined via properties: " + + String.join(", ", propertyKeysWithNoVersion) + "."); } } - private void registerReflection(IndexView index, BuildProducer reflectiveClass) { - Set reflectiveClassCollector = new HashSet<>(); + private static String elasticsearchVersionPropertyKey(String persistenceUnitName, String backendName) { + StringBuilder keyBuilder = new StringBuilder("quarkus.hibernate-search-orm."); + if (!PersistenceUnitUtil.isDefaultPersistenceUnit(persistenceUnitName)) { + keyBuilder.append(persistenceUnitName).append("."); + } + keyBuilder.append("elasticsearch."); + if (backendName != null) { + keyBuilder.append(backendName).append("."); + } + keyBuilder.append("version"); + return keyBuilder.toString(); + } - if (buildTimeConfig.defaultBackend.indexDefaults.analysis.configurer.isPresent()) { + private void registerReflectionForConfig(HibernateSearchElasticsearchBuildTimeConfigPersistenceUnit puConfig, + BuildProducer reflectiveClass) { + if (puConfig.defaultBackend.indexDefaults.analysis.configurer.isPresent()) { reflectiveClass.produce( new ReflectiveClassBuildItem(true, false, - buildTimeConfig.defaultBackend.indexDefaults.analysis.configurer.get())); + puConfig.defaultBackend.indexDefaults.analysis.configurer.get())); } - for (HibernateSearchElasticsearchBuildTimeConfig.ElasticsearchIndexBuildTimeConfig indexConfig : buildTimeConfig.defaultBackend.indexes + for (HibernateSearchElasticsearchBuildTimeConfigPersistenceUnit.ElasticsearchIndexBuildTimeConfig indexConfig : puConfig.defaultBackend.indexes .values()) { if (indexConfig.analysis.configurer.isPresent()) { reflectiveClass.produce( @@ -157,15 +204,19 @@ private void registerReflection(IndexView index, BuildProducer reflectiveClass) { + Set reflectiveClassCollector = new HashSet<>(); for (AnnotationInstance propertyMappingMetaAnnotationInstance : index .getAnnotations(PROPERTY_MAPPING_META_ANNOTATION)) { diff --git a/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/java/io/quarkus/hibernate/search/elasticsearch/test/configuration/IndexedEntity.java b/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/java/io/quarkus/hibernate/search/elasticsearch/test/configuration/IndexedEntity.java index 66b2b8b0cb188..af2b7832378d4 100644 --- a/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/java/io/quarkus/hibernate/search/elasticsearch/test/configuration/IndexedEntity.java +++ b/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/java/io/quarkus/hibernate/search/elasticsearch/test/configuration/IndexedEntity.java @@ -1,14 +1,27 @@ package io.quarkus.hibernate.search.elasticsearch.test.configuration; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + import org.hibernate.search.mapper.pojo.mapping.definition.annotation.Indexed; /** * An indexed entity. - *

- * Technically this particular class is not a valid entity as it is not marked with @Entity. - * However, that's not relevant for our tests, and it's easier this way. */ +@Entity @Indexed public class IndexedEntity { + @Id + @GeneratedValue + private Long id; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } } diff --git a/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/java/io/quarkus/hibernate/search/elasticsearch/test/configuration/NoConfigIndexedEntityTest.java b/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/java/io/quarkus/hibernate/search/elasticsearch/test/configuration/NoConfigIndexedEntityTest.java index ce971cac24eef..63ec0d03394a6 100644 --- a/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/java/io/quarkus/hibernate/search/elasticsearch/test/configuration/NoConfigIndexedEntityTest.java +++ b/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/java/io/quarkus/hibernate/search/elasticsearch/test/configuration/NoConfigIndexedEntityTest.java @@ -14,7 +14,8 @@ public class NoConfigIndexedEntityTest { @RegisterExtension static final QuarkusUnitTest config = new QuarkusUnitTest().setArchiveProducer( - () -> ShrinkWrap.create(JavaArchive.class).addClass(IndexedEntity.class)) + () -> ShrinkWrap.create(JavaArchive.class).addClass(IndexedEntity.class) + .addAsResource("application-nohsearchconfig.properties", "application.properties")) .setExpectedException(ConfigurationError.class); @Test diff --git a/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/java/io/quarkus/hibernate/search/elasticsearch/test/configuration/NoConfigNoIndexedEntityTest.java b/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/java/io/quarkus/hibernate/search/elasticsearch/test/configuration/NoConfigNoIndexedEntityTest.java index 4037dbe41a9c0..9aea0ebb75788 100644 --- a/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/java/io/quarkus/hibernate/search/elasticsearch/test/configuration/NoConfigNoIndexedEntityTest.java +++ b/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/java/io/quarkus/hibernate/search/elasticsearch/test/configuration/NoConfigNoIndexedEntityTest.java @@ -13,7 +13,8 @@ public class NoConfigNoIndexedEntityTest { @RegisterExtension static final QuarkusUnitTest config = new QuarkusUnitTest().setArchiveProducer( - () -> ShrinkWrap.create(JavaArchive.class)); + () -> ShrinkWrap.create(JavaArchive.class) + .addAsResource("application-nohsearchconfig.properties", "application.properties")); @Test public void testNoConfig() throws SQLException { diff --git a/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/resources/application-multiple-persistence-units.properties b/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/resources/application-multiple-persistence-units.properties index a0020e52fb1b7..a4a7d65292055 100644 --- a/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/resources/application-multiple-persistence-units.properties +++ b/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/resources/application-multiple-persistence-units.properties @@ -10,16 +10,28 @@ quarkus.datasource.data2.jdbc.url=jdbc:h2:mem:data2;DB_CLOSE_DELAY=-1 quarkus.hibernate-orm.dialect=org.hibernate.dialect.H2Dialect quarkus.hibernate-orm.database.generation=drop-and-create +quarkus.hibernate-search-orm.elasticsearch.version=7 +quarkus.hibernate-search-orm.elasticsearch.hosts=${elasticsearch.hosts:localhost:9200} +quarkus.hibernate-search-orm.elasticsearch.protocol=${elasticsearch.protocol:http} +quarkus.hibernate-search-orm.schema-management.strategy=drop-and-create-and-drop +quarkus.hibernate-search-orm.automatic-indexing.synchronization.strategy=sync + quarkus.hibernate-orm."pu1".dialect=org.hibernate.dialect.H2Dialect quarkus.hibernate-orm."pu1".database.generation=drop-and-create quarkus.hibernate-orm."pu1".datasource=data1 +quarkus.hibernate-search-orm."pu1".elasticsearch.version=7 +quarkus.hibernate-search-orm."pu1".elasticsearch.hosts=${elasticsearch.hosts:localhost:9200} +quarkus.hibernate-search-orm."pu1".elasticsearch.protocol=${elasticsearch.protocol:http} +quarkus.hibernate-search-orm."pu1".schema-management.strategy=drop-and-create-and-drop +quarkus.hibernate-search-orm."pu1".automatic-indexing.synchronization.strategy=sync + quarkus.hibernate-orm."pu2".dialect=org.hibernate.dialect.H2Dialect quarkus.hibernate-orm."pu2".database.generation=drop-and-create quarkus.hibernate-orm."pu2".datasource=data2 -quarkus.hibernate-search-orm.elasticsearch.version=7 -quarkus.hibernate-search-orm.elasticsearch.hosts=${elasticsearch.hosts:localhost:9200} -quarkus.hibernate-search-orm.elasticsearch.protocol=${elasticsearch.protocol:http} -quarkus.hibernate-search-orm.schema-management.strategy=drop-and-create-and-drop -quarkus.hibernate-search-orm.automatic-indexing.synchronization.strategy=sync +quarkus.hibernate-search-orm."pu2".elasticsearch.version=7 +quarkus.hibernate-search-orm."pu2".elasticsearch.hosts=${elasticsearch.hosts:localhost:9200} +quarkus.hibernate-search-orm."pu2".elasticsearch.protocol=${elasticsearch.protocol:http} +quarkus.hibernate-search-orm."pu2".schema-management.strategy=drop-and-create-and-drop +quarkus.hibernate-search-orm."pu2".automatic-indexing.synchronization.strategy=sync diff --git a/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/resources/application-nohsearchconfig.properties b/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/resources/application-nohsearchconfig.properties new file mode 100644 index 0000000000000..0cb2f82a307a0 --- /dev/null +++ b/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/resources/application-nohsearchconfig.properties @@ -0,0 +1,5 @@ +quarkus.datasource.db-kind=h2 +quarkus.datasource.jdbc.url=jdbc:h2:mem:default;DB_CLOSE_DELAY=-1 + +quarkus.hibernate-orm.dialect=org.hibernate.dialect.H2Dialect +quarkus.hibernate-orm.database.generation=drop-and-create diff --git a/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchBuildTimeConfig.java b/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchBuildTimeConfig.java index 8fa27397d077d..f412d7ed0f5a4 100644 --- a/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchBuildTimeConfig.java +++ b/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchBuildTimeConfig.java @@ -1,13 +1,9 @@ package io.quarkus.hibernate.search.orm.elasticsearch.runtime; import java.util.Map; -import java.util.Optional; - -import org.hibernate.search.backend.elasticsearch.ElasticsearchVersion; import io.quarkus.runtime.annotations.ConfigDocMapKey; import io.quarkus.runtime.annotations.ConfigDocSection; -import io.quarkus.runtime.annotations.ConfigGroup; import io.quarkus.runtime.annotations.ConfigItem; import io.quarkus.runtime.annotations.ConfigPhase; import io.quarkus.runtime.annotations.ConfigRoot; @@ -16,100 +12,17 @@ public class HibernateSearchElasticsearchBuildTimeConfig { /** - * Default backend + * Configuration for the default persistence unit. */ - @ConfigItem(name = "elasticsearch") - @ConfigDocSection - public ElasticsearchBackendBuildTimeConfig defaultBackend; + @ConfigItem(name = ConfigItem.PARENT) + public HibernateSearchElasticsearchBuildTimeConfigPersistenceUnit defaultPersistenceUnit; /** - * Named backends + * Configuration for additional named persistence units. */ - @ConfigItem(name = "elasticsearch") @ConfigDocSection - public ElasticsearchNamedBackendsBuildTimeConfig namedBackends; - - /** - * The class or the name of the bean that should be notified of any failure occurring in a background process - * (mainly index operations). - *

- * Must implement {@link org.hibernate.search.engine.reporting.FailureHandler}. - */ - @ConfigItem - public Optional> backgroundFailureHandler; - - @ConfigGroup - public static class ElasticsearchNamedBackendsBuildTimeConfig { - - /** - * Named backends - */ - @ConfigDocMapKey("backend-name") - public Map backends; - - } - - @ConfigGroup - public static class ElasticsearchBackendBuildTimeConfig { - /** - * The version of Elasticsearch used in the cluster. - *

- * As the schema is generated without a connection to the server, this item is mandatory. - *

- * It doesn't have to be the exact version (it can be 7 or 7.1 for instance) but it has to be sufficiently precise to - * choose a model dialect (the one used to generate the schema) compatible with the protocol dialect (the one used to - * communicate with Elasticsearch). - *

- * There's no rule of thumb here as it depends on the schema incompatibilities introduced by Elasticsearch versions. In - * any case, if there is a problem, you will have an error when Hibernate Search tries to connect to the cluster. - */ - @ConfigItem - public Optional version; - - /** - * Configuration for the index layout. - */ - @ConfigItem - public LayoutConfig layout; - - /** - * The default configuration for the Elasticsearch indexes. - */ - @ConfigItem(name = ConfigItem.PARENT) - public ElasticsearchIndexBuildTimeConfig indexDefaults; - - /** - * Per-index specific configuration. - */ - @ConfigItem - @ConfigDocMapKey("index-name") - public Map indexes; - } - - @ConfigGroup - public static class ElasticsearchIndexBuildTimeConfig { - /** - * Configuration for full-text analysis. - */ - @ConfigItem - public AnalysisConfig analysis; - } - - @ConfigGroup - public static class AnalysisConfig { - /** - * The class or the name of the bean used to configure full text analysis (e.g. analyzers, normalizers). - */ - @ConfigItem - public Optional> configurer; - } + @ConfigDocMapKey("persistence-unit-name") + @ConfigItem(name = ConfigItem.PARENT) + public Map persistenceUnits; - @ConfigGroup - public static class LayoutConfig { - /** - * The class or the name of the bean used to configure layout (e.g. index names, index aliases). - */ - @ConfigItem - public Optional> strategy; - } } diff --git a/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchBuildTimeConfigPersistenceUnit.java b/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchBuildTimeConfigPersistenceUnit.java new file mode 100644 index 0000000000000..3d6fc1ef92364 --- /dev/null +++ b/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchBuildTimeConfigPersistenceUnit.java @@ -0,0 +1,113 @@ +package io.quarkus.hibernate.search.orm.elasticsearch.runtime; + +import java.util.Map; +import java.util.Optional; + +import org.hibernate.search.backend.elasticsearch.ElasticsearchVersion; + +import io.quarkus.runtime.annotations.ConfigDocMapKey; +import io.quarkus.runtime.annotations.ConfigDocSection; +import io.quarkus.runtime.annotations.ConfigGroup; +import io.quarkus.runtime.annotations.ConfigItem; + +@ConfigGroup +public class HibernateSearchElasticsearchBuildTimeConfigPersistenceUnit { + + /** + * Default backend + */ + @ConfigItem(name = "elasticsearch") + @ConfigDocSection + public ElasticsearchBackendBuildTimeConfig defaultBackend; + + /** + * Named backends + */ + @ConfigItem(name = "elasticsearch") + @ConfigDocSection + public ElasticsearchNamedBackendsBuildTimeConfig namedBackends; + + /** + * The class or the name of the bean that should be notified of any failure occurring in a background process + * (mainly index operations). + *

+ * Must implement {@link org.hibernate.search.engine.reporting.FailureHandler}. + */ + @ConfigItem + public Optional> backgroundFailureHandler; + + @ConfigGroup + public static class ElasticsearchNamedBackendsBuildTimeConfig { + + /** + * Named backends + */ + @ConfigDocMapKey("backend-name") + public Map backends; + + } + + @ConfigGroup + public static class ElasticsearchBackendBuildTimeConfig { + /** + * The version of Elasticsearch used in the cluster. + *

+ * As the schema is generated without a connection to the server, this item is mandatory. + *

+ * It doesn't have to be the exact version (it can be 7 or 7.1 for instance) but it has to be sufficiently precise to + * choose a model dialect (the one used to generate the schema) compatible with the protocol dialect (the one used to + * communicate with Elasticsearch). + *

+ * There's no rule of thumb here as it depends on the schema incompatibilities introduced by Elasticsearch versions. In + * any case, if there is a problem, you will have an error when Hibernate Search tries to connect to the cluster. + */ + @ConfigItem + public Optional version; + + /** + * Configuration for the index layout. + */ + @ConfigItem + public LayoutConfig layout; + + /** + * The default configuration for the Elasticsearch indexes. + */ + @ConfigItem(name = ConfigItem.PARENT) + public ElasticsearchIndexBuildTimeConfig indexDefaults; + + /** + * Per-index specific configuration. + */ + @ConfigItem + @ConfigDocMapKey("index-name") + public Map indexes; + } + + @ConfigGroup + public static class ElasticsearchIndexBuildTimeConfig { + /** + * Configuration for full-text analysis. + */ + @ConfigItem + public AnalysisConfig analysis; + } + + @ConfigGroup + public static class AnalysisConfig { + /** + * The class or the name of the bean used to configure full text analysis (e.g. analyzers, normalizers). + */ + @ConfigItem + public Optional> configurer; + } + + @ConfigGroup + public static class LayoutConfig { + /** + * The class or the name of the bean used to configure layout (e.g. index names, index aliases). + */ + @ConfigItem + public Optional> strategy; + } +} diff --git a/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRecorder.java b/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRecorder.java index c2198d672b271..b8c4e75571ffd 100644 --- a/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRecorder.java +++ b/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRecorder.java @@ -28,25 +28,33 @@ import org.hibernate.search.mapper.orm.session.SearchSession; import io.quarkus.arc.Arc; +import io.quarkus.hibernate.orm.runtime.PersistenceUnitUtil; import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationRuntimeInitListener; import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationStaticInitListener; -import io.quarkus.hibernate.search.orm.elasticsearch.runtime.HibernateSearchElasticsearchBuildTimeConfig.ElasticsearchBackendBuildTimeConfig; -import io.quarkus.hibernate.search.orm.elasticsearch.runtime.HibernateSearchElasticsearchBuildTimeConfig.ElasticsearchIndexBuildTimeConfig; -import io.quarkus.hibernate.search.orm.elasticsearch.runtime.HibernateSearchElasticsearchRuntimeConfig.ElasticsearchBackendRuntimeConfig; -import io.quarkus.hibernate.search.orm.elasticsearch.runtime.HibernateSearchElasticsearchRuntimeConfig.ElasticsearchIndexRuntimeConfig; +import io.quarkus.hibernate.search.orm.elasticsearch.runtime.HibernateSearchElasticsearchBuildTimeConfigPersistenceUnit.ElasticsearchBackendBuildTimeConfig; +import io.quarkus.hibernate.search.orm.elasticsearch.runtime.HibernateSearchElasticsearchBuildTimeConfigPersistenceUnit.ElasticsearchIndexBuildTimeConfig; +import io.quarkus.hibernate.search.orm.elasticsearch.runtime.HibernateSearchElasticsearchRuntimeConfigPersistenceUnit.ElasticsearchBackendRuntimeConfig; +import io.quarkus.hibernate.search.orm.elasticsearch.runtime.HibernateSearchElasticsearchRuntimeConfigPersistenceUnit.ElasticsearchIndexRuntimeConfig; import io.quarkus.runtime.annotations.Recorder; @Recorder public class HibernateSearchElasticsearchRecorder { public HibernateOrmIntegrationStaticInitListener createStaticInitListener( - HibernateSearchElasticsearchBuildTimeConfig buildTimeConfig) { + HibernateSearchElasticsearchBuildTimeConfigPersistenceUnit buildTimeConfig) { return new HibernateSearchIntegrationStaticInitListener(buildTimeConfig); } public HibernateOrmIntegrationRuntimeInitListener createRuntimeInitListener( - HibernateSearchElasticsearchRuntimeConfig runtimeConfig) { - return new HibernateSearchIntegrationRuntimeInitListener(runtimeConfig); + HibernateSearchElasticsearchRuntimeConfig runtimeConfig, String persistenceUnitName) { + HibernateSearchElasticsearchRuntimeConfigPersistenceUnit puConfig = PersistenceUnitUtil + .isDefaultPersistenceUnit(persistenceUnitName) + ? runtimeConfig.defaultPersistenceUnit + : runtimeConfig.persistenceUnits.get(persistenceUnitName); + if (puConfig == null) { + return null; + } + return new HibernateSearchIntegrationRuntimeInitListener(puConfig); } public Supplier searchMappingSupplier(String persistenceUnitName, boolean isDefaultPersistenceUnit) { @@ -84,9 +92,10 @@ public SearchSession get() { private static final class HibernateSearchIntegrationStaticInitListener implements HibernateOrmIntegrationStaticInitListener { - private final HibernateSearchElasticsearchBuildTimeConfig buildTimeConfig; + private final HibernateSearchElasticsearchBuildTimeConfigPersistenceUnit buildTimeConfig; - private HibernateSearchIntegrationStaticInitListener(HibernateSearchElasticsearchBuildTimeConfig buildTimeConfig) { + private HibernateSearchIntegrationStaticInitListener( + HibernateSearchElasticsearchBuildTimeConfigPersistenceUnit buildTimeConfig) { this.buildTimeConfig = buildTimeConfig; } @@ -150,9 +159,10 @@ private void contributeBackendIndexBuildTimeProperties(BiConsumer backends; - - } - - @ConfigGroup - public static class ElasticsearchBackendRuntimeConfig { - /** - * The list of hosts of the Elasticsearch servers. - */ - @ConfigItem(defaultValue = "localhost:9200") - List hosts; - - /** - * The protocol to use when contacting Elasticsearch servers. - * Set to "https" to enable SSL/TLS. - */ - @ConfigItem(defaultValue = "http") - ElasticsearchClientProtocol protocol; - - /** - * The username used for authentication. - */ - @ConfigItem - Optional username; - - /** - * The password used for authentication. - */ - @ConfigItem - Optional password; - - /** - * The timeout when establishing a connection to an Elasticsearch server. - */ - @ConfigItem(defaultValue = "1S") - Duration connectionTimeout; - - /** - * The timeout when reading responses from an Elasticsearch server. - */ - @ConfigItem(defaultValue = "30S") - Duration readTimeout; - - /** - * The timeout when executing a request to an Elasticsearch server. - *

- * This includes the time needed to wait for a connection to be available, - * send the request and read the response. - */ - @ConfigItem - Optional requestTimeout; - - /** - * The maximum number of connections to all the Elasticsearch servers. - */ - @ConfigItem(defaultValue = "20") - int maxConnections; - - /** - * The maximum number of connections per Elasticsearch server. - */ - @ConfigItem(defaultValue = "10") - int maxConnectionsPerRoute; - - /** - * Configuration for the automatic discovery of new Elasticsearch nodes. - */ - @ConfigItem - DiscoveryConfig discovery; - - /** - * Configuration for the thread pool assigned to the backend. - */ - @ConfigItem - ThreadPoolConfig threadPool; - - /** - * The default configuration for the Elasticsearch indexes. - */ - @ConfigItem(name = ConfigItem.PARENT) - ElasticsearchIndexRuntimeConfig indexDefaults; - - /** - * Per-index specific configuration. - */ - @ConfigItem - @ConfigDocMapKey("index-name") - Map indexes; - } - - public enum ElasticsearchClientProtocol { - /** - * Use clear-text HTTP, with SSL/TLS disabled. - */ - HTTP("http"), - /** - * Use HTTPS, with SSL/TLS enabled. - */ - HTTPS("https"); - - public static ElasticsearchClientProtocol of(String value) { - return StringHelper.parseDiscreteValues( - values(), - ElasticsearchClientProtocol::getHibernateSearchString, - (invalidValue, validValues) -> new SearchException( - String.format( - Locale.ROOT, - "Invalid protocol: '%1$s'. Valid protocols are: %2$s.", - invalidValue, - validValues)), - value); - } - - private final String hibernateSearchString; - - ElasticsearchClientProtocol(String hibernateSearchString) { - this.hibernateSearchString = hibernateSearchString; - } - - public String getHibernateSearchString() { - return hibernateSearchString; - } - } - - @ConfigGroup - public static class ElasticsearchIndexRuntimeConfig { - /** - * Configuration for the schema management of the indexes. - */ - @ConfigItem - ElasticsearchIndexSchemaManagementConfig schemaManagement; - - /** - * Configuration for the indexing process that creates, updates and deletes documents. - */ - @ConfigItem - ElasticsearchIndexIndexingConfig indexing; - } - - @ConfigGroup - public static class DiscoveryConfig { - - /** - * Defines if automatic discovery is enabled. - */ - @ConfigItem - boolean enabled; - - /** - * Refresh interval of the node list. - */ - @ConfigItem(defaultValue = "10S") - Duration refreshInterval; - - } - - @ConfigGroup - public static class AutomaticIndexingConfig { - - /** - * Configuration for synchronization with the index when indexing automatically. - */ - @ConfigItem - AutomaticIndexingSynchronizationConfig synchronization; - - /** - * Whether to check if dirty properties are relevant to indexing before actually reindexing an entity. - *

- * When enabled, re-indexing of an entity is skipped if the only changes are on properties that are not used when - * indexing. - */ - @ConfigItem(defaultValue = "true") - boolean enableDirtyCheck; - } - - @ConfigGroup - public static class AutomaticIndexingSynchronizationConfig { - - // @formatter:off - /** - * The synchronization strategy to use when indexing automatically. - * - * Defines how complete indexing should be before resuming the application thread - * after a database transaction is committed. - * - * Available values: - * - * [cols=5] - * !=== - * .2+h!Strategy - * .2+h!Throughput - * 3+^h!Guarantees when the application thread resumes - * - * h!Changes applied - * h!Changes safe from crash/power loss - * h!Changes visible on search - * - * !async - * !Best - * ^!icon:times[role=red] - * ^!icon:times[role=red] - * ^!icon:times[role=red] - * - * !write-sync (**default**) - * !Medium - * ^!icon:check[role=lime] - * ^!icon:check[role=lime] - * ^!icon:times[role=red] - * - * !read-sync - * !Medium to worst - * ^!icon:check[role=lime] - * ^!icon:times[role=red] - * ^!icon:check[role=lime] - * - * !sync - * !Worst - * ^!icon:check[role=lime] - * ^!icon:check[role=lime] - * ^!icon:check[role=lime] - * !=== - * - * See - * https://docs.jboss.org/hibernate/search/6.0/reference/en-US/html_single/#mapper-orm-indexing-automatic-synchronization[this - * section of the reference documentation] - * for more information. - * - * @asciidoclet - */ - // @formatter:on - @ConfigItem(defaultValue = AutomaticIndexingSynchronizationStrategyNames.WRITE_SYNC) - String strategy; - } - - @ConfigGroup - public static class SearchQueryLoadingConfig { - - /** - * Configuration for cache lookup when loading entities during the execution of a search query. - */ - @ConfigItem - SearchQueryLoadingCacheLookupConfig cacheLookup; - - /** - * The fetch size to use when loading entities during the execution of a search query. - */ - @ConfigItem(defaultValue = "100") - int fetchSize; - } - - @ConfigGroup - public static class SearchQueryLoadingCacheLookupConfig { - - /** - * The strategy to use when loading entities during the execution of a search query. - */ - @ConfigItem(defaultValue = "skip") - EntityLoadingCacheLookupStrategy strategy; - } - - @ConfigGroup - public static class SchemaManagementConfig { - - /** - * The strategy used for index lifecycle. - */ - // We can't set an actual default value here: see comment on this class. - @ConfigItem(defaultValue = "create-or-validate") - SchemaManagementStrategyName strategy; - - } - - @ConfigGroup - public static class ThreadPoolConfig { - /** - * The size of the thread pool assigned to the backend. - *

- * Note that number is per backend, not per index. - * Adding more indexes will not add more threads. - *

- * As all operations happening in this thread-pool are non-blocking, - * raising its size above the number of processor cores available to the JVM will not bring noticeable performance - * benefit. - * The only reason to alter this setting would be to reduce the number of threads; - * for example, in an application with a single index with a single indexing queue, - * running on a machine with 64 processor cores, - * you might want to bring down the number of threads. - *

- * Defaults to the number of processor cores available to the JVM on startup. - */ - // We can't set an actual default value here: see comment on this class. - @ConfigItem - OptionalInt size; - } - - // We can't set actual default values in this section, - // otherwise "quarkus.hibernate-search-orm.elasticsearch.index-defaults" will be ignored. - @ConfigGroup - public static class ElasticsearchIndexSchemaManagementConfig { - /** - * The minimal cluster status required. - */ - // We can't set an actual default value here: see comment on this class. - @ConfigItem(defaultValueDocumentation = "yellow") - Optional requiredStatus; - - /** - * How long we should wait for the status before failing the bootstrap. - */ - // We can't set an actual default value here: see comment on this class. - @ConfigItem(defaultValueDocumentation = "10S") - Optional requiredStatusWaitTimeout; - } - - // We can't set actual default values in this section, - // otherwise "quarkus.hibernate-search-orm.elasticsearch.index-defaults" will be ignored. - @ConfigGroup - public static class ElasticsearchIndexIndexingConfig { - /** - * The number of indexing queues assigned to each index. - *

- * Higher values will lead to more connections being used in parallel, - * which may lead to higher indexing throughput, - * but incurs a risk of overloading Elasticsearch, - * i.e. of overflowing its HTTP request buffers and tripping - * circuit breakers, - * leading to Elasticsearch giving up on some request and resulting in indexing failures. - */ - // We can't set an actual default value here: see comment on this class. - @ConfigItem(defaultValueDocumentation = "10") - OptionalInt queueCount; - - /** - * The size of indexing queues. - *

- * Lower values may lead to lower memory usage, especially if there are many queues, - * but values that are too low will reduce the likeliness of reaching the max bulk size - * and increase the likeliness of application threads blocking because the queue is full, - * which may lead to lower indexing throughput. - */ - // We can't set an actual default value here: see comment on this class. - @ConfigItem(defaultValueDocumentation = "1000") - OptionalInt queueSize; + @ConfigDocMapKey("persistence-unit-name") + @ConfigItem(name = ConfigItem.PARENT) + public Map persistenceUnits; - /** - * The maximum size of bulk requests created when processing indexing queues. - *

- * Higher values will lead to more documents being sent in each HTTP request sent to Elasticsearch, - * which may lead to higher indexing throughput, - * but incurs a risk of overloading Elasticsearch, - * i.e. of overflowing its HTTP request buffers and tripping - * circuit breakers, - * leading to Elasticsearch giving up on some request and resulting in indexing failures. - *

- * Note that raising this number above the queue size has no effect, - * as bulks cannot include more requests than are contained in the queue. - */ - // We can't set an actual default value here: see comment on this class. - @ConfigItem(defaultValueDocumentation = "100") - OptionalInt maxBulkSize; - } } diff --git a/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRuntimeConfigPersistenceUnit.java b/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRuntimeConfigPersistenceUnit.java new file mode 100644 index 0000000000000..adb103bcdd301 --- /dev/null +++ b/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRuntimeConfigPersistenceUnit.java @@ -0,0 +1,426 @@ +package io.quarkus.hibernate.search.orm.elasticsearch.runtime; + +import java.time.Duration; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; +import java.util.OptionalInt; + +import org.hibernate.search.backend.elasticsearch.index.IndexStatus; +import org.hibernate.search.mapper.orm.automaticindexing.session.AutomaticIndexingSynchronizationStrategyNames; +import org.hibernate.search.mapper.orm.schema.management.SchemaManagementStrategyName; +import org.hibernate.search.mapper.orm.search.loading.EntityLoadingCacheLookupStrategy; +import org.hibernate.search.util.common.SearchException; +import org.hibernate.search.util.common.impl.StringHelper; + +import io.quarkus.runtime.annotations.ConfigDocMapKey; +import io.quarkus.runtime.annotations.ConfigDocSection; +import io.quarkus.runtime.annotations.ConfigGroup; +import io.quarkus.runtime.annotations.ConfigItem; + +@ConfigGroup +public class HibernateSearchElasticsearchRuntimeConfigPersistenceUnit { + + /** + * Default backend + */ + @ConfigItem(name = "elasticsearch") + @ConfigDocSection + ElasticsearchBackendRuntimeConfig defaultBackend; + + /** + * Named backends + */ + @ConfigItem(name = "elasticsearch") + @ConfigDocSection + public ElasticsearchNamedBackendsRuntimeConfig namedBackends; + + /** + * Configuration for automatic creation and validation of the Elasticsearch schema: + * indexes, their mapping, their settings. + */ + @ConfigItem + SchemaManagementConfig schemaManagement; + + /** + * Configuration for how entities are loaded by a search query. + */ + @ConfigItem(name = "query.loading") + SearchQueryLoadingConfig queryLoading; + + /** + * Configuration for the automatic indexing. + */ + @ConfigItem + AutomaticIndexingConfig automaticIndexing; + + @ConfigGroup + public static class ElasticsearchNamedBackendsRuntimeConfig { + + /** + * Named backends + */ + @ConfigDocMapKey("backend-name") + public Map backends; + + } + + @ConfigGroup + public static class ElasticsearchBackendRuntimeConfig { + /** + * The list of hosts of the Elasticsearch servers. + */ + @ConfigItem(defaultValue = "localhost:9200") + List hosts; + + /** + * The protocol to use when contacting Elasticsearch servers. + * Set to "https" to enable SSL/TLS. + */ + @ConfigItem(defaultValue = "http") + ElasticsearchClientProtocol protocol; + + /** + * The username used for authentication. + */ + @ConfigItem + Optional username; + + /** + * The password used for authentication. + */ + @ConfigItem + Optional password; + + /** + * The timeout when establishing a connection to an Elasticsearch server. + */ + @ConfigItem(defaultValue = "1S") + Duration connectionTimeout; + + /** + * The timeout when reading responses from an Elasticsearch server. + */ + @ConfigItem(defaultValue = "30S") + Duration readTimeout; + + /** + * The timeout when executing a request to an Elasticsearch server. + *

+ * This includes the time needed to wait for a connection to be available, + * send the request and read the response. + */ + @ConfigItem + Optional requestTimeout; + + /** + * The maximum number of connections to all the Elasticsearch servers. + */ + @ConfigItem(defaultValue = "20") + int maxConnections; + + /** + * The maximum number of connections per Elasticsearch server. + */ + @ConfigItem(defaultValue = "10") + int maxConnectionsPerRoute; + + /** + * Configuration for the automatic discovery of new Elasticsearch nodes. + */ + @ConfigItem + DiscoveryConfig discovery; + + /** + * Configuration for the thread pool assigned to the backend. + */ + @ConfigItem + ThreadPoolConfig threadPool; + + /** + * The default configuration for the Elasticsearch indexes. + */ + @ConfigItem(name = ConfigItem.PARENT) + ElasticsearchIndexRuntimeConfig indexDefaults; + + /** + * Per-index specific configuration. + */ + @ConfigItem + @ConfigDocMapKey("index-name") + Map indexes; + } + + public enum ElasticsearchClientProtocol { + /** + * Use clear-text HTTP, with SSL/TLS disabled. + */ + HTTP("http"), + /** + * Use HTTPS, with SSL/TLS enabled. + */ + HTTPS("https"); + + public static ElasticsearchClientProtocol of(String value) { + return StringHelper.parseDiscreteValues( + values(), + ElasticsearchClientProtocol::getHibernateSearchString, + (invalidValue, validValues) -> new SearchException( + String.format( + Locale.ROOT, + "Invalid protocol: '%1$s'. Valid protocols are: %2$s.", + invalidValue, + validValues)), + value); + } + + private final String hibernateSearchString; + + ElasticsearchClientProtocol(String hibernateSearchString) { + this.hibernateSearchString = hibernateSearchString; + } + + public String getHibernateSearchString() { + return hibernateSearchString; + } + } + + @ConfigGroup + public static class ElasticsearchIndexRuntimeConfig { + /** + * Configuration for the schema management of the indexes. + */ + @ConfigItem + ElasticsearchIndexSchemaManagementConfig schemaManagement; + + /** + * Configuration for the indexing process that creates, updates and deletes documents. + */ + @ConfigItem + ElasticsearchIndexIndexingConfig indexing; + } + + @ConfigGroup + public static class DiscoveryConfig { + + /** + * Defines if automatic discovery is enabled. + */ + @ConfigItem + boolean enabled; + + /** + * Refresh interval of the node list. + */ + @ConfigItem(defaultValue = "10S") + Duration refreshInterval; + + } + + @ConfigGroup + public static class AutomaticIndexingConfig { + + /** + * Configuration for synchronization with the index when indexing automatically. + */ + @ConfigItem + AutomaticIndexingSynchronizationConfig synchronization; + + /** + * Whether to check if dirty properties are relevant to indexing before actually reindexing an entity. + *

+ * When enabled, re-indexing of an entity is skipped if the only changes are on properties that are not used when + * indexing. + */ + @ConfigItem(defaultValue = "true") + boolean enableDirtyCheck; + } + + @ConfigGroup + public static class AutomaticIndexingSynchronizationConfig { + + // @formatter:off + /** + * The synchronization strategy to use when indexing automatically. + * + * Defines how complete indexing should be before resuming the application thread + * after a database transaction is committed. + * + * Available values: + * + * [cols=5] + * !=== + * .2+h!Strategy + * .2+h!Throughput + * 3+^h!Guarantees when the application thread resumes + * + * h!Changes applied + * h!Changes safe from crash/power loss + * h!Changes visible on search + * + * !async + * !Best + * ^!icon:times[role=red] + * ^!icon:times[role=red] + * ^!icon:times[role=red] + * + * !write-sync (**default**) + * !Medium + * ^!icon:check[role=lime] + * ^!icon:check[role=lime] + * ^!icon:times[role=red] + * + * !read-sync + * !Medium to worst + * ^!icon:check[role=lime] + * ^!icon:times[role=red] + * ^!icon:check[role=lime] + * + * !sync + * !Worst + * ^!icon:check[role=lime] + * ^!icon:check[role=lime] + * ^!icon:check[role=lime] + * !=== + * + * See + * https://docs.jboss.org/hibernate/search/6.0/reference/en-US/html_single/#mapper-orm-indexing-automatic-synchronization[this + * section of the reference documentation] + * for more information. + * + * @asciidoclet + */ + // @formatter:on + @ConfigItem(defaultValue = AutomaticIndexingSynchronizationStrategyNames.WRITE_SYNC) + String strategy; + } + + @ConfigGroup + public static class SearchQueryLoadingConfig { + + /** + * Configuration for cache lookup when loading entities during the execution of a search query. + */ + @ConfigItem + SearchQueryLoadingCacheLookupConfig cacheLookup; + + /** + * The fetch size to use when loading entities during the execution of a search query. + */ + @ConfigItem(defaultValue = "100") + int fetchSize; + } + + @ConfigGroup + public static class SearchQueryLoadingCacheLookupConfig { + + /** + * The strategy to use when loading entities during the execution of a search query. + */ + @ConfigItem(defaultValue = "skip") + EntityLoadingCacheLookupStrategy strategy; + } + + @ConfigGroup + public static class SchemaManagementConfig { + + /** + * The strategy used for index lifecycle. + */ + // We can't set an actual default value here: see comment on this class. + @ConfigItem(defaultValue = "create-or-validate") + SchemaManagementStrategyName strategy; + + } + + @ConfigGroup + public static class ThreadPoolConfig { + /** + * The size of the thread pool assigned to the backend. + *

+ * Note that number is per backend, not per index. + * Adding more indexes will not add more threads. + *

+ * As all operations happening in this thread-pool are non-blocking, + * raising its size above the number of processor cores available to the JVM will not bring noticeable performance + * benefit. + * The only reason to alter this setting would be to reduce the number of threads; + * for example, in an application with a single index with a single indexing queue, + * running on a machine with 64 processor cores, + * you might want to bring down the number of threads. + *

+ * Defaults to the number of processor cores available to the JVM on startup. + */ + // We can't set an actual default value here: see comment on this class. + @ConfigItem + OptionalInt size; + } + + // We can't set actual default values in this section, + // otherwise "quarkus.hibernate-search-orm.elasticsearch.index-defaults" will be ignored. + @ConfigGroup + public static class ElasticsearchIndexSchemaManagementConfig { + /** + * The minimal cluster status required. + */ + // We can't set an actual default value here: see comment on this class. + @ConfigItem(defaultValueDocumentation = "yellow") + Optional requiredStatus; + + /** + * How long we should wait for the status before failing the bootstrap. + */ + // We can't set an actual default value here: see comment on this class. + @ConfigItem(defaultValueDocumentation = "10S") + Optional requiredStatusWaitTimeout; + } + + // We can't set actual default values in this section, + // otherwise "quarkus.hibernate-search-orm.elasticsearch.index-defaults" will be ignored. + @ConfigGroup + public static class ElasticsearchIndexIndexingConfig { + /** + * The number of indexing queues assigned to each index. + *

+ * Higher values will lead to more connections being used in parallel, + * which may lead to higher indexing throughput, + * but incurs a risk of overloading Elasticsearch, + * i.e. of overflowing its HTTP request buffers and tripping + * circuit breakers, + * leading to Elasticsearch giving up on some request and resulting in indexing failures. + */ + // We can't set an actual default value here: see comment on this class. + @ConfigItem(defaultValueDocumentation = "10") + OptionalInt queueCount; + + /** + * The size of indexing queues. + *

+ * Lower values may lead to lower memory usage, especially if there are many queues, + * but values that are too low will reduce the likeliness of reaching the max bulk size + * and increase the likeliness of application threads blocking because the queue is full, + * which may lead to lower indexing throughput. + */ + // We can't set an actual default value here: see comment on this class. + @ConfigItem(defaultValueDocumentation = "1000") + OptionalInt queueSize; + + /** + * The maximum size of bulk requests created when processing indexing queues. + *

+ * Higher values will lead to more documents being sent in each HTTP request sent to Elasticsearch, + * which may lead to higher indexing throughput, + * but incurs a risk of overloading Elasticsearch, + * i.e. of overflowing its HTTP request buffers and tripping + * circuit breakers, + * leading to Elasticsearch giving up on some request and resulting in indexing failures. + *

+ * Note that raising this number above the queue size has no effect, + * as bulks cannot include more requests than are contained in the queue. + */ + // We can't set an actual default value here: see comment on this class. + @ConfigItem(defaultValueDocumentation = "100") + OptionalInt maxBulkSize; + } +} From 11c23bce8c20ed8d85fea9f7bdd530a4c7c1f9f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Tue, 8 Dec 2020 10:31:16 +0100 Subject: [PATCH 46/62] Disable Hibernate Search per PU when no indexed entities are found MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yoann Rodière --- ...HibernateSearchElasticsearchProcessor.java | 4 +++- .../HibernateSearchElasticsearchRecorder.java | 20 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/HibernateSearchElasticsearchProcessor.java b/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/HibernateSearchElasticsearchProcessor.java index 583eddd0b5884..9591a3729c8c5 100644 --- a/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/HibernateSearchElasticsearchProcessor.java +++ b/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/HibernateSearchElasticsearchProcessor.java @@ -104,7 +104,9 @@ private void buildForPersistenceUnit(HibernateSearchElasticsearchRecorder record BuildProducer configuredPersistenceUnits, BuildProducer integrations) { if (indexedAnnotationsForPU.isEmpty()) { - // we don't have any indexed entity, we can bail out + // we don't have any indexed entity, we can disable Hibernate Search + integrations.produce(new HibernateOrmIntegrationStaticConfiguredBuildItem(HIBERNATE_SEARCH_ELASTICSEARCH, + persistenceUnitName, recorder.createDisabledListener())); return; } diff --git a/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRecorder.java b/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRecorder.java index b8c4e75571ffd..6ab0b65104b6f 100644 --- a/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRecorder.java +++ b/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRecorder.java @@ -45,6 +45,10 @@ public HibernateOrmIntegrationStaticInitListener createStaticInitListener( return new HibernateSearchIntegrationStaticInitListener(buildTimeConfig); } + public HibernateOrmIntegrationStaticInitListener createDisabledListener() { + return new HibernateSearchIntegrationDisabledListener(); + } + public HibernateOrmIntegrationRuntimeInitListener createRuntimeInitListener( HibernateSearchElasticsearchRuntimeConfig runtimeConfig, String persistenceUnitName) { HibernateSearchElasticsearchRuntimeConfigPersistenceUnit puConfig = PersistenceUnitUtil @@ -89,6 +93,22 @@ public SearchSession get() { }; } + private static final class HibernateSearchIntegrationDisabledListener + implements HibernateOrmIntegrationStaticInitListener { + private HibernateSearchIntegrationDisabledListener() { + } + + @Override + public void contributeBootProperties(BiConsumer propertyCollector) { + propertyCollector.accept(HibernateOrmMapperSettings.ENABLED, false); + } + + @Override + public void onMetadataInitialized(Metadata metadata, BootstrapContext bootstrapContext, + BiConsumer propertyCollector) { + } + } + private static final class HibernateSearchIntegrationStaticInitListener implements HibernateOrmIntegrationStaticInitListener { From 0afe5b70a43681b4229a01a1d540c5c5aac8cf3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Tue, 8 Dec 2020 09:15:45 +0100 Subject: [PATCH 47/62] Test Hibernate Search in multi-PU mode with one PU not using Hibernate Search MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yoann Rodière --- .../MultiplePersistenceUnitsCdiTest.java | 39 +++++++++++++++++++ .../pu3/PU3Entity.java | 39 +++++++++++++++++++ .../pu3/package-info.java | 4 ++ ...tion-multiple-persistence-units.properties | 9 +++++ 4 files changed, 91 insertions(+) create mode 100644 extensions/hibernate-search-orm-elasticsearch/deployment/src/test/java/io/quarkus/hibernate/search/elasticsearch/test/multiplepersistenceunits/pu3/PU3Entity.java create mode 100644 extensions/hibernate-search-orm-elasticsearch/deployment/src/test/java/io/quarkus/hibernate/search/elasticsearch/test/multiplepersistenceunits/pu3/package-info.java diff --git a/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/java/io/quarkus/hibernate/search/elasticsearch/test/multiplepersistenceunits/MultiplePersistenceUnitsCdiTest.java b/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/java/io/quarkus/hibernate/search/elasticsearch/test/multiplepersistenceunits/MultiplePersistenceUnitsCdiTest.java index b23d2d235396f..c42bec47d34ce 100644 --- a/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/java/io/quarkus/hibernate/search/elasticsearch/test/multiplepersistenceunits/MultiplePersistenceUnitsCdiTest.java +++ b/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/java/io/quarkus/hibernate/search/elasticsearch/test/multiplepersistenceunits/MultiplePersistenceUnitsCdiTest.java @@ -1,16 +1,22 @@ package io.quarkus.hibernate.search.elasticsearch.test.multiplepersistenceunits; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import javax.enterprise.context.control.ActivateRequestContext; +import javax.enterprise.inject.Instance; +import javax.enterprise.inject.spi.CDI; import javax.inject.Inject; +import javax.persistence.EntityManager; import javax.transaction.NotSupportedException; import javax.transaction.SystemException; import javax.transaction.UserTransaction; +import org.hibernate.search.mapper.orm.Search; import org.hibernate.search.mapper.orm.entity.SearchIndexedEntity; import org.hibernate.search.mapper.orm.mapping.SearchMapping; import org.hibernate.search.mapper.orm.session.SearchSession; +import org.hibernate.search.util.common.SearchException; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.spec.JavaArchive; import org.junit.jupiter.api.Test; @@ -20,6 +26,7 @@ import io.quarkus.hibernate.search.elasticsearch.test.multiplepersistenceunits.defaultpu.DefaultPUEntity; import io.quarkus.hibernate.search.elasticsearch.test.multiplepersistenceunits.pu1.PU1Entity; import io.quarkus.hibernate.search.elasticsearch.test.multiplepersistenceunits.pu2.PU2Entity; +import io.quarkus.hibernate.search.elasticsearch.test.multiplepersistenceunits.pu3.PU3Entity; import io.quarkus.test.QuarkusUnitTest; public class MultiplePersistenceUnitsCdiTest { @@ -30,6 +37,7 @@ public class MultiplePersistenceUnitsCdiTest { .addPackage(DefaultPUEntity.class.getPackage()) .addPackage(PU1Entity.class.getPackage()) .addPackage(PU2Entity.class.getPackage()) + .addPackage(PU3Entity.class.getPackage()) .addAsResource("application-multiple-persistence-units.properties", "application.properties")); @Inject @@ -54,6 +62,10 @@ public class MultiplePersistenceUnitsCdiTest { @PersistenceUnit("pu2") SearchSession pu2Session; + @Inject + @PersistenceUnit("pu3") + EntityManager pu3EntityManager; + @Inject UserTransaction transaction; @@ -120,6 +132,33 @@ public void testPU2Session() { .returns(entity.getId(), PU2Entity::getId)); } + @Test + public void testPU3Mapping() { + // There are no indexed entities in PU3: Hibernate Search should be disabled + + Instance pu3MappingInstance = CDI.current().getBeanManager().createInstance() + .select(SearchMapping.class, new PersistenceUnit.PersistenceUnitLiteral("pu3")); + assertThat(pu3MappingInstance.isUnsatisfied()).isTrue(); + + assertThatThrownBy(() -> Search.mapping(pu3EntityManager.getEntityManagerFactory())) + .isInstanceOf(SearchException.class) + .hasMessageContaining("Hibernate Search was not initialized"); + } + + @Test + @ActivateRequestContext + public void testPU3Session() { + // There are no indexed entities in PU3: Hibernate Search should be disabled + + Instance pu3SessionInstance = CDI.current().getBeanManager().createInstance() + .select(SearchSession.class, new PersistenceUnit.PersistenceUnitLiteral("pu3")); + assertThat(pu3SessionInstance.isUnsatisfied()).isTrue(); + + inTransaction(() -> assertThatThrownBy(() -> Search.session(pu3EntityManager).search(PU3Entity.class)) + .isInstanceOf(SearchException.class) + .hasMessageContaining("Hibernate Search was not initialized")); + } + private void inTransaction(Runnable runnable) { try { transaction.begin(); diff --git a/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/java/io/quarkus/hibernate/search/elasticsearch/test/multiplepersistenceunits/pu3/PU3Entity.java b/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/java/io/quarkus/hibernate/search/elasticsearch/test/multiplepersistenceunits/pu3/PU3Entity.java new file mode 100644 index 0000000000000..1ae6751ca329f --- /dev/null +++ b/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/java/io/quarkus/hibernate/search/elasticsearch/test/multiplepersistenceunits/pu3/PU3Entity.java @@ -0,0 +1,39 @@ +package io.quarkus.hibernate.search.elasticsearch.test.multiplepersistenceunits.pu3; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +@Entity +public class PU3Entity { + + @Id + @GeneratedValue + private Long id; + + private String text; + + public PU3Entity() { + } + + public PU3Entity(String text) { + this.text = text; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + +} diff --git a/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/java/io/quarkus/hibernate/search/elasticsearch/test/multiplepersistenceunits/pu3/package-info.java b/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/java/io/quarkus/hibernate/search/elasticsearch/test/multiplepersistenceunits/pu3/package-info.java new file mode 100644 index 0000000000000..a622544cbad3b --- /dev/null +++ b/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/java/io/quarkus/hibernate/search/elasticsearch/test/multiplepersistenceunits/pu3/package-info.java @@ -0,0 +1,4 @@ +@PersistenceUnit("pu3") +package io.quarkus.hibernate.search.elasticsearch.test.multiplepersistenceunits.pu3; + +import io.quarkus.hibernate.orm.PersistenceUnit; \ No newline at end of file diff --git a/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/resources/application-multiple-persistence-units.properties b/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/resources/application-multiple-persistence-units.properties index a4a7d65292055..7b9b1ea054372 100644 --- a/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/resources/application-multiple-persistence-units.properties +++ b/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/resources/application-multiple-persistence-units.properties @@ -7,6 +7,9 @@ quarkus.datasource.data1.jdbc.url=jdbc:h2:mem:data1;DB_CLOSE_DELAY=-1 quarkus.datasource.data2.db-kind=h2 quarkus.datasource.data2.jdbc.url=jdbc:h2:mem:data2;DB_CLOSE_DELAY=-1 +quarkus.datasource.data3.db-kind=h2 +quarkus.datasource.data3.jdbc.url=jdbc:h2:mem:data3;DB_CLOSE_DELAY=-1 + quarkus.hibernate-orm.dialect=org.hibernate.dialect.H2Dialect quarkus.hibernate-orm.database.generation=drop-and-create @@ -35,3 +38,9 @@ quarkus.hibernate-search-orm."pu2".elasticsearch.hosts=${elasticsearch.hosts:loc quarkus.hibernate-search-orm."pu2".elasticsearch.protocol=${elasticsearch.protocol:http} quarkus.hibernate-search-orm."pu2".schema-management.strategy=drop-and-create-and-drop quarkus.hibernate-search-orm."pu2".automatic-indexing.synchronization.strategy=sync + +quarkus.hibernate-orm."pu3".dialect=org.hibernate.dialect.H2Dialect +quarkus.hibernate-orm."pu3".database.generation=drop-and-create +quarkus.hibernate-orm."pu3".datasource=data3 + +# No indexed entity in PU3 From 0936ea8f300e7fe59106452d28eaef3d2950ebf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Tue, 8 Dec 2020 10:48:42 +0100 Subject: [PATCH 48/62] Clarify expectations in NoConfigIndexedEntityTest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yoann Rodière --- .../test/configuration/NoConfigIndexedEntityTest.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/java/io/quarkus/hibernate/search/elasticsearch/test/configuration/NoConfigIndexedEntityTest.java b/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/java/io/quarkus/hibernate/search/elasticsearch/test/configuration/NoConfigIndexedEntityTest.java index 63ec0d03394a6..4acb20724fbab 100644 --- a/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/java/io/quarkus/hibernate/search/elasticsearch/test/configuration/NoConfigIndexedEntityTest.java +++ b/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/java/io/quarkus/hibernate/search/elasticsearch/test/configuration/NoConfigIndexedEntityTest.java @@ -1,5 +1,7 @@ package io.quarkus.hibernate.search.elasticsearch.test.configuration; +import static org.assertj.core.api.Assertions.assertThat; + import java.sql.SQLException; import org.jboss.shrinkwrap.api.ShrinkWrap; @@ -16,7 +18,10 @@ public class NoConfigIndexedEntityTest { static final QuarkusUnitTest config = new QuarkusUnitTest().setArchiveProducer( () -> ShrinkWrap.create(JavaArchive.class).addClass(IndexedEntity.class) .addAsResource("application-nohsearchconfig.properties", "application.properties")) - .setExpectedException(ConfigurationError.class); + .assertException(throwable -> assertThat(throwable) + .isInstanceOf(ConfigurationError.class) + .hasMessageContaining("The Elasticsearch version needs to be defined via properties:" + + " quarkus.hibernate-search-orm.elasticsearch.version")); @Test public void testNoConfig() throws SQLException { From 041e710ee6d7d75cd2891c9ff9238365020b5e94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Tue, 8 Dec 2020 11:50:07 +0100 Subject: [PATCH 49/62] Remove dangling callout in Hibernate ORM documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yoann Rodière --- docs/src/main/asciidoc/hibernate-orm.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/main/asciidoc/hibernate-orm.adoc b/docs/src/main/asciidoc/hibernate-orm.adoc index 13a8f6fee3007..3048adb718c2f 100644 --- a/docs/src/main/asciidoc/hibernate-orm.adoc +++ b/docs/src/main/asciidoc/hibernate-orm.adoc @@ -350,7 +350,7 @@ You can inject the `EntityManagerFactory` of a named persistence unit using the [source,java] ---- @Inject -@PersistenceUnit("users") <1> +@PersistenceUnit("users") EntityManagerFactory entityManagerFactory; ---- From f30dcb34de2f7e04343655b3e77099264daff4c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Tue, 8 Dec 2020 11:54:37 +0100 Subject: [PATCH 50/62] Document multi-PU support in Hibernate Search MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yoann Rodière --- docs/src/main/asciidoc/hibernate-orm.adoc | 1 + .../hibernate-search-orm-elasticsearch.adoc | 106 +++++++++++++++++- 2 files changed, 105 insertions(+), 2 deletions(-) diff --git a/docs/src/main/asciidoc/hibernate-orm.adoc b/docs/src/main/asciidoc/hibernate-orm.adoc index 3048adb718c2f..a0502ed023c65 100644 --- a/docs/src/main/asciidoc/hibernate-orm.adoc +++ b/docs/src/main/asciidoc/hibernate-orm.adoc @@ -246,6 +246,7 @@ You can point your persistence unit to the default datasource by setting it to ` It is perfectly valid to have several persistence units pointing to the same datasource. ==== +[[multiple-persistence-units-attaching-model-classes]] ==== Attaching model classes to persistence units There are two ways to attach model classes to persistence units, and they should not be mixed: diff --git a/docs/src/main/asciidoc/hibernate-search-orm-elasticsearch.adoc b/docs/src/main/asciidoc/hibernate-search-orm-elasticsearch.adoc index 52fba1ca94ea3..c6e8cf6fcdd98 100644 --- a/docs/src/main/asciidoc/hibernate-search-orm-elasticsearch.adoc +++ b/docs/src/main/asciidoc/hibernate-search-orm-elasticsearch.adoc @@ -483,8 +483,9 @@ In our existing `LibraryResource`, we just need to inject the `SearchSession`: SearchSession searchSession; // <1> ---- <1> Inject a Hibernate Search session, which relies on the `EntityManager` under the hood. -Applications with multiple persistence units can use a CDI qualifier to select the right one: -either `@Named` or `@io.quarkus.hibernate.orm.PersistenceUnit`. +Applications with multiple persistence units can use the CDI qualifier `@io.quarkus.hibernate.orm.PersistenceUnit` +to select the right one: +see <>. And then we can add the following methods (and a few ``import``s): @@ -637,6 +638,107 @@ You can now interact with your REST service: As you can see, all your updates are automatically synchronized to the Elasticsearch cluster. +[[multiple-persistence-units]] +== Multiple persistence units + +=== Configuring multiple persistence units + +With the Hibernate ORM extension, +link:hibernate-orm#multiple-persistence-units[you can set up multiple persistence units], +each with its own datasource and configuration. + +If you do declare multiple persistence units, +you will also configure Hibernate Search separately for each persistence unit. + +The properties at the root of the `quarkus.hibernate-search-orm.` namespace define the default persistence unit. +For instance, the following snippet defines a default datasource and a default persistence unit, +and sets the Elasticsearch host for that persistence unit to `es1.mycompany.com:9200`. + +[source,properties] +---- +quarkus.datasource.db-kind=h2 +quarkus.datasource.jdbc.url=jdbc:h2:mem:default;DB_CLOSE_DELAY=-1 + +quarkus.hibernate-orm.dialect=org.hibernate.dialect.H2Dialect + +quarkus.hibernate-search-orm.elasticsearch.hosts=es1.mycompany.com:9200 +quarkus.hibernate-search-orm.elasticsearch.version=7 +quarkus.hibernate-search-orm.automatic-indexing.synchronization.strategy=write-sync +---- + +Using a map based approach, it is also possible to configure named persistence units: + +[source,properties] +---- +quarkus.datasource."users".db-kind=h2 <1> +quarkus.datasource."users".jdbc.url=jdbc:h2:mem:users;DB_CLOSE_DELAY=-1 + +quarkus.datasource."inventory".db-kind=h2 <2> +quarkus.datasource."inventory".jdbc.url=jdbc:h2:mem:inventory;DB_CLOSE_DELAY=-1 + +quarkus.hibernate-orm."users".datasource=users <3> +quarkus.hibernate-orm."users".packages=org.acme.model.user + +quarkus.hibernate-orm."inventory".datasource=inventory <4> +quarkus.hibernate-orm."inventory".packages=org.acme.model.inventory + +quarkus.hibernate-search-orm."users".elasticsearch.hosts=es1.mycompany.com:9200 <5> +quarkus.hibernate-search-orm."users".elasticsearch.version=7 +quarkus.hibernate-search-orm."users".automatic-indexing.synchronization.strategy=write-sync + +quarkus.hibernate-search-orm."inventory".elasticsearch.hosts=es2.mycompany.com:9200 <6> +quarkus.hibernate-search-orm."inventory".elasticsearch.version=7 +quarkus.hibernate-search-orm."inventory".automatic-indexing.synchronization.strategy=write-sync +---- +<1> Define a datasource named `users`. +<2> Define a datasource named `inventory`. +<3> Define a persistence unit called `users` pointing to the `users` datasource. +<4> Define a persistence unit called `inventory` pointing to the `inventory` datasource. +<5> Configure Hibernate Search for the `users` persistence unit, +setting the Elasticsearch host for that persistence unit to `es1.mycompany.com:9200`. +<6> Configure Hibernate Search for the `inventory` persistence unit, +setting the Elasticsearch host for that persistence unit to `es2.mycompany.com:9200`. + +[[multiple-persistence-units-attaching-model-classes]] +=== Attaching model classes to persistence units + +For each persistence unit, Hibernate Search will only consider indexed entities that are attached to that persistence unit. +Entities are attached to a persistence unit by +link:hibernate-orm#multiple-persistence-units-attaching-model-classes[configuring the Hibernate ORM extension]. + +[[multiple-persistence-units-attaching-cdi]] +=== CDI integration + +You can inject Hibernate Search's main entry points, `SearchSession` and `SearchMapping`, using CDI: + +[source,java] +---- +@Inject +SearchSession searchSession; +---- + +This will inject the `SearchSession` of the default persistence unit. + +To inject the `SearchSession` of a named persistence unit (`users` in our example), +just add a qualifier: + +[source,java] +---- +@Inject +@PersistenceUnit("users") <1> +SearchSession searchSession; +---- +<1> This is the `@io.quarkus.hibernate.orm.PersistenceUnit` annotation. + +You can inject the `SearchMapping` of a named persistence unit using the exact same mechanism: + +[source,java] +---- +@Inject +@PersistenceUnit("users") +SearchMapping searchMapping; +---- + == Building a native executable You can build a native executable with the usual command `./mvnw package -Pnative`. From 21c288e0fcd58b2c6968587fa2169fc4b21bd07d Mon Sep 17 00:00:00 2001 From: Gwenneg Lepage Date: Mon, 30 Nov 2020 01:21:45 +0100 Subject: [PATCH 51/62] Prepare removal of transformed caching interceptor bindings --- .../CacheAnnotationsTransformer.java | 37 +++++++++++-------- .../deployment/CacheDeploymentConstants.java | 4 +- .../cache/runtime/CacheInterceptor.java | 15 ++++++-- .../runtime/CacheInvalidateInterceptor.java | 3 +- .../CacheInvalidateInterceptorBinding.java | 3 -- .../runtime/CacheKeyParameterPositions.java | 29 +++++++++++++++ .../cache/runtime/CacheResultInterceptor.java | 3 +- .../CacheResultInterceptorBinding.java | 3 -- .../cache/runtime/caffeine/CaffeineCache.java | 6 +-- 9 files changed, 72 insertions(+), 31 deletions(-) create mode 100644 extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheKeyParameterPositions.java diff --git a/extensions/cache/deployment/src/main/java/io/quarkus/cache/deployment/CacheAnnotationsTransformer.java b/extensions/cache/deployment/src/main/java/io/quarkus/cache/deployment/CacheAnnotationsTransformer.java index 5e28635699d07..d943fd0554732 100644 --- a/extensions/cache/deployment/src/main/java/io/quarkus/cache/deployment/CacheAnnotationsTransformer.java +++ b/extensions/cache/deployment/src/main/java/io/quarkus/cache/deployment/CacheAnnotationsTransformer.java @@ -5,10 +5,13 @@ import static io.quarkus.cache.deployment.CacheDeploymentConstants.CACHE_INVALIDATE_ALL_LIST; import static io.quarkus.cache.deployment.CacheDeploymentConstants.CACHE_INVALIDATE_LIST; import static io.quarkus.cache.deployment.CacheDeploymentConstants.CACHE_KEY; -import static io.quarkus.cache.deployment.CacheDeploymentConstants.CACHE_KEY_PARAMETER_POSITIONS_PARAM; +import static io.quarkus.cache.deployment.CacheDeploymentConstants.CACHE_KEY_PARAMETER_POSITIONS; import static io.quarkus.cache.deployment.CacheDeploymentConstants.CACHE_NAME_PARAM; import static io.quarkus.cache.deployment.CacheDeploymentConstants.CACHE_RESULT; import static io.quarkus.cache.deployment.CacheDeploymentConstants.LOCK_TIMEOUT_PARAM; +import static org.jboss.jandex.AnnotationInstance.create; +import static org.jboss.jandex.AnnotationValue.createArrayValue; +import static org.jboss.jandex.AnnotationValue.createShortValue; import java.util.ArrayList; import java.util.List; @@ -57,9 +60,26 @@ public void transform(TransformationContext context) { } } } + if (requiresCacheKeyParameterPositionsInterceptorBinding(method)) { + List positions = new ArrayList<>(); + for (AnnotationInstance annotation : method.annotations(CACHE_KEY)) { + positions.add(createShortValue("", annotation.target().asMethodParameter().position())); + } + if (!positions.isEmpty()) { + AnnotationValue annotationValue = createArrayValue("value", toArray(positions)); + AnnotationInstance binding = create(CACHE_KEY_PARAMETER_POSITIONS, method, + new AnnotationValue[] { annotationValue }); + interceptorBindings.add(binding); + } + } context.transform().addAll(interceptorBindings).done(); } + private boolean requiresCacheKeyParameterPositionsInterceptorBinding(MethodInfo method) { + return method.hasAnnotation(CACHE_KEY) && (method.hasAnnotation(CACHE_INVALIDATE) + || method.hasAnnotation(CACHE_INVALIDATE_LIST) || method.hasAnnotation(CACHE_RESULT)); + } + private AnnotationInstance createCacheInvalidateAllBinding(AnnotationInstance annotation, AnnotationTarget target) { return createBinding(CacheInvalidateAllInterceptorBinding.class, target, getCacheName(annotation)); } @@ -68,7 +88,6 @@ private AnnotationInstance createCacheInvalidateBinding(MethodInfo method, Annot AnnotationTarget target) { List parameters = new ArrayList<>(); parameters.add(getCacheName(annotation)); - findCacheKeyParameters(method).ifPresent(parameters::add); return createBinding(CacheInvalidateInterceptorBinding.class, target, toArray(parameters)); } @@ -76,7 +95,6 @@ private AnnotationInstance createCacheResultBinding(MethodInfo method, Annotatio AnnotationTarget target) { List parameters = new ArrayList<>(); parameters.add(getCacheName(annotation)); - findCacheKeyParameters(method).ifPresent(parameters::add); findLockTimeout(annotation).ifPresent(parameters::add); return createBinding(CacheResultInterceptorBinding.class, target, toArray(parameters)); } @@ -89,19 +107,6 @@ private AnnotationValue getCacheName(AnnotationInstance annotation) { return annotation.value(CACHE_NAME_PARAM); } - private Optional findCacheKeyParameters(MethodInfo method) { - List parameters = new ArrayList<>(); - for (AnnotationInstance annotation : method.annotations()) { - if (annotation.target().kind() == Kind.METHOD_PARAMETER && CACHE_KEY.equals(annotation.name())) { - parameters.add(AnnotationValue.createShortValue("", annotation.target().asMethodParameter().position())); - } - } - if (parameters.isEmpty()) { - return Optional.empty(); - } - return Optional.of(AnnotationValue.createArrayValue(CACHE_KEY_PARAMETER_POSITIONS_PARAM, toArray(parameters))); - } - private Optional findLockTimeout(AnnotationInstance annotation) { return Optional.ofNullable(annotation.value(LOCK_TIMEOUT_PARAM)); } diff --git a/extensions/cache/deployment/src/main/java/io/quarkus/cache/deployment/CacheDeploymentConstants.java b/extensions/cache/deployment/src/main/java/io/quarkus/cache/deployment/CacheDeploymentConstants.java index 0116dadc22830..82c9f24635e8e 100644 --- a/extensions/cache/deployment/src/main/java/io/quarkus/cache/deployment/CacheDeploymentConstants.java +++ b/extensions/cache/deployment/src/main/java/io/quarkus/cache/deployment/CacheDeploymentConstants.java @@ -9,6 +9,7 @@ import io.quarkus.cache.CacheInvalidateAll; import io.quarkus.cache.CacheKey; import io.quarkus.cache.CacheResult; +import io.quarkus.cache.runtime.CacheKeyParameterPositions; public class CacheDeploymentConstants { @@ -23,10 +24,11 @@ public class CacheDeploymentConstants { CACHE_RESULT, CACHE_INVALIDATE, CACHE_INVALIDATE_ALL); public static final List API_METHODS_ANNOTATIONS_LISTS = Arrays.asList( CACHE_INVALIDATE_LIST, CACHE_INVALIDATE_ALL_LIST); + public static final DotName CACHE_KEY_PARAMETER_POSITIONS = DotName + .createSimple(CacheKeyParameterPositions.class.getName()); // Annotations parameters. public static final String CACHE_NAME_PARAM = "cacheName"; - public static final String CACHE_KEY_PARAMETER_POSITIONS_PARAM = "cacheKeyParameterPositions"; public static final String LOCK_TIMEOUT_PARAM = "lockTimeout"; // Caffeine. diff --git a/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheInterceptor.java b/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheInterceptor.java index 202f017c1b85b..27bc160e75dc4 100644 --- a/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheInterceptor.java +++ b/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheInterceptor.java @@ -19,7 +19,7 @@ public abstract class CacheInterceptor { protected CacheRepository cacheRepository; @SuppressWarnings("unchecked") - protected List getInterceptorBindings(InvocationContext context, Class bindingClass) { + protected List getInterceptorBindings(InvocationContext context, Class bindingClass) { List bindings = new ArrayList<>(); for (Annotation binding : InterceptorBindings.getInterceptorBindings(context)) { if (bindingClass.isInstance(binding)) { @@ -29,12 +29,21 @@ protected List getInterceptorBindings(InvocationContext context, Class return bindings; } - protected T getInterceptorBinding(InvocationContext context, Class bindingClass) { + protected T getInterceptorBinding(InvocationContext context, Class bindingClass) { return getInterceptorBindings(context, bindingClass).get(0); } + protected short[] getCacheKeyParameterPositions(InvocationContext context) { + List bindings = getInterceptorBindings(context, CacheKeyParameterPositions.class); + if (bindings.isEmpty()) { + return new short[0]; + } else { + return bindings.get(0).value(); + } + } + protected Object getCacheKey(CaffeineCache cache, short[] cacheKeyParameterPositions, Object[] methodParameterValues) { - if (methodParameterValues.length == 0) { + if (methodParameterValues == null || methodParameterValues.length == 0) { // If the intercepted method doesn't have any parameter, then the default cache key will be used. return cache.getDefaultKey(); } else if (cacheKeyParameterPositions.length == 1) { diff --git a/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheInvalidateInterceptor.java b/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheInvalidateInterceptor.java index 2f8265732817b..064c9f9bd7c43 100644 --- a/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheInvalidateInterceptor.java +++ b/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheInvalidateInterceptor.java @@ -19,11 +19,12 @@ public class CacheInvalidateInterceptor extends CacheInterceptor { @AroundInvoke public Object intercept(InvocationContext context) throws Exception { Object key = null; + short[] cacheKeyParameterPositions = getCacheKeyParameterPositions(context); for (CacheInvalidateInterceptorBinding binding : getInterceptorBindings(context, CacheInvalidateInterceptorBinding.class)) { CaffeineCache cache = cacheRepository.getCache(binding.cacheName()); if (key == null) { - key = getCacheKey(cache, binding.cacheKeyParameterPositions(), context.getParameters()); + key = getCacheKey(cache, cacheKeyParameterPositions, context.getParameters()); } if (LOGGER.isDebugEnabled()) { LOGGER.debugf("Invalidating entry with key [%s] from cache [%s]", key, cache.getName()); diff --git a/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheInvalidateInterceptorBinding.java b/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheInvalidateInterceptorBinding.java index 94da6f15a5410..91eb0c4614799 100644 --- a/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheInvalidateInterceptorBinding.java +++ b/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheInvalidateInterceptorBinding.java @@ -20,9 +20,6 @@ @Nonbinding String cacheName() default ""; - @Nonbinding - short[] cacheKeyParameterPositions() default {}; - @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @interface List { diff --git a/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheKeyParameterPositions.java b/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheKeyParameterPositions.java new file mode 100644 index 0000000000000..a3e48e3668c9b --- /dev/null +++ b/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheKeyParameterPositions.java @@ -0,0 +1,29 @@ +package io.quarkus.cache.runtime; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import javax.enterprise.util.Nonbinding; +import javax.interceptor.InterceptorBinding; + +/** + * This interceptor binding is added at build time on a method if: + *

    + *
  • it is annotated with {@link io.quarkus.cache.CacheResult CacheResult} or {@link io.quarkus.cache.CacheInvalidate + * CacheInvalidate}
  • + *
  • at least one of its arguments is annotated with {@link io.quarkus.cache.CacheKey CacheKey}
  • + *
+ * It helps improving performances by storing at build time the positions of {@link io.quarkus.cache.CacheKey + * CacheKey}-annotated arguments instead of relying on reflection at run time (which is bad for performances) to identify these + * positions. + */ +@InterceptorBinding +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.TYPE, ElementType.METHOD }) +public @interface CacheKeyParameterPositions { + + @Nonbinding + short[] value() default {}; +} diff --git a/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheResultInterceptor.java b/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheResultInterceptor.java index 0c05be23ec167..c534a51713186 100644 --- a/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheResultInterceptor.java +++ b/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheResultInterceptor.java @@ -27,7 +27,8 @@ public Object intercept(InvocationContext context) throws Exception { CacheResultInterceptorBinding binding = getInterceptorBinding(context, CacheResultInterceptorBinding.class); CaffeineCache cache = cacheRepository.getCache(binding.cacheName()); - Object key = getCacheKey(cache, binding.cacheKeyParameterPositions(), context.getParameters()); + short[] cacheKeyParameterPositions = getCacheKeyParameterPositions(context); + Object key = getCacheKey(cache, cacheKeyParameterPositions, context.getParameters()); if (LOGGER.isDebugEnabled()) { LOGGER.debugf("Loading entry with key [%s] from cache [%s]", key, cache.getName()); } diff --git a/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheResultInterceptorBinding.java b/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheResultInterceptorBinding.java index d091e9b6b5248..f4e3b3e052f79 100644 --- a/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheResultInterceptorBinding.java +++ b/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheResultInterceptorBinding.java @@ -16,9 +16,6 @@ @Nonbinding String cacheName() default ""; - @Nonbinding - short[] cacheKeyParameterPositions() default {}; - @Nonbinding long lockTimeout() default 0; } diff --git a/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/caffeine/CaffeineCache.java b/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/caffeine/CaffeineCache.java index b4030f024e9bf..dcb27cf47ac34 100644 --- a/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/caffeine/CaffeineCache.java +++ b/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/caffeine/CaffeineCache.java @@ -75,13 +75,13 @@ public CompletableFuture get(Object key, Function valueL cache.asMap().remove(key, newCacheValue); newCacheValue.complete(new CaffeineComputationThrowable(t)); } - return unwrapCacheValueOrThrowable(key, newCacheValue); + return unwrapCacheValueOrThrowable(newCacheValue); } else { - return unwrapCacheValueOrThrowable(key, existingCacheValue); + return unwrapCacheValueOrThrowable(existingCacheValue); } } - private CompletableFuture unwrapCacheValueOrThrowable(Object key, CompletableFuture cacheValue) { + private CompletableFuture unwrapCacheValueOrThrowable(CompletableFuture cacheValue) { return cacheValue.thenApply(new Function() { @Override public Object apply(Object value) { From 5761ad15b7252d3bfb0422be712a01292daf28b1 Mon Sep 17 00:00:00 2001 From: Justin Lee Date: Mon, 7 Dec 2020 15:34:00 -0500 Subject: [PATCH 52/62] process classes so field access can be replaced --- .../PanacheHibernateResourceProcessor.java | 3 +- .../BasePanacheMongoResourceProcessor.java | 44 ++++++++++- .../deployment/PanacheEntityEnhancer.java | 1 + .../panache/{ => resources}/BookResource.java | 5 +- .../{ => resources}/PersonEntityResource.kt | 5 +- .../PersonRepositoryResource.kt | 7 +- .../ReactivePersonEntityResource.kt | 6 +- .../ReactivePersonRepositoryResource.kt | 6 +- .../it/mongodb/panache/AccessorEntity.java | 56 ++++++++++++++ .../it/mongodb/panache/GenericEntity.java | 16 ++++ .../it/mongodb/panache/TestEndpoint.java | 74 +++++++++++++++++++ .../{ => resources}/PersonEntityResource.java | 5 +- .../PersonRepositoryResource.java | 7 +- .../ReactivePersonEntityResource.java | 3 +- .../ReactivePersonRepositoryResource.java | 3 +- .../panache/MongodbPanacheResourceTest.java | 20 +++++ 16 files changed, 242 insertions(+), 19 deletions(-) rename integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/{ => resources}/BookResource.java (81%) rename integration-tests/mongodb-panache-kotlin/src/main/kotlin/io/quarkus/it/mongodb/panache/person/{ => resources}/PersonEntityResource.kt (91%) rename integration-tests/mongodb-panache-kotlin/src/main/kotlin/io/quarkus/it/mongodb/panache/person/{ => resources}/PersonRepositoryResource.kt (88%) rename integration-tests/mongodb-panache-kotlin/src/main/kotlin/io/quarkus/it/mongodb/panache/reactive/person/{ => resources}/ReactivePersonEntityResource.kt (95%) rename integration-tests/mongodb-panache-kotlin/src/main/kotlin/io/quarkus/it/mongodb/panache/reactive/person/{ => resources}/ReactivePersonRepositoryResource.kt (95%) create mode 100644 integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/AccessorEntity.java create mode 100644 integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/GenericEntity.java create mode 100644 integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/TestEndpoint.java rename integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/person/{ => resources}/PersonEntityResource.java (91%) rename integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/person/{ => resources}/PersonRepositoryResource.java (89%) rename integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/reactive/person/{ => resources}/ReactivePersonEntityResource.java (95%) rename integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/reactive/person/{ => resources}/ReactivePersonRepositoryResource.java (95%) diff --git a/extensions/panache/hibernate-orm-panache/deployment/src/main/java/io/quarkus/hibernate/orm/panache/deployment/PanacheHibernateResourceProcessor.java b/extensions/panache/hibernate-orm-panache/deployment/src/main/java/io/quarkus/hibernate/orm/panache/deployment/PanacheHibernateResourceProcessor.java index 5ee25e8b00026..ee4c5b2b72a6f 100644 --- a/extensions/panache/hibernate-orm-panache/deployment/src/main/java/io/quarkus/hibernate/orm/panache/deployment/PanacheHibernateResourceProcessor.java +++ b/extensions/panache/hibernate-orm-panache/deployment/src/main/java/io/quarkus/hibernate/orm/panache/deployment/PanacheHibernateResourceProcessor.java @@ -1,5 +1,7 @@ package io.quarkus.hibernate.orm.panache.deployment; +import static io.quarkus.panache.common.deployment.PanacheEntityEnhancer.META_INF_PANACHE_ARCHIVE_MARKER; + import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -63,7 +65,6 @@ public final class PanacheHibernateResourceProcessor { private static final DotName DOTNAME_ENTITY_MANAGER = DotName.createSimple(EntityManager.class.getName()); private static final DotName DOTNAME_ID = DotName.createSimple(Id.class.getName()); - protected static final String META_INF_PANACHE_ARCHIVE_MARKER = "META-INF/panache-archive.marker"; @BuildStep FeatureBuildItem featureBuildItem() { diff --git a/extensions/panache/mongodb-panache-common/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/BasePanacheMongoResourceProcessor.java b/extensions/panache/mongodb-panache-common/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/BasePanacheMongoResourceProcessor.java index cb392fb6f81d8..ca2e501bafcf1 100644 --- a/extensions/panache/mongodb-panache-common/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/BasePanacheMongoResourceProcessor.java +++ b/extensions/panache/mongodb-panache-common/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/BasePanacheMongoResourceProcessor.java @@ -1,6 +1,7 @@ package io.quarkus.mongodb.panache.deployment; import static io.quarkus.deployment.util.JandexUtil.resolveTypeParameters; +import static io.quarkus.panache.common.deployment.PanacheEntityEnhancer.META_INF_PANACHE_ARCHIVE_MARKER; import static org.jboss.jandex.DotName.createSimple; import java.util.Collection; @@ -27,6 +28,8 @@ import org.jboss.jandex.Type; import io.quarkus.arc.deployment.ValidationPhaseBuildItem; +import io.quarkus.bootstrap.classloading.ClassPathElement; +import io.quarkus.bootstrap.classloading.QuarkusClassLoader; import io.quarkus.builder.BuildException; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; @@ -34,7 +37,6 @@ import io.quarkus.deployment.annotations.Record; import io.quarkus.deployment.bean.JavaBeanUtil; import io.quarkus.deployment.builditem.ApplicationArchivesBuildItem; -import io.quarkus.deployment.builditem.ApplicationIndexBuildItem; import io.quarkus.deployment.builditem.BytecodeTransformerBuildItem; import io.quarkus.deployment.builditem.CombinedIndexBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; @@ -49,8 +51,10 @@ import io.quarkus.mongodb.panache.ProjectionFor; import io.quarkus.mongodb.panache.jackson.ObjectIdDeserializer; import io.quarkus.mongodb.panache.jackson.ObjectIdSerializer; +import io.quarkus.panache.common.deployment.MetamodelInfo; import io.quarkus.panache.common.deployment.PanacheEntityClassesBuildItem; import io.quarkus.panache.common.deployment.PanacheEntityEnhancer; +import io.quarkus.panache.common.deployment.PanacheFieldAccessEnhancer; import io.quarkus.panache.common.deployment.PanacheMethodCustomizer; import io.quarkus.panache.common.deployment.PanacheMethodCustomizerBuildItem; import io.quarkus.panache.common.deployment.PanacheRepositoryEnhancer; @@ -65,7 +69,7 @@ public abstract class BasePanacheMongoResourceProcessor { public static final DotName PROJECTION_FOR = createSimple(ProjectionFor.class.getName()); @BuildStep - public void buildImperative(CombinedIndexBuildItem index, ApplicationIndexBuildItem applicationIndex, + public void buildImperative(CombinedIndexBuildItem index, BuildProducer transformers, BuildProducer reflectiveClass, BuildProducer propertyMappingClass, @@ -235,7 +239,8 @@ public void mongoClientNames(ApplicationArchivesBuildItem applicationArchivesBui } protected void processEntities(CombinedIndexBuildItem index, - BuildProducer transformers, BuildProducer reflectiveClass, + BuildProducer transformers, + BuildProducer reflectiveClass, BuildProducer propertyMappingClass, PanacheEntityEnhancer entityEnhancer, TypeBundle typeBundle) { @@ -264,6 +269,39 @@ protected void processEntities(CombinedIndexBuildItem index, // Register for building the property mapping cache propertyMappingClass.produce(new PropertyMappingClassBuildStep(modelClass)); } + + replaceFieldAccesses(transformers, entityEnhancer, modelClasses); + } + + private void replaceFieldAccesses(BuildProducer transformers, + PanacheEntityEnhancer entityEnhancer, Set modelClasses) { + MetamodelInfo modelInfo = entityEnhancer.getModelInfo(); + Set modelClassNamesInternal = new HashSet<>(); + for (String entityClassName : modelClasses) { + modelClassNamesInternal.add(entityClassName.replace(".", "/")); + } + + if (modelInfo.hasEntities()) { + PanacheFieldAccessEnhancer panacheFieldAccessEnhancer = new PanacheFieldAccessEnhancer(modelInfo); + QuarkusClassLoader tccl = (QuarkusClassLoader) Thread.currentThread().getContextClassLoader(); + Set produced = new HashSet<>(); + + for (ClassPathElement i : tccl.getElementsWithResource(META_INF_PANACHE_ARCHIVE_MARKER)) { + for (String res : i.getProvidedResources()) { + if (res.endsWith(".class")) { + String cn = res.replace("/", ".").substring(0, res.length() - 6); + if (produced.contains(cn)) { + continue; + } + if (!modelClasses.contains(cn)) { + produced.add(cn); + transformers.produce( + new BytecodeTransformerBuildItem(cn, panacheFieldAccessEnhancer, modelClassNamesInternal)); + } + } + } + } + } } protected void processRepositories(CombinedIndexBuildItem index, diff --git a/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/PanacheEntityEnhancer.java b/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/PanacheEntityEnhancer.java index bb6c8dec51937..eb3a2eaac578d 100644 --- a/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/PanacheEntityEnhancer.java +++ b/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/PanacheEntityEnhancer.java @@ -16,6 +16,7 @@ public abstract class PanacheEntityEnhancer extends PanacheMongoEntity { + public T t; + public T t2; + + public T getT2() { + return t2; + } + + public void setT2(T t2) { + this.t2 = t2; + } +} \ No newline at end of file diff --git a/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/TestEndpoint.java b/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/TestEndpoint.java new file mode 100644 index 0000000000000..adedbb1e53de4 --- /dev/null +++ b/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/TestEndpoint.java @@ -0,0 +1,74 @@ +package io.quarkus.it.mongodb.panache; + +import java.lang.reflect.Method; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; + +import org.junit.jupiter.api.Assertions; + +@Path("/accessors") +public class TestEndpoint { + + @GET + public String testAccessors() throws NoSuchMethodException, SecurityException { + checkMethod(AccessorEntity.class, "getString", String.class); + checkMethod(AccessorEntity.class, "isBool", boolean.class); + checkMethod(AccessorEntity.class, "getC", char.class); + checkMethod(AccessorEntity.class, "getS", short.class); + checkMethod(AccessorEntity.class, "getI", int.class); + checkMethod(AccessorEntity.class, "getL", long.class); + checkMethod(AccessorEntity.class, "getF", float.class); + checkMethod(AccessorEntity.class, "getD", double.class); + checkMethod(AccessorEntity.class, "getT", Object.class); + checkMethod(AccessorEntity.class, "getT2", Object.class); + + checkMethod(AccessorEntity.class, "setString", void.class, String.class); + checkMethod(AccessorEntity.class, "setBool", void.class, boolean.class); + checkMethod(AccessorEntity.class, "setC", void.class, char.class); + checkMethod(AccessorEntity.class, "setS", void.class, short.class); + checkMethod(AccessorEntity.class, "setI", void.class, int.class); + checkMethod(AccessorEntity.class, "setL", void.class, long.class); + checkMethod(AccessorEntity.class, "setF", void.class, float.class); + checkMethod(AccessorEntity.class, "setD", void.class, double.class); + checkMethod(AccessorEntity.class, "setT", void.class, Object.class); + checkMethod(AccessorEntity.class, "setT2", void.class, Object.class); + + try { + checkMethod(AccessorEntity.class, "getTrans2", Object.class); + Assertions.fail("transient field should have no getter: trans2"); + } catch (NoSuchMethodException x) { + } + + try { + checkMethod(AccessorEntity.class, "setTrans2", void.class, Object.class); + Assertions.fail("transient field should have no setter: trans2"); + } catch (NoSuchMethodException x) { + } + + // Now check that accessors are called + AccessorEntity entity = new AccessorEntity(); + @SuppressWarnings("unused") + byte b = entity.b; + Assertions.assertEquals(1, entity.getBCalls); + entity.i = 2; + Assertions.assertEquals(1, entity.setICalls); + Object trans = entity.trans; + Assertions.assertEquals(0, entity.getTransCalls); + entity.trans = trans; + Assertions.assertEquals(0, entity.setTransCalls); + + // accessors inside the entity itself + entity.method(); + Assertions.assertEquals(2, entity.getBCalls); + Assertions.assertEquals(2, entity.setICalls); + + return "OK"; + } + + private void checkMethod(Class klass, String name, Class returnType, Class... params) + throws NoSuchMethodException, SecurityException { + Method method = klass.getMethod(name, params); + Assertions.assertEquals(returnType, method.getReturnType()); + } +} diff --git a/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/person/PersonEntityResource.java b/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/person/resources/PersonEntityResource.java similarity index 91% rename from integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/person/PersonEntityResource.java rename to integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/person/resources/PersonEntityResource.java index 6a27373e06afa..79488c614b84d 100644 --- a/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/person/PersonEntityResource.java +++ b/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/person/resources/PersonEntityResource.java @@ -1,4 +1,4 @@ -package io.quarkus.it.mongodb.panache.person; +package io.quarkus.it.mongodb.panache.person.resources; import java.net.URI; import java.util.HashSet; @@ -8,6 +8,9 @@ import javax.ws.rs.*; import javax.ws.rs.core.Response; +import io.quarkus.it.mongodb.panache.person.PersonEntity; +import io.quarkus.it.mongodb.panache.person.PersonName; +import io.quarkus.it.mongodb.panache.person.Status; import io.quarkus.panache.common.Sort; @Path("/persons/entity") diff --git a/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/person/PersonRepositoryResource.java b/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/person/resources/PersonRepositoryResource.java similarity index 89% rename from integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/person/PersonRepositoryResource.java rename to integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/person/resources/PersonRepositoryResource.java index ba013af7c583d..d19703c3cbbd0 100644 --- a/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/person/PersonRepositoryResource.java +++ b/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/person/resources/PersonRepositoryResource.java @@ -1,4 +1,4 @@ -package io.quarkus.it.mongodb.panache.person; +package io.quarkus.it.mongodb.panache.person.resources; import java.net.URI; import java.util.HashSet; @@ -9,6 +9,11 @@ import javax.ws.rs.*; import javax.ws.rs.core.Response; +import io.quarkus.it.mongodb.panache.person.MockablePersonRepository; +import io.quarkus.it.mongodb.panache.person.Person; +import io.quarkus.it.mongodb.panache.person.PersonName; +import io.quarkus.it.mongodb.panache.person.PersonRepository; +import io.quarkus.it.mongodb.panache.person.Status; import io.quarkus.panache.common.Sort; @Path("/persons/repository") diff --git a/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/reactive/person/ReactivePersonEntityResource.java b/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/reactive/person/resources/ReactivePersonEntityResource.java similarity index 95% rename from integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/reactive/person/ReactivePersonEntityResource.java rename to integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/reactive/person/resources/ReactivePersonEntityResource.java index d2cc1adf648f7..c278d7ac751d4 100644 --- a/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/reactive/person/ReactivePersonEntityResource.java +++ b/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/reactive/person/resources/ReactivePersonEntityResource.java @@ -1,4 +1,4 @@ -package io.quarkus.it.mongodb.panache.reactive.person; +package io.quarkus.it.mongodb.panache.reactive.person.resources; import java.net.URI; import java.util.HashSet; @@ -9,6 +9,7 @@ import javax.ws.rs.core.Response; import io.quarkus.it.mongodb.panache.person.PersonName; +import io.quarkus.it.mongodb.panache.reactive.person.ReactivePersonEntity; import io.quarkus.panache.common.Sort; import io.smallrye.mutiny.Uni; diff --git a/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/reactive/person/ReactivePersonRepositoryResource.java b/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/reactive/person/resources/ReactivePersonRepositoryResource.java similarity index 95% rename from integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/reactive/person/ReactivePersonRepositoryResource.java rename to integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/reactive/person/resources/ReactivePersonRepositoryResource.java index cf714dc509877..84e2a7854fd0d 100644 --- a/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/reactive/person/ReactivePersonRepositoryResource.java +++ b/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/reactive/person/resources/ReactivePersonRepositoryResource.java @@ -1,4 +1,4 @@ -package io.quarkus.it.mongodb.panache.reactive.person; +package io.quarkus.it.mongodb.panache.reactive.person.resources; import java.net.URI; import java.util.HashSet; @@ -11,6 +11,7 @@ import io.quarkus.it.mongodb.panache.person.Person; import io.quarkus.it.mongodb.panache.person.PersonName; +import io.quarkus.it.mongodb.panache.reactive.person.ReactivePersonRepository; import io.quarkus.panache.common.Sort; import io.smallrye.mutiny.Uni; diff --git a/integration-tests/mongodb-panache/src/test/java/io/quarkus/it/mongodb/panache/MongodbPanacheResourceTest.java b/integration-tests/mongodb-panache/src/test/java/io/quarkus/it/mongodb/panache/MongodbPanacheResourceTest.java index 2a12ff1967051..581ffd903442b 100644 --- a/integration-tests/mongodb-panache/src/test/java/io/quarkus/it/mongodb/panache/MongodbPanacheResourceTest.java +++ b/integration-tests/mongodb-panache/src/test/java/io/quarkus/it/mongodb/panache/MongodbPanacheResourceTest.java @@ -38,6 +38,11 @@ class MongodbPanacheResourceTest { private static final TypeRef> LIST_OF_PERSON_TYPE_REF = new TypeRef>() { }; + @Test + public void testAccessors() { + callEndpoint("/accessors"); + } + @Test public void testBookEntity() { callBookEndpoint("/books/entity"); @@ -195,6 +200,21 @@ private void callBookEndpoint(String endpoint) { Assertions.assertEquals(204, response.statusCode()); } + private void callEndpoint(String endpoint) { + RestAssured.defaultParser = Parser.JSON; + RestAssured.config + .objectMapperConfig(new ObjectMapperConfig().jackson2ObjectMapperFactory((type, s) -> new ObjectMapper() + .registerModule(new Jdk8Module()) + .registerModule(new JavaTimeModule()) + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS))); + + Response response = RestAssured + .given() + .get(endpoint) + .andReturn(); + Assertions.assertEquals(200, response.statusCode()); + } + private void callPersonEndpoint(String endpoint) { RestAssured.defaultParser = Parser.JSON; RestAssured.config From 5b1e89912f307f688e47d72d5dd70b331d3f57c0 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Thu, 10 Dec 2020 17:15:09 +0100 Subject: [PATCH 53/62] Remove the now useless em field from Hibernate Search ORM guide --- .../main/asciidoc/hibernate-search-orm-elasticsearch.adoc | 5 ----- 1 file changed, 5 deletions(-) diff --git a/docs/src/main/asciidoc/hibernate-search-orm-elasticsearch.adoc b/docs/src/main/asciidoc/hibernate-search-orm-elasticsearch.adoc index c6e8cf6fcdd98..20367b3aa6207 100644 --- a/docs/src/main/asciidoc/hibernate-search-orm-elasticsearch.adoc +++ b/docs/src/main/asciidoc/hibernate-search-orm-elasticsearch.adoc @@ -198,8 +198,6 @@ Just copy this content in the `LibraryResource` file created by the Maven `creat ---- package org.acme.hibernate.search.elasticsearch; -import javax.inject.Inject; -import javax.persistence.EntityManager; import javax.transaction.Transactional; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; @@ -217,9 +215,6 @@ import org.jboss.resteasy.annotations.jaxrs.PathParam; @Path("/library") public class LibraryResource { - @Inject - EntityManager em; - @PUT @Path("book") @Transactional From cade32d1388ba3f7420566344cc2c5f50ed1b8bc Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Thu, 10 Dec 2020 11:59:01 +0200 Subject: [PATCH 54/62] Ensure than an exception during serialization of json is properly handled Fixes: #13797 --- .../jackson/deployment/test/Cheese.java | 16 ++++++ .../deployment/test/CheeseEndpoint.java | 16 ++++++ .../test/ExceptionInWriterTest.java | 30 ++++++++++++ .../serialisers/JacksonMessageBodyWriter.java | 45 ++++++++++------- .../jsonb/deployment/test/Cheese.java | 20 ++++++++ .../jsonb/deployment/test/CheeseEndpoint.java | 16 ++++++ .../test/ExceptionInWriterTest.java | 30 ++++++++++++ .../serialisers/JsonbMessageBodyWriter.java | 49 ++++++++++++++++--- 8 files changed, 199 insertions(+), 23 deletions(-) create mode 100644 extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/Cheese.java create mode 100644 extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/CheeseEndpoint.java create mode 100644 extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/ExceptionInWriterTest.java create mode 100644 extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/deployment/src/test/java/io/quarkus/resteasy/reactive/jsonb/deployment/test/Cheese.java create mode 100644 extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/deployment/src/test/java/io/quarkus/resteasy/reactive/jsonb/deployment/test/CheeseEndpoint.java create mode 100644 extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/deployment/src/test/java/io/quarkus/resteasy/reactive/jsonb/deployment/test/ExceptionInWriterTest.java diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/Cheese.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/Cheese.java new file mode 100644 index 0000000000000..e45bc5adbffbc --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/Cheese.java @@ -0,0 +1,16 @@ +package io.quarkus.resteasy.reactive.jackson.deployment.test; + +public class Cheese { + + // private is what's causing the exception + private final String name; + + public Cheese(String name) { + this.name = name; + } + + @Override + public String toString() { + return "Cheese: " + name; + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/CheeseEndpoint.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/CheeseEndpoint.java new file mode 100644 index 0000000000000..7cc108929dff1 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/CheeseEndpoint.java @@ -0,0 +1,16 @@ +package io.quarkus.resteasy.reactive.jackson.deployment.test; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +@Path("cheese") +public class CheeseEndpoint { + + @Produces({ MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN }) + @GET + public Cheese get() { + return new Cheese("Morbier"); + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/ExceptionInWriterTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/ExceptionInWriterTest.java new file mode 100644 index 0000000000000..2ab992b10d2db --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/ExceptionInWriterTest.java @@ -0,0 +1,30 @@ +package io.quarkus.resteasy.reactive.jackson.deployment.test; + +import java.util.function.Supplier; + +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.restassured.RestAssured; + +public class ExceptionInWriterTest { + + @RegisterExtension + static QuarkusUnitTest test = new QuarkusUnitTest() + .setArchiveProducer(new Supplier() { + @Override + public JavaArchive get() { + return ShrinkWrap.create(JavaArchive.class) + .addClasses(Cheese.class, CheeseEndpoint.class); + } + }); + + @Test + public void test() { + RestAssured.with().header("Accept", "text/plain", "application/json").get("/cheese") + .then().statusCode(500); + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/JacksonMessageBodyWriter.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/JacksonMessageBodyWriter.java index a6c89d5236459..af6719505b7af 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/JacksonMessageBodyWriter.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/JacksonMessageBodyWriter.java @@ -16,16 +16,26 @@ import org.jboss.resteasy.reactive.server.spi.ServerRequestContext; import com.fasterxml.jackson.annotation.JsonView; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; public class JacksonMessageBodyWriter implements ServerMessageBodyWriter { private static final String JSON_VIEW_NAME = JsonView.class.getName(); - private final ObjectMapper mapper; + private final ObjectWriter writer; @Inject public JacksonMessageBodyWriter(ObjectMapper mapper) { - this.mapper = mapper; + // we don't want the ObjectWriter to close the stream automatically, as we want to handle closing manually at the proper points + if (mapper.getFactory().isEnabled(JsonGenerator.Feature.AUTO_CLOSE_TARGET)) { + JsonFactory jsonFactory = mapper.getFactory().copy(); + jsonFactory.configure(JsonGenerator.Feature.AUTO_CLOSE_TARGET, false); + this.writer = mapper.writer().with(jsonFactory); + } else { + this.writer = mapper.writer(); + } } @Override @@ -39,7 +49,7 @@ public void writeTo(Object o, Class type, Type genericType, Annotation[] anno if (o instanceof String) { // YUK: done in order to avoid adding extra quotes... entityStream.write(((String) o).getBytes()); } else { - entityStream.write(mapper.writeValueAsBytes(o)); + entityStream.write(writer.writeValueAsBytes(o)); } } @@ -50,22 +60,23 @@ public boolean isWriteable(Class type, ResteasyReactiveResourceInfo target, M @Override public void writeResponse(Object o, ServerRequestContext context) throws WebApplicationException, IOException { - try (OutputStream stream = context.getOrCreateOutputStream()) { - if (o instanceof String) { // YUK: done in order to avoid adding extra quotes... - stream.write(((String) o).getBytes()); - } else { - // First test the names to see if JsonView is used. We do this to avoid doing reflection for the common case - // where JsonView is not used - if (context.getResteasyReactiveResourceInfo().getMethodAnnotationNames().contains(JSON_VIEW_NAME)) { - Method method = context.getResteasyReactiveResourceInfo().getMethod(); - JsonView jsonView = method.getAnnotation(JsonView.class); - if ((jsonView != null) && (jsonView.value().length > 0)) { - mapper.writerWithView(jsonView.value()[0]).writeValue(stream, o); - return; - } + OutputStream stream = context.getOrCreateOutputStream(); + if (o instanceof String) { // YUK: done in order to avoid adding extra quotes... + stream.write(((String) o).getBytes()); + } else { + // First test the names to see if JsonView is used. We do this to avoid doing reflection for the common case + // where JsonView is not used + if (context.getResteasyReactiveResourceInfo().getMethodAnnotationNames().contains(JSON_VIEW_NAME)) { + Method method = context.getResteasyReactiveResourceInfo().getMethod(); + JsonView jsonView = method.getAnnotation(JsonView.class); + if ((jsonView != null) && (jsonView.value().length > 0)) { + writer.withView(jsonView.value()[0]).writeValue(stream, o); + return; } - mapper.writeValue(stream, o); } + writer.writeValue(stream, o); } + // we don't use try-with-resources because that results in writing to the http output without the exception mapping coming into play + stream.close(); } } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/deployment/src/test/java/io/quarkus/resteasy/reactive/jsonb/deployment/test/Cheese.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/deployment/src/test/java/io/quarkus/resteasy/reactive/jsonb/deployment/test/Cheese.java new file mode 100644 index 0000000000000..603cdb741be4f --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/deployment/src/test/java/io/quarkus/resteasy/reactive/jsonb/deployment/test/Cheese.java @@ -0,0 +1,20 @@ +package io.quarkus.resteasy.reactive.jsonb.deployment.test; + +public class Cheese { + + private final String name; + + public Cheese(String name) { + this.name = name; + } + + @Override + public String toString() { + return "Cheese: " + name; + } + + public String getName() { + // explicitly cause an exception to ensure that an exception during json writing is properly handled + throw new RuntimeException("Fake exception during serialization"); + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/deployment/src/test/java/io/quarkus/resteasy/reactive/jsonb/deployment/test/CheeseEndpoint.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/deployment/src/test/java/io/quarkus/resteasy/reactive/jsonb/deployment/test/CheeseEndpoint.java new file mode 100644 index 0000000000000..6955c935dc53f --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/deployment/src/test/java/io/quarkus/resteasy/reactive/jsonb/deployment/test/CheeseEndpoint.java @@ -0,0 +1,16 @@ +package io.quarkus.resteasy.reactive.jsonb.deployment.test; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +@Path("cheese") +public class CheeseEndpoint { + + @Produces({ MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN }) + @GET + public Cheese get() { + return new Cheese("Morbier"); + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/deployment/src/test/java/io/quarkus/resteasy/reactive/jsonb/deployment/test/ExceptionInWriterTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/deployment/src/test/java/io/quarkus/resteasy/reactive/jsonb/deployment/test/ExceptionInWriterTest.java new file mode 100644 index 0000000000000..a14c804de8f01 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/deployment/src/test/java/io/quarkus/resteasy/reactive/jsonb/deployment/test/ExceptionInWriterTest.java @@ -0,0 +1,30 @@ +package io.quarkus.resteasy.reactive.jsonb.deployment.test; + +import java.util.function.Supplier; + +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.restassured.RestAssured; + +public class ExceptionInWriterTest { + + @RegisterExtension + static QuarkusUnitTest test = new QuarkusUnitTest() + .setArchiveProducer(new Supplier() { + @Override + public JavaArchive get() { + return ShrinkWrap.create(JavaArchive.class) + .addClasses(Cheese.class, CheeseEndpoint.class); + } + }); + + @Test + public void test() { + RestAssured.with().header("Accept", "text/plain", "application/json").get("/cheese") + .then().statusCode(500); + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/runtime/src/main/java/io/quarkus/resteasy/reactive/jsonb/runtime/serialisers/JsonbMessageBodyWriter.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/runtime/src/main/java/io/quarkus/resteasy/reactive/jsonb/runtime/serialisers/JsonbMessageBodyWriter.java index ce4f3438eb72c..eaa08beacd003 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/runtime/src/main/java/io/quarkus/resteasy/reactive/jsonb/runtime/serialisers/JsonbMessageBodyWriter.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/runtime/src/main/java/io/quarkus/resteasy/reactive/jsonb/runtime/serialisers/JsonbMessageBodyWriter.java @@ -46,13 +46,50 @@ public boolean isWriteable(Class type, ResteasyReactiveResourceInfo target, M @Override public void writeResponse(Object o, ServerRequestContext context) throws WebApplicationException, IOException { - try (OutputStream stream = context.getOrCreateOutputStream()) { - if (o instanceof String) { // YUK: done in order to avoid adding extra quotes... - stream.write(((String) o).getBytes()); - } else { - json.toJson(o, stream); - } + OutputStream originalStream = context.getOrCreateOutputStream(); + OutputStream stream = new NoopCloseAndFlushOutputStream(originalStream); + if (o instanceof String) { // YUK: done in order to avoid adding extra quotes... + stream.write(((String) o).getBytes()); + } else { + json.toJson(o, stream); + } + // we don't use try-with-resources because that results in writing to the http output without the exception mapping coming into play + originalStream.close(); + } + + /** + * This class is needed because Yasson doesn't give us a way to control if the output stream is going to be closed or not + */ + private static class NoopCloseAndFlushOutputStream extends OutputStream { + private final OutputStream delegate; + + public NoopCloseAndFlushOutputStream(OutputStream delegate) { + this.delegate = delegate; + } + + @Override + public void flush() { + } + @Override + public void close() { + + } + + @Override + public void write(int b) throws IOException { + delegate.write(b); + } + + @Override + public void write(byte[] b) throws IOException { + delegate.write(b); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + delegate.write(b, off, len); + } } } From d3eb31ea8ae5cbafd9976510ef9e8e965b75dba9 Mon Sep 17 00:00:00 2001 From: Ladislav Thon Date: Mon, 30 Nov 2020 10:03:06 +0100 Subject: [PATCH 55/62] Allow repeatable interceptor bindings --- docs/src/main/asciidoc/cdi-reference.adoc | 49 ++++++ .../quarkus/arc/processor/BeanDeployment.java | 45 ++++-- .../io/quarkus/arc/processor/BeanInfo.java | 7 +- .../quarkus/arc/processor/Interceptors.java | 18 +-- .../io/quarkus/arc/processor/Methods.java | 2 +- ...ritedRepeatableInterceptorBindingTest.java | 146 ++++++++++++++++++ .../RepeatableInterceptorBindingTest.java | 142 +++++++++++++++++ ...torBindingWithNonbindingAttributeTest.java | 144 +++++++++++++++++ 8 files changed, 527 insertions(+), 26 deletions(-) create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/bindings/repeatable/InheritedRepeatableInterceptorBindingTest.java create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/bindings/repeatable/RepeatableInterceptorBindingTest.java create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/bindings/repeatable/RepeatableInterceptorBindingWithNonbindingAttributeTest.java diff --git a/docs/src/main/asciidoc/cdi-reference.adoc b/docs/src/main/asciidoc/cdi-reference.adoc index e0669b67c423b..832dabe30339b 100644 --- a/docs/src/main/asciidoc/cdi-reference.adoc +++ b/docs/src/main/asciidoc/cdi-reference.adoc @@ -735,6 +735,55 @@ class SharedService { <2> `@Lock(Lock.Type.READ)` overrides the value specified at class level. It means that any number of clients can invoke the method concurrently, unless the bean instance is locked by `@Lock(Lock.Type.WRITE)`. <3> You can also specify the "wait time". If it's not possible to acquire the lock in the given time a `LockException` is thrown. +=== Repeatable interceptor bindings + +Quarkus has limited support for `@Repeatable` interceptor binding annotations. + +When binding an interceptor to a component, you can declare multiple `@Repeatable` annotations on methods. +Repeatable interceptor bindings declared on classes and stereotypes are not supported, because there are some open questions around interactions with the Interceptors specification. +This might be added in the future. + +As an example, suppose we have an interceptor that clears a cache. +The corresponding interceptor binding would be called `@CacheInvalidateAll` and would be declared as `@Repeatable`. +If we wanted to clear two caches at the same time, we would add `@CacheInvalidateAll` twice: + +[source,java] +---- +@ApplicationScoped +class CachingService { + @CacheInvalidateAll(cacheName = "foo") + @CacheInvalidateAll(cacheName = "bar") + void heavyComputation() { + // ... + // some computation that updates a lot of data + // and requires 2 caches to be invalidated + // ... + } +} +---- + +This is how interceptors are used. +What about creating an interceptor? + +When declaring interceptor bindings of an interceptor, you can add multiple `@Repeatable` annotations to the interceptor class as usual. +This is useless when the annotation members are `@Nonbinding`, as would be the case for the `@Cached` annotation, but is important otherwise. + +For example, suppose we have an interceptor that can automatically log method invocations to certain targets. +The interceptor binding annotation `@Logged` would have a member called `target`, which specifies where to store the log. +Our implementation could be restricted to console logging and file logging: + +[source,java] +---- +@Interceptor +@Logged(target = "console") +@Logged(target = "file") +class NaiveLoggingInterceptor { + // ... +} +---- + +Other interceptors could be provided to log method invocations to different targets. + [[build_time_apis]] == Build Time Extensions diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java index b4f8050b01601..6a7f4dbecc6a7 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java @@ -66,6 +66,8 @@ public class BeanDeployment { private final Map interceptorBindings; + private final Map repeatingInterceptorBindingAnnotations; + private final Map> nonBindingFields; private final Map> transitiveInterceptorBindings; @@ -151,6 +153,7 @@ public class BeanDeployment { } } } + this.repeatingInterceptorBindingAnnotations = findContainerAnnotations(interceptorBindings, this.beanArchiveIndex); buildContextPut(Key.INTERCEPTOR_BINDINGS.asString(), Collections.unmodifiableMap(interceptorBindings)); this.stereotypes = findStereotypes(this.beanArchiveIndex, interceptorBindings, beanDefiningAnnotations, customContexts, @@ -411,22 +414,40 @@ ClassInfo getQualifier(DotName name) { /** * Extracts qualifiers from given annotation instance. * This returns a collection because in case of repeating qualifiers there can be multiple. - * For most instances this will be a singleton instance (if given annotatation is a qualifier) or an empty list for + * For most instances this will be a singleton instance (if given annotation is a qualifier) or an empty list for * cases where the annotation is not a qualifier. * - * @param annotation instance to be inspected + * @param annotation annotation to be inspected * @return a collection of qualifiers or an empty collection */ Collection extractQualifiers(AnnotationInstance annotation) { + return extractAnnotations(annotation, qualifiers, repeatingQualifierAnnotations); + } + + /** + * Extracts interceptor bindings from given annotation instance. + * This returns a collection because in case of repeating interceptor bindings there can be multiple. + * For most instances this will be a singleton instance (if given annotation is an interceptor binding) or + * an empty list for cases where the annotation is not an interceptor binding. + * + * @param annotation annotation to be inspected + * @return a collection of interceptor bindings or an empty collection + */ + Collection extractInterceptorBindings(AnnotationInstance annotation) { + return extractAnnotations(annotation, interceptorBindings, repeatingInterceptorBindingAnnotations); + } + + private static Collection extractAnnotations(AnnotationInstance annotation, + Map singulars, Map repeatables) { DotName annotationName = annotation.name(); - if (qualifiers.get(annotationName) != null) { + if (singulars.get(annotationName) != null) { return Collections.singleton(annotation); } else { - if (repeatingQualifierAnnotations.get(annotationName) != null) { - // container annotation, we need to extract actual qualifiers + if (repeatables.get(annotationName) != null) { + // repeatable, we need to extract actual annotations return new ArrayList<>(Arrays.asList(annotation.value().asNestedArray())); } else { - // neither qualifier, nor container annotation, return empty collection + // neither singular nor repeatable, return empty collection return Collections.emptyList(); } } @@ -500,13 +521,13 @@ private static Map findQualifiers(IndexView index) { return qualifiers; } - private static Map findContainerAnnotations(Map qualifiers, IndexView index) { + private static Map findContainerAnnotations(Map annotations, IndexView index) { Map containerAnnotations = new HashMap<>(); - for (ClassInfo qualifier : qualifiers.values()) { - AnnotationInstance instance = qualifier.classAnnotation(DotNames.REPEATABLE); - if (instance != null) { - DotName annotationName = instance.value().asClass().name(); - containerAnnotations.put(annotationName, getClassByName(index, annotationName)); + for (ClassInfo annotation : annotations.values()) { + AnnotationInstance repeatableMetaAnnotation = annotation.classAnnotation(DotNames.REPEATABLE); + if (repeatableMetaAnnotation != null) { + DotName containerAnnotationName = repeatableMetaAnnotation.value().asClass().name(); + containerAnnotations.put(containerAnnotationName, getClassByName(index, containerAnnotationName)); } } return containerAnnotations; diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanInfo.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanInfo.java index c66364b69a128..24fc03cd7f540 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanInfo.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanInfo.java @@ -466,7 +466,7 @@ private void addClassLevelBindings(ClassInfo classInfo, Collection beanDeployment.getInterceptorBinding(a.name()) != null && bindings.stream().noneMatch(e -> e.name().equals(a.name()))) - .forEach(a -> bindings.add(a)); + .forEach(bindings::add); if (classInfo.superClassType() != null && !classInfo.superClassType().name().equals(DotNames.OBJECT)) { ClassInfo superClass = getClassByName(beanDeployment.getBeanArchiveIndex(), classInfo.superName()); if (superClass != null) { @@ -485,9 +485,8 @@ private void addConstructorLevelBindings(ClassInfo classInfo, Collection beanDeployment.getInterceptorBinding(a.name()) != null - && bindings.stream().noneMatch(e -> e.name().equals(a.name()))) - .forEach(a -> bindings.add(a)); + .flatMap(a -> beanDeployment.extractInterceptorBindings(a).stream()) + .forEach(bindings::add); } } diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Interceptors.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Interceptors.java index 182121277f344..474ad3ef79280 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Interceptors.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Interceptors.java @@ -27,15 +27,15 @@ static InterceptorInfo createInterceptor(ClassInfo interceptorClass, BeanDeploym Integer priority = 0; boolean priorityDeclared = false; for (AnnotationInstance annotation : store.getAnnotations(interceptorClass)) { - if (beanDeployment.getInterceptorBinding(annotation.name()) != null) { - bindings.add(annotation); - // can also be a transitive binding - Set transitiveInterceptorBindings = beanDeployment - .getTransitiveInterceptorBindings(annotation.name()); - if (transitiveInterceptorBindings != null) { - bindings.addAll(transitiveInterceptorBindings); - } - } else if (annotation.name().equals(DotNames.PRIORITY)) { + bindings.addAll(beanDeployment.extractInterceptorBindings(annotation)); + // can also be a transitive binding + Set transitiveInterceptorBindings = beanDeployment + .getTransitiveInterceptorBindings(annotation.name()); + if (transitiveInterceptorBindings != null) { + bindings.addAll(transitiveInterceptorBindings); + } + + if (annotation.name().equals(DotNames.PRIORITY)) { priority = annotation.value().asInt(); priorityDeclared = true; } diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Methods.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Methods.java index 2d65f3eb01fb4..2a9166dade933 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Methods.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Methods.java @@ -162,7 +162,7 @@ static Set addInterceptedMethodCandidates(BeanDeployment beanDeploym } else { Collection methodAnnnotations = beanDeployment.getAnnotations(method); List methodLevelBindings = methodAnnnotations.stream() - .filter(a -> beanDeployment.getInterceptorBinding(a.name()) != null) + .flatMap(a -> beanDeployment.extractInterceptorBindings(a).stream()) .collect(Collectors.toList()); merged.addAll(methodLevelBindings); for (AnnotationInstance classLevelBinding : classLevelBindings) { diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/bindings/repeatable/InheritedRepeatableInterceptorBindingTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/bindings/repeatable/InheritedRepeatableInterceptorBindingTest.java new file mode 100644 index 0000000000000..0fe18aab02fef --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/bindings/repeatable/InheritedRepeatableInterceptorBindingTest.java @@ -0,0 +1,146 @@ +package io.quarkus.arc.test.interceptors.bindings.repeatable; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.test.ArcTestContainer; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.concurrent.atomic.AtomicBoolean; +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import javax.enterprise.context.ApplicationScoped; +import javax.interceptor.AroundConstruct; +import javax.interceptor.AroundInvoke; +import javax.interceptor.Interceptor; +import javax.interceptor.InterceptorBinding; +import javax.interceptor.InvocationContext; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +/** + * Tests usage of inherited repeating interceptor binding. + */ +public class InheritedRepeatableInterceptorBindingTest { + @RegisterExtension + public ArcTestContainer container = new ArcTestContainer(MyBinding.class, MyBinding.List.class, + SuperclassWithMethodLevelBindings.class, MethodInterceptedBean.class, IncrementingInterceptor.class); + + @BeforeEach + public void setUp() { + IncrementingInterceptor.AROUND_CONSTRUCT.set(false); + IncrementingInterceptor.POST_CONSTRUCT.set(false); + IncrementingInterceptor.PRE_DESTROY.set(false); + } + + @Test + public void methodLevelInterceptor() { + MethodInterceptedBean bean = Arc.container().instance(MethodInterceptedBean.class).get(); + + assertEquals(10, bean.foo()); + assertEquals(21, bean.foobar()); + assertEquals(30, bean.foobaz()); + assertEquals(41, bean.foobarbaz()); + assertEquals(50, bean.nonannotated()); + + // interceptor bindings are not inherited for constructors + assertFalse(IncrementingInterceptor.AROUND_CONSTRUCT.get()); + + // post-construct and pre-destroy interceptors aren't called, + // because there are no class-level interceptor bindings + assertFalse(IncrementingInterceptor.POST_CONSTRUCT.get()); + assertFalse(IncrementingInterceptor.PRE_DESTROY.get()); + } + + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @Retention(RetentionPolicy.RUNTIME) + @Repeatable(MyBinding.List.class) + @Inherited + @InterceptorBinding + @interface MyBinding { + String value(); + + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @Retention(RetentionPolicy.RUNTIME) + @Inherited + @interface List { + MyBinding[] value(); + } + } + + @ApplicationScoped + static class SuperclassWithMethodLevelBindings { + @MyBinding("foo") + @MyBinding("bar") + public SuperclassWithMethodLevelBindings() { + } + + @MyBinding("foo") + public int foo() { + return 10; + } + + @MyBinding("foo") + @MyBinding("bar") + public int foobar() { + return 20; + } + + @MyBinding("foo") + @MyBinding("baz") + public int foobaz() { + return 30; + } + + @MyBinding("foo") + @MyBinding("bar") + @MyBinding("baz") + public int foobarbaz() { + return 40; + } + + public int nonannotated() { + return 50; + } + } + + @ApplicationScoped + static class MethodInterceptedBean extends SuperclassWithMethodLevelBindings { + } + + @Interceptor + @MyBinding("foo") + @MyBinding("bar") + static class IncrementingInterceptor { + static final AtomicBoolean AROUND_CONSTRUCT = new AtomicBoolean(false); + static final AtomicBoolean POST_CONSTRUCT = new AtomicBoolean(false); + static final AtomicBoolean PRE_DESTROY = new AtomicBoolean(false); + + @AroundConstruct + public void aroundConstruct(InvocationContext ctx) throws Exception { + AROUND_CONSTRUCT.set(true); + ctx.proceed(); + } + + @PostConstruct + public void postConstruct(InvocationContext ctx) { + POST_CONSTRUCT.set(true); + } + + @PreDestroy + public void preDestroy(InvocationContext ctx) { + PRE_DESTROY.set(true); + } + + @AroundInvoke + public Object intercept(InvocationContext ctx) throws Exception { + return ((Integer) ctx.proceed()) + 1; + } + } +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/bindings/repeatable/RepeatableInterceptorBindingTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/bindings/repeatable/RepeatableInterceptorBindingTest.java new file mode 100644 index 0000000000000..938f60e60f899 --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/bindings/repeatable/RepeatableInterceptorBindingTest.java @@ -0,0 +1,142 @@ +package io.quarkus.arc.test.interceptors.bindings.repeatable; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.test.ArcTestContainer; +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.concurrent.atomic.AtomicBoolean; +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import javax.enterprise.context.ApplicationScoped; +import javax.interceptor.AroundConstruct; +import javax.interceptor.AroundInvoke; +import javax.interceptor.Interceptor; +import javax.interceptor.InterceptorBinding; +import javax.interceptor.InvocationContext; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +/** + * Tests repeating interceptor binding usage. + */ +public class RepeatableInterceptorBindingTest { + @RegisterExtension + public ArcTestContainer container = new ArcTestContainer(MyBinding.class, MyBinding.List.class, + MethodInterceptedBean.class, IncrementingInterceptor.class); + + @BeforeEach + public void setUp() { + IncrementingInterceptor.AROUND_CONSTRUCT.set(false); + IncrementingInterceptor.POST_CONSTRUCT.set(false); + IncrementingInterceptor.PRE_DESTROY.set(false); + } + + @Test + public void methodLevelInterceptor() { + MethodInterceptedBean bean = Arc.container().instance(MethodInterceptedBean.class).get(); + + assertEquals(10, bean.foo()); + assertEquals(21, bean.foobar()); + assertEquals(30, bean.foobaz()); + assertEquals(41, bean.foobarbaz()); + assertEquals(50, bean.nonannotated()); + + // the instance is created lazily (the bean is @ApplicationScoped), + // so the around construct interceptor is also called lazily, + // when the first method call is made on the client proxy + assertTrue(IncrementingInterceptor.AROUND_CONSTRUCT.get()); + + // post-construct and pre-destroy interceptors aren't called, + // because there are no class-level interceptor bindings + assertFalse(IncrementingInterceptor.POST_CONSTRUCT.get()); + assertFalse(IncrementingInterceptor.PRE_DESTROY.get()); + } + + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @Retention(RetentionPolicy.RUNTIME) + @Repeatable(MyBinding.List.class) + @InterceptorBinding + @interface MyBinding { + String value(); + + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @Retention(RetentionPolicy.RUNTIME) + @interface List { + MyBinding[] value(); + } + } + + @ApplicationScoped + static class MethodInterceptedBean { + @MyBinding("foo") + @MyBinding("bar") + public MethodInterceptedBean() { + } + + @MyBinding("foo") + public int foo() { + return 10; + } + + @MyBinding("foo") + @MyBinding("bar") + public int foobar() { + return 20; + } + + @MyBinding("foo") + @MyBinding("baz") + public int foobaz() { + return 30; + } + + @MyBinding("foo") + @MyBinding("bar") + @MyBinding("baz") + public int foobarbaz() { + return 40; + } + + public int nonannotated() { + return 50; + } + } + + @Interceptor + @MyBinding("foo") + @MyBinding("bar") + static class IncrementingInterceptor { + static final AtomicBoolean AROUND_CONSTRUCT = new AtomicBoolean(false); + static final AtomicBoolean POST_CONSTRUCT = new AtomicBoolean(false); + static final AtomicBoolean PRE_DESTROY = new AtomicBoolean(false); + + @AroundConstruct + public void aroundConstruct(InvocationContext ctx) throws Exception { + AROUND_CONSTRUCT.set(true); + ctx.proceed(); + } + + @PostConstruct + public void postConstruct(InvocationContext ctx) { + POST_CONSTRUCT.set(true); + } + + @PreDestroy + public void preDestroy(InvocationContext ctx) { + PRE_DESTROY.set(true); + } + + @AroundInvoke + public Object intercept(InvocationContext ctx) throws Exception { + return ((Integer) ctx.proceed()) + 1; + } + } +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/bindings/repeatable/RepeatableInterceptorBindingWithNonbindingAttributeTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/bindings/repeatable/RepeatableInterceptorBindingWithNonbindingAttributeTest.java new file mode 100644 index 0000000000000..1659dc6b138b9 --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/bindings/repeatable/RepeatableInterceptorBindingWithNonbindingAttributeTest.java @@ -0,0 +1,144 @@ +package io.quarkus.arc.test.interceptors.bindings.repeatable; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.test.ArcTestContainer; +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.concurrent.atomic.AtomicBoolean; +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.util.Nonbinding; +import javax.interceptor.AroundConstruct; +import javax.interceptor.AroundInvoke; +import javax.interceptor.Interceptor; +import javax.interceptor.InterceptorBinding; +import javax.interceptor.InvocationContext; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +/** + * Tests repeating interceptor binding usage, with a nonbinding annotation attribute. + */ +public class RepeatableInterceptorBindingWithNonbindingAttributeTest { + @RegisterExtension + public ArcTestContainer container = new ArcTestContainer(MyBinding.class, MyBinding.List.class, + MethodInterceptedBean.class, IncrementingInterceptor.class); + + @BeforeEach + public void setUp() { + IncrementingInterceptor.AROUND_CONSTRUCT.set(false); + IncrementingInterceptor.POST_CONSTRUCT.set(false); + IncrementingInterceptor.PRE_DESTROY.set(false); + } + + @Test + public void methodLevelInterceptor() { + MethodInterceptedBean bean = Arc.container().instance(MethodInterceptedBean.class).get(); + + assertEquals(11, bean.foo()); + assertEquals(21, bean.foobar()); + assertEquals(31, bean.foobaz()); + assertEquals(41, bean.foobarbaz()); + assertEquals(50, bean.nonannotated()); + + // the instance is created lazily (the bean is @ApplicationScoped), + // so the around construct interceptor is also called lazily, + // when the first method call is made on the client proxy + assertTrue(IncrementingInterceptor.AROUND_CONSTRUCT.get()); + + // post-construct and pre-destroy interceptors aren't called, + // because there are no class-level interceptor bindings + assertFalse(IncrementingInterceptor.POST_CONSTRUCT.get()); + assertFalse(IncrementingInterceptor.PRE_DESTROY.get()); + } + + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @Retention(RetentionPolicy.RUNTIME) + @Repeatable(MyBinding.List.class) + @InterceptorBinding + @interface MyBinding { + @Nonbinding + String value(); + + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @Retention(RetentionPolicy.RUNTIME) + @interface List { + MyBinding[] value(); + } + } + + @ApplicationScoped + static class MethodInterceptedBean { + @MyBinding("foo") + @MyBinding("bar") + public MethodInterceptedBean() { + } + + @MyBinding("foo") + public int foo() { + return 10; + } + + @MyBinding("foo") + @MyBinding("bar") + public int foobar() { + return 20; + } + + @MyBinding("foo") + @MyBinding("baz") + public int foobaz() { + return 30; + } + + @MyBinding("foo") + @MyBinding("bar") + @MyBinding("baz") + public int foobarbaz() { + return 40; + } + + public int nonannotated() { + return 50; + } + } + + @Interceptor + @MyBinding("foo") + @MyBinding("bar") + static class IncrementingInterceptor { + static final AtomicBoolean AROUND_CONSTRUCT = new AtomicBoolean(false); + static final AtomicBoolean POST_CONSTRUCT = new AtomicBoolean(false); + static final AtomicBoolean PRE_DESTROY = new AtomicBoolean(false); + + @AroundConstruct + public void aroundConstruct(InvocationContext ctx) throws Exception { + AROUND_CONSTRUCT.set(true); + ctx.proceed(); + } + + @PostConstruct + public void postConstruct(InvocationContext ctx) { + POST_CONSTRUCT.set(true); + } + + @PreDestroy + public void preDestroy(InvocationContext ctx) { + PRE_DESTROY.set(true); + } + + @AroundInvoke + public Object intercept(InvocationContext ctx) throws Exception { + return ((Integer) ctx.proceed()) + 1; + } + } +} From 860dee964de28109840dfbbda1cd0f27e928ffce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Mon, 7 Dec 2020 10:55:05 +0100 Subject: [PATCH 56/62] Upgrade to Hibernate Search 6.0.0.Final MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yoann Rodière --- bom/application/pom.xml | 2 +- .../runtime/HibernateSearchElasticsearchRecorder.java | 10 ++++------ ...earchElasticsearchRuntimeConfigPersistenceUnit.java | 4 ++-- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 767328864fd8e..8b3ae7c810bd2 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -93,7 +93,7 @@ 5.4.25.Final 1.0.0.Beta1 6.1.6.Final - 6.0.0.CR2 + 6.0.0.Final 5.10.6.Final 1.1.1.Final 1.9 diff --git a/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRecorder.java b/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRecorder.java index 6ab0b65104b6f..1fe3dd5d74100 100644 --- a/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRecorder.java +++ b/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRecorder.java @@ -22,10 +22,9 @@ import org.hibernate.search.mapper.orm.Search; import org.hibernate.search.mapper.orm.bootstrap.spi.HibernateOrmIntegrationBooter; import org.hibernate.search.mapper.orm.cfg.HibernateOrmMapperSettings; -import org.hibernate.search.mapper.orm.cfg.spi.HibernateOrmMapperSpiSettings; -import org.hibernate.search.mapper.orm.cfg.spi.HibernateOrmReflectionStrategyName; import org.hibernate.search.mapper.orm.mapping.SearchMapping; import org.hibernate.search.mapper.orm.session.SearchSession; +import org.hibernate.search.util.common.reflect.spi.ValueReadHandleFactory; import io.quarkus.arc.Arc; import io.quarkus.hibernate.orm.runtime.PersistenceUnitUtil; @@ -121,9 +120,6 @@ private HibernateSearchIntegrationStaticInitListener( @Override public void contributeBootProperties(BiConsumer propertyCollector) { - addConfig(propertyCollector, HibernateOrmMapperSpiSettings.REFLECTION_STRATEGY, - HibernateOrmReflectionStrategyName.JAVA_LANG_REFLECT); - addConfig(propertyCollector, EngineSettings.BACKGROUND_FAILURE_HANDLER, buildTimeConfig.backgroundFailureHandler); @@ -139,7 +135,9 @@ public void contributeBootProperties(BiConsumer propertyCollecto @Override public void onMetadataInitialized(Metadata metadata, BootstrapContext bootstrapContext, BiConsumer propertyCollector) { - HibernateOrmIntegrationBooter booter = HibernateOrmIntegrationBooter.create(metadata, bootstrapContext); + HibernateOrmIntegrationBooter booter = HibernateOrmIntegrationBooter.builder(metadata, bootstrapContext) + .valueReadHandleFactory(ValueReadHandleFactory.usingJavaLangReflect()) + .build(); booter.preBoot(propertyCollector); } diff --git a/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRuntimeConfigPersistenceUnit.java b/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRuntimeConfigPersistenceUnit.java index adb103bcdd301..b05d16fbb1273 100644 --- a/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRuntimeConfigPersistenceUnit.java +++ b/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRuntimeConfigPersistenceUnit.java @@ -8,11 +8,11 @@ import java.util.OptionalInt; import org.hibernate.search.backend.elasticsearch.index.IndexStatus; +import org.hibernate.search.engine.cfg.spi.ParseUtils; import org.hibernate.search.mapper.orm.automaticindexing.session.AutomaticIndexingSynchronizationStrategyNames; import org.hibernate.search.mapper.orm.schema.management.SchemaManagementStrategyName; import org.hibernate.search.mapper.orm.search.loading.EntityLoadingCacheLookupStrategy; import org.hibernate.search.util.common.SearchException; -import org.hibernate.search.util.common.impl.StringHelper; import io.quarkus.runtime.annotations.ConfigDocMapKey; import io.quarkus.runtime.annotations.ConfigDocSection; @@ -163,7 +163,7 @@ public enum ElasticsearchClientProtocol { HTTPS("https"); public static ElasticsearchClientProtocol of(String value) { - return StringHelper.parseDiscreteValues( + return ParseUtils.parseDiscreteValues( values(), ElasticsearchClientProtocol::getHibernateSearchString, (invalidValue, validValues) -> new SearchException( From 7664478b4b03619d50347c84ef0272bdd50aa1de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Mon, 7 Dec 2020 11:19:13 +0100 Subject: [PATCH 57/62] Rely on SPI for Hibernate Search code substitutions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yoann Rodière --- ...HibernateOrmIntegrationBooterBehavior.java | 19 +++++++++++++++++ ...ute_HibernateOrmIntegrationBooterImpl.java | 21 ------------------- 2 files changed, 19 insertions(+), 21 deletions(-) create mode 100644 extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/graal/Substitute_HibernateOrmIntegrationBooterBehavior.java delete mode 100644 extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/graal/Substitute_HibernateOrmIntegrationBooterImpl.java diff --git a/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/graal/Substitute_HibernateOrmIntegrationBooterBehavior.java b/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/graal/Substitute_HibernateOrmIntegrationBooterBehavior.java new file mode 100644 index 0000000000000..28782cee1b881 --- /dev/null +++ b/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/graal/Substitute_HibernateOrmIntegrationBooterBehavior.java @@ -0,0 +1,19 @@ +package io.quarkus.hibernate.search.orm.elasticsearch.runtime.graal; + +import org.hibernate.search.mapper.orm.bootstrap.spi.HibernateOrmIntegrationBooterBehavior; + +import com.oracle.svm.core.annotate.Substitute; +import com.oracle.svm.core.annotate.TargetClass; + +/** + * Disallow the first phase of boot during runtime init so that bootstrap code can be DCEd. + */ +@TargetClass(className = "org.hibernate.search.mapper.orm.bootstrap.spi.HibernateOrmIntegrationBooterBehavior") +final class Substitute_HibernateOrmIntegrationBooterBehavior { + + @Substitute + public static T bootFirstPhase(HibernateOrmIntegrationBooterBehavior.BootPhase phase) { + throw new IllegalStateException("The first phase of Hibernate Search's boot should have occurred during static init."); + } + +} diff --git a/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/graal/Substitute_HibernateOrmIntegrationBooterImpl.java b/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/graal/Substitute_HibernateOrmIntegrationBooterImpl.java deleted file mode 100644 index 25e8b1c42e1aa..0000000000000 --- a/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/graal/Substitute_HibernateOrmIntegrationBooterImpl.java +++ /dev/null @@ -1,21 +0,0 @@ -package io.quarkus.hibernate.search.orm.elasticsearch.runtime.graal; - -import com.oracle.svm.core.annotate.Substitute; -import com.oracle.svm.core.annotate.TargetClass; - -/** - * Force two phase-boot so that bootstrap code can be DCEd. - */ -@TargetClass(className = "org.hibernate.search.mapper.orm.bootstrap.impl.HibernateOrmIntegrationBooterImpl") -final class Substitute_HibernateOrmIntegrationBooterImpl { - - @Substitute - private HibernateOrmIntegrationPartialBuildState doBootFirstPhase() { - throw new IllegalStateException("Partial build state should have been generated during the static init phase."); - } - - @TargetClass(className = "org.hibernate.search.mapper.orm.bootstrap.impl.HibernateOrmIntegrationBooterImpl", innerClass = "HibernateOrmIntegrationPartialBuildState") - final static class HibernateOrmIntegrationPartialBuildState { - - } -} From 2af5eea1a5d26422d965da54151cdcaba464493e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Mon, 7 Dec 2020 11:22:28 +0100 Subject: [PATCH 58/62] Rely on SPIs to declare necessary reflection for Hibernate Search MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yoann Rodière --- .../elasticsearch/HibernateSearchClasses.java | 63 +------------------ 1 file changed, 3 insertions(+), 60 deletions(-) diff --git a/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/HibernateSearchClasses.java b/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/HibernateSearchClasses.java index cb4d75b9db886..3f5b5caadd6a4 100644 --- a/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/HibernateSearchClasses.java +++ b/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/HibernateSearchClasses.java @@ -1,35 +1,9 @@ package io.quarkus.hibernate.search.orm.elasticsearch; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; -import org.hibernate.search.backend.elasticsearch.lowlevel.index.aliases.impl.IndexAliasDefinition; -import org.hibernate.search.backend.elasticsearch.lowlevel.index.aliases.impl.IndexAliasDefinitionJsonAdapterFactory; -import org.hibernate.search.backend.elasticsearch.lowlevel.index.analysis.impl.AnalysisDefinition; -import org.hibernate.search.backend.elasticsearch.lowlevel.index.analysis.impl.AnalysisDefinitionJsonAdapterFactory; -import org.hibernate.search.backend.elasticsearch.lowlevel.index.analysis.impl.AnalyzerDefinition; -import org.hibernate.search.backend.elasticsearch.lowlevel.index.analysis.impl.AnalyzerDefinitionJsonAdapterFactory; -import org.hibernate.search.backend.elasticsearch.lowlevel.index.analysis.impl.CharFilterDefinition; -import org.hibernate.search.backend.elasticsearch.lowlevel.index.analysis.impl.NormalizerDefinition; -import org.hibernate.search.backend.elasticsearch.lowlevel.index.analysis.impl.NormalizerDefinitionJsonAdapterFactory; -import org.hibernate.search.backend.elasticsearch.lowlevel.index.analysis.impl.TokenFilterDefinition; -import org.hibernate.search.backend.elasticsearch.lowlevel.index.analysis.impl.TokenizerDefinition; -import org.hibernate.search.backend.elasticsearch.lowlevel.index.mapping.impl.AbstractTypeMapping; -import org.hibernate.search.backend.elasticsearch.lowlevel.index.mapping.impl.DynamicTemplate; -import org.hibernate.search.backend.elasticsearch.lowlevel.index.mapping.impl.DynamicTemplateJsonAdapterFactory; -import org.hibernate.search.backend.elasticsearch.lowlevel.index.mapping.impl.DynamicType; -import org.hibernate.search.backend.elasticsearch.lowlevel.index.mapping.impl.FormatJsonAdapter; -import org.hibernate.search.backend.elasticsearch.lowlevel.index.mapping.impl.NamedDynamicTemplate; -import org.hibernate.search.backend.elasticsearch.lowlevel.index.mapping.impl.NamedDynamicTemplateJsonAdapterFactory; -import org.hibernate.search.backend.elasticsearch.lowlevel.index.mapping.impl.PropertyMapping; -import org.hibernate.search.backend.elasticsearch.lowlevel.index.mapping.impl.PropertyMappingJsonAdapterFactory; -import org.hibernate.search.backend.elasticsearch.lowlevel.index.mapping.impl.RootTypeMapping; -import org.hibernate.search.backend.elasticsearch.lowlevel.index.mapping.impl.RootTypeMappingJsonAdapterFactory; -import org.hibernate.search.backend.elasticsearch.lowlevel.index.mapping.impl.RoutingType; -import org.hibernate.search.backend.elasticsearch.lowlevel.index.mapping.impl.RoutingTypeJsonAdapter; -import org.hibernate.search.backend.elasticsearch.lowlevel.index.settings.impl.Analysis; -import org.hibernate.search.backend.elasticsearch.lowlevel.index.settings.impl.IndexSettings; +import org.hibernate.search.backend.elasticsearch.gson.spi.GsonClasses; import org.hibernate.search.mapper.pojo.mapping.definition.annotation.Indexed; import org.hibernate.search.mapper.pojo.mapping.definition.annotation.processing.TypeMapping; import org.jboss.jandex.DotName; @@ -44,39 +18,8 @@ class HibernateSearchClasses { static final List GSON_CLASSES = new ArrayList<>(); static { - List> publicGsonClasses = Arrays.asList( - AbstractTypeMapping.class, - DynamicType.class, - FormatJsonAdapter.class, - RoutingTypeJsonAdapter.class, - PropertyMapping.class, - PropertyMappingJsonAdapterFactory.class, - RootTypeMapping.class, - RootTypeMappingJsonAdapterFactory.class, - RoutingType.class, - IndexSettings.class, - Analysis.class, - AnalysisDefinition.class, - AnalyzerDefinition.class, - AnalyzerDefinitionJsonAdapterFactory.class, - NormalizerDefinition.class, - NormalizerDefinitionJsonAdapterFactory.class, - TokenizerDefinition.class, - TokenFilterDefinition.class, - CharFilterDefinition.class, - AnalysisDefinitionJsonAdapterFactory.class, - IndexAliasDefinition.class, - IndexAliasDefinitionJsonAdapterFactory.class, - DynamicTemplate.class, - DynamicTemplateJsonAdapterFactory.class, - NamedDynamicTemplate.class, - NamedDynamicTemplateJsonAdapterFactory.class); - for (Class publicGsonClass : publicGsonClasses) { - Class currentClass = publicGsonClass; - while (currentClass != Object.class) { - GSON_CLASSES.add(DotName.createSimple(currentClass.getName())); - currentClass = currentClass.getSuperclass(); - } + for (Class gsonClass : GsonClasses.typesRequiringReflection()) { + GSON_CLASSES.add(DotName.createSimple(gsonClass.getName())); } } } From e94dff5a2f276e65a1b8ec454528619a6b191b46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Thu, 10 Dec 2020 18:46:06 +0100 Subject: [PATCH 59/62] Stop suggesting that Hibernate Search 6 is work in progress MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yoann Rodière --- .../hibernate-search-orm-elasticsearch.adoc | 20 ++----------------- 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/docs/src/main/asciidoc/hibernate-search-orm-elasticsearch.adoc b/docs/src/main/asciidoc/hibernate-search-orm-elasticsearch.adoc index 20367b3aa6207..a2f77bccbad71 100644 --- a/docs/src/main/asciidoc/hibernate-search-orm-elasticsearch.adoc +++ b/docs/src/main/asciidoc/hibernate-search-orm-elasticsearch.adoc @@ -759,27 +759,11 @@ In a real life application, it is obviously something you won't do at startup. == Further reading -If you are interested into learning more about Hibernate Search 6, the Hibernate team has https://docs.jboss.org/hibernate/search/6.0/reference/en-US/html_single/[some documentation] in the works. - -It is still work in progress but covers all main concepts and features, -so it can guide you in your exploration. +If you are interested in learning more about Hibernate Search 6, +the Hibernate team publishes https://docs.jboss.org/hibernate/search/6.0/reference/en-US/html_single/[an extensive reference documentation]. == FAQ -=== Why Hibernate Search 6 (and not a fully supported version)? - -To optimize the Hibernate Search bootstrap for Quarkus, the Hibernate team had to reorganize things a bit (collect the metadata offline, start the Elasticsearch client later...). - -This couldn't be done in the 5.x code base so we decided to go with the in-progress Hibernate Search 6. - -=== Can I really use it? - -Hibernate Search 6 is currently in the Candidate Release phase: the code is of production quality and can be relied on. - -What we don't guarantee is that there might be API changes along the way to the final release of Hibernate Search 6 and you might have to adapt your code. - -If it is not a major issue for you, then sure you can use it. - === Why Elasticsearch only? Hibernate Search supports both a Lucene backend and an Elasticsearch backend. From 21947b068ecedfb7a6f0eaef0a5240ec9d15d741 Mon Sep 17 00:00:00 2001 From: Manyanda Chitimbo Date: Thu, 10 Dec 2020 19:44:21 +0100 Subject: [PATCH 60/62] fix(k8s-service-binding): add missing ConfigItem in the service binding configuration Follows up https://github.com/quarkusio/quarkus/pull/13737#discussion_r540131477 --- .../service/binding/runtime/KubernetesServiceBindingConfig.java | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/kubernetes-service-binding/runtime/src/main/java/io/quarkus/kubernetes/service/binding/runtime/KubernetesServiceBindingConfig.java b/extensions/kubernetes-service-binding/runtime/src/main/java/io/quarkus/kubernetes/service/binding/runtime/KubernetesServiceBindingConfig.java index 37866722c7870..a77e90540ebc7 100644 --- a/extensions/kubernetes-service-binding/runtime/src/main/java/io/quarkus/kubernetes/service/binding/runtime/KubernetesServiceBindingConfig.java +++ b/extensions/kubernetes-service-binding/runtime/src/main/java/io/quarkus/kubernetes/service/binding/runtime/KubernetesServiceBindingConfig.java @@ -12,6 +12,7 @@ public class KubernetesServiceBindingConfig { /** * If enabled, Service Bindings will be looked in the file system */ + @ConfigItem public boolean enabled; /** From 403cc790ac3d38a509cd531771f13e1678e3b5fc Mon Sep 17 00:00:00 2001 From: Nicolas Gimenez Date: Wed, 9 Dec 2020 23:09:04 +0100 Subject: [PATCH 61/62] First attempt to create Picocli example codestart for Quarkus JBang --- .../code/jbang-picocli-code/codestart.yml | 17 ++++++++++ .../src/{resource.class-name}.tpl.qute.java | 32 +++++++++++++++++++ .../QuarkusJBangCodestartGenerationTest.java | 12 +++++++ 3 files changed, 61 insertions(+) create mode 100644 devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/code/jbang-picocli-code/codestart.yml create mode 100644 devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/code/jbang-picocli-code/java/src/{resource.class-name}.tpl.qute.java diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/code/jbang-picocli-code/codestart.yml b/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/code/jbang-picocli-code/codestart.yml new file mode 100644 index 0000000000000..dbdd53328c184 --- /dev/null +++ b/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/code/jbang-picocli-code/codestart.yml @@ -0,0 +1,17 @@ +name: jbang-picocli-code +ref: picocli +type: code +language: + base: + data: + resource: + class-name: EntryCommand + hello-name: "hello" + hello-description: "Greet World!" + hello-message: "Hello World!" + goodbye-name: "goodbye" + goodbye-description: "Say goodbye to World!" + goodbye-message: "Goodbye World!" + dependencies: + - io.quarkus:quarkus-picocli + diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/code/jbang-picocli-code/java/src/{resource.class-name}.tpl.qute.java b/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/code/jbang-picocli-code/java/src/{resource.class-name}.tpl.qute.java new file mode 100644 index 0000000000000..31245f6e91059 --- /dev/null +++ b/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/code/jbang-picocli-code/java/src/{resource.class-name}.tpl.qute.java @@ -0,0 +1,32 @@ +//usr/bin/env jbang "$0" "$@" ; exit $? +{#for dep in dependencies} +//DEPS {dep.formatted-ga}:{quarkus.version} +{/for} + +//JAVAC_OPTIONS -parameters +// +import io.quarkus.picocli.runtime.annotations.TopCommand; +import picocli.CommandLine; + +@TopCommand +@CommandLine.Command(mixinStandardHelpOptions = true, subcommands = {HelloCommand.class, GoodByeCommand.class}) +public class EntryCommand { +} + +@CommandLine.Command(name = "{resource.hello-name}", description = "{resource.hello-description}") +class HelloCommand implements Runnable { + + @Override + public void run() { + System.out.println("{resource.hello-message"); + } +} + +@CommandLine.Command(name = "{resource.goodbye-name}", description = "{resource.goodbye-description}") +class GoodByeCommand implements Runnable { + + @Override + public void run() { + System.out.println("{resource.goodbye-message}"); + } +} diff --git a/integration-tests/devtools/src/test/java/io/quarkus/devtools/codestarts/jbang/QuarkusJBangCodestartGenerationTest.java b/integration-tests/devtools/src/test/java/io/quarkus/devtools/codestarts/jbang/QuarkusJBangCodestartGenerationTest.java index f68be14a92bb8..dc90489033bd4 100644 --- a/integration-tests/devtools/src/test/java/io/quarkus/devtools/codestarts/jbang/QuarkusJBangCodestartGenerationTest.java +++ b/integration-tests/devtools/src/test/java/io/quarkus/devtools/codestarts/jbang/QuarkusJBangCodestartGenerationTest.java @@ -31,7 +31,19 @@ void generateDefaultProject() throws IOException { assertThat(projectDir.resolve("jbang")).exists(); assertThat(projectDir.resolve("src/GreetingResource.java")).exists(); + } + + @Test + void generatePicocliProject() throws IOException { + final QuarkusJBangCodestartProjectInput input = QuarkusJBangCodestartProjectInput.builder() + .addCodestart("jbang-picocli-code") + .putData("quarkus.version", "999-SNAPSHOT") + .build(); + final Path projectDir = testDirPath.resolve("picocli"); + getCatalog().createProject(input).generate(projectDir); + assertThat(projectDir.resolve("jbang")).exists(); + assertThat(projectDir.resolve("src/EntryCommand.java")).exists(); } private QuarkusJBangCodestartCatalog getCatalog() throws IOException { From 182101d6c98d6d63546b0c6c974ef6c060ab5a3d Mon Sep 17 00:00:00 2001 From: Nicolas Gimenez Date: Thu, 10 Dec 2020 22:33:11 +0100 Subject: [PATCH 62/62] Create Picocli codestart for Quarkus Jbang project type --- .../src/test/java/io/quarkus/cli/CliTest.java | 12 ++++++- .../base/README.tpl.qute.md | 35 +++++++++++++++++++ .../code/jbang-picocli-code/codestart.yml | 16 +++++---- .../src/{resource.class-name}.tpl.qute.java | 12 +++---- .../base/README.tpl.qute.md | 3 ++ .../quarkus-jbang/base/README.tpl.qute.md | 3 -- 6 files changed, 64 insertions(+), 17 deletions(-) create mode 100644 devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/code/jbang-picocli-code/base/README.tpl.qute.md create mode 100644 devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/code/jbang-resteasy-code/base/README.tpl.qute.md diff --git a/devtools/cli/src/test/java/io/quarkus/cli/CliTest.java b/devtools/cli/src/test/java/io/quarkus/cli/CliTest.java index 5a1eeb2ffdb3a..5a983f5a97cdd 100644 --- a/devtools/cli/src/test/java/io/quarkus/cli/CliTest.java +++ b/devtools/cli/src/test/java/io/quarkus/cli/CliTest.java @@ -303,7 +303,7 @@ public void testMavenBuild() throws Exception { } @Test - public void testCreateJBang() throws Exception { + public void testCreateJBangRestEasy() throws Exception { execute("create-jbang", "--output-folder=my-jbang-project", "resteasy-jsonb"); @@ -312,6 +312,16 @@ public void testCreateJBang() throws Exception { Assertions.assertEquals(CommandLine.ExitCode.OK, exitCode); } + @Test + public void testCreateJBangPicocli() throws Exception { + + execute("create-jbang", "--output-folder=my-jbang-project", "quarkus-picocli"); + + Path project = workspace.resolve("my-jbang-project"); + System.setProperty("user.dir", project.toFile().getAbsolutePath()); + Assertions.assertEquals(CommandLine.ExitCode.OK, exitCode); + } + private void deleteDir(Path path) throws Exception { if (!path.toFile().exists()) return; diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/code/jbang-picocli-code/base/README.tpl.qute.md b/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/code/jbang-picocli-code/base/README.tpl.qute.md new file mode 100644 index 0000000000000..6f5a73266f7dc --- /dev/null +++ b/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/code/jbang-picocli-code/base/README.tpl.qute.md @@ -0,0 +1,35 @@ +## Greet the world! + +```shell script +./jbang src/EntryCommand.java hello +``` + +## Say Goodbye! + +```shell script +./jbang src/EntryCommand.java goodbye +``` + +## Show help + +```shell script +./jbang src/EntryCommand.java -h +``` + +or + +```shell script +./jbang src/EntryCommand.java --help +``` + +## Show version + +```shell script +./jbang src/EntryCommand.java -V +``` + +or + +```shell script +./jbang src/EntryCommand.java -h +``` diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/code/jbang-picocli-code/codestart.yml b/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/code/jbang-picocli-code/codestart.yml index dbdd53328c184..43d11b3b75a53 100644 --- a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/code/jbang-picocli-code/codestart.yml +++ b/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/code/jbang-picocli-code/codestart.yml @@ -6,12 +6,14 @@ language: data: resource: class-name: EntryCommand - hello-name: "hello" - hello-description: "Greet World!" - hello-message: "Hello World!" - goodbye-name: "goodbye" - goodbye-description: "Say goodbye to World!" - goodbye-message: "Goodbye World!" + subcommands: "{HelloCommand.class, GoodByeCommand.class}" + hello: + name: "hello" + description: "Greet World!" + message: "Hello World!" + goodbye: + name: "goodbye" + description: "Say goodbye to World!" + message: "Goodbye World!" dependencies: - io.quarkus:quarkus-picocli - diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/code/jbang-picocli-code/java/src/{resource.class-name}.tpl.qute.java b/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/code/jbang-picocli-code/java/src/{resource.class-name}.tpl.qute.java index 31245f6e91059..1055032c37d0e 100644 --- a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/code/jbang-picocli-code/java/src/{resource.class-name}.tpl.qute.java +++ b/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/code/jbang-picocli-code/java/src/{resource.class-name}.tpl.qute.java @@ -9,24 +9,24 @@ import picocli.CommandLine; @TopCommand -@CommandLine.Command(mixinStandardHelpOptions = true, subcommands = {HelloCommand.class, GoodByeCommand.class}) -public class EntryCommand { +@CommandLine.Command(mixinStandardHelpOptions = true, subcommands = {resource.subcommands}) +public class {resource.class-name} { } -@CommandLine.Command(name = "{resource.hello-name}", description = "{resource.hello-description}") +@CommandLine.Command(name = "{resource.hello.name}", description = "{resource.hello.description}") class HelloCommand implements Runnable { @Override public void run() { - System.out.println("{resource.hello-message"); + System.out.println("{resource.hello.message}"); } } -@CommandLine.Command(name = "{resource.goodbye-name}", description = "{resource.goodbye-description}") +@CommandLine.Command(name = "{resource.goodbye.name}", description = "{resource.goodbye.description}") class GoodByeCommand implements Runnable { @Override public void run() { - System.out.println("{resource.goodbye-message}"); + System.out.println("{resource.goodbye.message}"); } } diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/code/jbang-resteasy-code/base/README.tpl.qute.md b/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/code/jbang-resteasy-code/base/README.tpl.qute.md new file mode 100644 index 0000000000000..bb72b9b43ac54 --- /dev/null +++ b/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/code/jbang-resteasy-code/base/README.tpl.qute.md @@ -0,0 +1,3 @@ +```shell script +./jbang src/GreetingResource.java +``` diff --git a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/project/quarkus-jbang/base/README.tpl.qute.md b/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/project/quarkus-jbang/base/README.tpl.qute.md index a0985e19bccec..05efca97c0ebd 100644 --- a/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/project/quarkus-jbang/base/README.tpl.qute.md +++ b/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus-jbang/project/quarkus-jbang/base/README.tpl.qute.md @@ -1,5 +1,2 @@ # JBang Quarkus Project -```shell script -./jbang src/GreetingResource.java -``` \ No newline at end of file