2.1.214
- 42.5.0
+ 42.5.13.0.98.0.3011.2.0.jre11
@@ -4252,6 +4252,11 @@
graal-sdk${graal-sdk.version}
+
+ org.graalvm.js
+ js
+ ${graal-sdk.version}
+ io.smallryejandex
diff --git a/build-parent/pom.xml b/build-parent/pom.xml
index b2ae4963435fc..1087209f91a06 100644
--- a/build-parent/pom.xml
+++ b/build-parent/pom.xml
@@ -34,7 +34,7 @@
${version.surefire.plugin}
- 3.0.3
+ 3.0.41.0.02.5.7
diff --git a/devtools/maven/src/main/java/io/quarkus/maven/BuildMojo.java b/devtools/maven/src/main/java/io/quarkus/maven/BuildMojo.java
index 6745692b559dd..7bb6a30512282 100644
--- a/devtools/maven/src/main/java/io/quarkus/maven/BuildMojo.java
+++ b/devtools/maven/src/main/java/io/quarkus/maven/BuildMojo.java
@@ -29,6 +29,7 @@
import io.quarkus.bootstrap.app.AugmentResult;
import io.quarkus.bootstrap.app.CuratedApplication;
import io.quarkus.bootstrap.util.IoUtils;
+import io.quarkus.maven.dependency.ArtifactCoords;
/**
* Builds the Quarkus application.
@@ -77,11 +78,11 @@ protected boolean beforeExecute() throws MojoExecutionException {
getLog().info("Skipping Quarkus build");
return false;
}
- if (mavenProject().getPackaging().equals("pom")) {
+ if (mavenProject().getPackaging().equals(ArtifactCoords.TYPE_POM)) {
getLog().info("Type of the artifact is POM, skipping build goal");
return false;
}
- if (!mavenProject().getArtifact().getArtifactHandler().getExtension().equals("jar")) {
+ if (!mavenProject().getArtifact().getArtifactHandler().getExtension().equals(ArtifactCoords.TYPE_JAR)) {
throw new MojoExecutionException(
"The project artifact's extension is '" + mavenProject().getArtifact().getArtifactHandler().getExtension()
+ "' while this goal expects it be 'jar'");
diff --git a/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java b/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java
index ac5bc0683a3e1..aa3ef463c7f52 100644
--- a/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java
+++ b/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java
@@ -391,6 +391,11 @@ public void setLog(Log log) {
@Override
public void execute() throws MojoFailureException, MojoExecutionException {
+ if (project.getPackaging().equals(ArtifactCoords.TYPE_POM)) {
+ getLog().info("Type of the artifact is POM, skipping dev goal");
+ return;
+ }
+
mavenVersionEnforcer.ensureMavenVersion(getLog(), session);
initToolchain();
diff --git a/docs/src/main/asciidoc/rest-client-reactive.adoc b/docs/src/main/asciidoc/rest-client-reactive.adoc
index e2f3200065220..3a29e83ad1fb6 100644
--- a/docs/src/main/asciidoc/rest-client-reactive.adoc
+++ b/docs/src/main/asciidoc/rest-client-reactive.adoc
@@ -1050,9 +1050,11 @@ Then, in your test you can simply use `@InjectMock` to create and inject a mock:
[source,java]
----
-import static org.assertj.core.api.Assertions.assertThat;
+package io.quarkus.it.rest.client.main;
+
import static org.mockito.Mockito.when;
+import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -1063,6 +1065,7 @@ import io.quarkus.test.junit.mockito.InjectMock;
public class InjectMockTest {
@InjectMock
+ @RestClient
Client mock;
@BeforeEach
@@ -1082,6 +1085,8 @@ If Mockito doesn't meet your needs, you can create a mock programmatically using
[source,java]
----
+package io.quarkus.it.rest.client.main;
+
import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/AdditionalBeanBuildItem.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/AdditionalBeanBuildItem.java
index 16dff62bae3f2..23afa839228d5 100644
--- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/AdditionalBeanBuildItem.java
+++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/AdditionalBeanBuildItem.java
@@ -128,7 +128,8 @@ public Builder setUnremovable() {
}
/**
- * The default scope is only used if there is no scope declared on the bean class.
+ * The default scope is only used if there is no scope declared on the bean class or added by an annotation transformer
+ * with priority higher than {@code io.quarkus.arc.processor.BuildExtension.DEFAULT_PRIORITY}
*
* The default scope should be used in cases where a bean class source is not controlled by the extension and the
* scope annotation cannot be declared directly on the class.
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 9b5840817838c..00fe8ebcf5324 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
@@ -260,6 +260,10 @@ public void transform(TransformationContext transformationContext) {
// If it declares a scope no action is needed
return;
}
+ if (customScopes.isScopeIn(transformationContext.getAnnotations())) {
+ // if one of annotations (even if added via transformer) is a scope, no action is needed
+ return;
+ }
DotName defaultScope = additionalBeanTypes.get(beanClassName);
if (defaultScope != null) {
transformationContext.transform().add(defaultScope).done();
diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/LookupConditionsProcessor.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/LookupConditionsProcessor.java
index 89d4bde9aecd2..d2f66dccebc8c 100644
--- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/LookupConditionsProcessor.java
+++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/LookupConditionsProcessor.java
@@ -4,6 +4,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Optional;
+import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -31,6 +32,9 @@ public class LookupConditionsProcessor {
private static final DotName LOOK_UP_UNLESS_PROPERTY_CONTAINER = DotName
.createSimple(LookupUnlessProperty.List.class.getName());
+ public static final Set LOOKUP_BEAN_ANNOTATIONS = Set.of(LOOK_UP_IF_PROPERTY, LOOK_UP_IF_CONTAINER,
+ LOOK_UP_UNLESS_PROPERTY, LOOK_UP_UNLESS_PROPERTY_CONTAINER);
+
private static final String NAME = "name";
private static final String STRING_VALUE = "stringValue";
private static final String LOOKUP_IF_MISSING = "lookupIfMissing";
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 df40a1a0d9ca0..f56ed896c9ef1 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
@@ -731,7 +731,7 @@ private FilterClassIntrospector createFilterClassIntrospector() {
return ab.get();
}
- // We want to add @Typed to resources and providers so that they can be resolved as CDI bean using purely their
+ // We want to add @Typed to resources, beanparams and providers so that they can be resolved as CDI bean using purely their
// class as a bean type. This removes any ambiguity that potential subclasses may have.
@BuildStep
public void transformEndpoints(
@@ -745,6 +745,10 @@ public void transformEndpoints(
allResources.addAll(resourceScanningResultBuildItem.getResult().getScannedResources().keySet());
allResources.addAll(resourceScanningResultBuildItem.getResult().getPossibleSubResources().keySet());
+ // all found bean params
+ Set beanParams = resourceScanningResultBuildItem.getResult()
+ .getBeanParams();
+
// discovered filters and interceptors
Set filtersAndInterceptors = new HashSet<>();
InterceptorContainer readerInterceptors = resourceInterceptorsBuildItem.getResourceInterceptors()
@@ -789,6 +793,14 @@ public void transform(TransformationContext context) {
&& clazz.declaredAnnotation(ResteasyReactiveDotNames.TYPED) == null) {
// Add @Typed(MyResource.class)
context.transform().add(createTypedAnnotationInstance(clazz, beanArchiveIndexBuildItem)).done();
+ return;
+ }
+ // check if the class is a bean param
+ if (beanParams.contains(clazz.name().toString())
+ && clazz.declaredAnnotation(ResteasyReactiveDotNames.TYPED) == null) {
+ // Add @Typed(MyBean.class)
+ context.transform().add(createTypedAnnotationInstance(clazz, beanArchiveIndexBuildItem)).done();
+ return;
}
}
}));
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 32f47b768d25a..aacd2043ff891 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
@@ -50,6 +50,7 @@
import io.quarkus.arc.deployment.BuildTimeEnabledProcessor;
import io.quarkus.arc.deployment.GeneratedBeanBuildItem;
import io.quarkus.arc.deployment.GeneratedBeanGizmoAdaptor;
+import io.quarkus.arc.deployment.LookupConditionsProcessor;
import io.quarkus.arc.processor.DotNames;
import io.quarkus.deployment.Capabilities;
import io.quarkus.deployment.Capability;
@@ -81,6 +82,13 @@
*/
public class ResteasyReactiveScanningProcessor {
+ public static final Set CONDITIONAL_BEAN_ANNOTATIONS;
+
+ static {
+ CONDITIONAL_BEAN_ANNOTATIONS = new HashSet<>(BuildTimeEnabledProcessor.BUILD_TIME_ENABLED_BEAN_ANNOTATIONS);
+ CONDITIONAL_BEAN_ANNOTATIONS.addAll(LookupConditionsProcessor.LOOKUP_BEAN_ANNOTATIONS);
+ }
+
@BuildStep
public MethodScannerBuildItem asyncSupport() {
return new MethodScannerBuildItem(new AsyncReturnTypeScanner());
@@ -313,9 +321,18 @@ public void handleCustomAnnotatedMethods(
List generatedFilters = FilterGeneration.generate(index,
Set.of(HTTP_SERVER_REQUEST, HTTP_SERVER_RESPONSE, ROUTING_CONTEXT), Set.of(Unremovable.class.getName()),
(methodInfo -> {
+ List methodAnnotations = methodInfo.annotations();
+ for (AnnotationInstance methodAnnotation : methodAnnotations) {
+ if (CONDITIONAL_BEAN_ANNOTATIONS.contains(methodAnnotation.name())) {
+ throw new RuntimeException("The combination of '@" + methodAnnotation.name().withoutPackagePrefix()
+ + "' and '@ServerRequestFilter' or '@ServerResponseFilter' is not allowed. Offending method is '"
+ + methodInfo.name() + "' of class '" + methodInfo.declaringClass().name() + "'");
+ }
+ }
+
List classAnnotations = methodInfo.declaringClass().declaredAnnotations();
for (AnnotationInstance classAnnotation : classAnnotations) {
- if (BuildTimeEnabledProcessor.BUILD_TIME_ENABLED_BEAN_ANNOTATIONS.contains(classAnnotation.name())) {
+ if (CONDITIONAL_BEAN_ANNOTATIONS.contains(classAnnotation.name())) {
return true;
}
}
diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/beanparam/BeanParamTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/beanparam/BeanParamTest.java
new file mode 100644
index 0000000000000..85a739e30b75a
--- /dev/null
+++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/beanparam/BeanParamTest.java
@@ -0,0 +1,104 @@
+package io.quarkus.resteasy.reactive.server.test.beanparam;
+
+import javax.ws.rs.BeanParam;
+import javax.ws.rs.CookieParam;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.QueryParam;
+
+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 BeanParamTest {
+ @RegisterExtension
+ static final QuarkusUnitTest TEST = new QuarkusUnitTest()
+ .setArchiveProducer(() -> {
+ return ShrinkWrap.create(JavaArchive.class)
+ .addClasses(MyBeanParamWithFieldsAndProperties.class, Top.class);
+ });
+
+ @Test
+ void shouldDeployWithoutIssues() {
+ // we only need to check that it deploys
+ }
+
+ public static class Top {
+ @PathParam("pathParam")
+ private String pathParam = "pathParam";
+
+ public String getPathParam() {
+ return pathParam;
+ }
+
+ public void setPathParam(String pathParam) {
+ this.pathParam = pathParam;
+ }
+ }
+
+ public static class MyBeanParamWithFieldsAndProperties extends Top {
+ @HeaderParam("headerParam")
+ private String headerParam = "headerParam";
+ @CookieParam("cookieParam")
+ private String cookieParam = "cookieParam";
+ @FormParam("formParam")
+ private String formParam = "formParam";
+ @QueryParam("queryParam")
+ private String queryParam = "queryParam";
+
+ // FIXME: Matrix not supported
+
+ public String getHeaderParam() {
+ return headerParam;
+ }
+
+ public void setHeaderParam(String headerParam) {
+ this.headerParam = headerParam;
+ }
+
+ public String getCookieParam() {
+ return cookieParam;
+ }
+
+ public void setCookieParam(String cookieParam) {
+ this.cookieParam = cookieParam;
+ }
+
+ public String getFormParam() {
+ return formParam;
+ }
+
+ public void setFormParam(String formParam) {
+ this.formParam = formParam;
+ }
+
+ public String getQueryParam() {
+ return queryParam;
+ }
+
+ public void setQueryParam(String queryParam) {
+ this.queryParam = queryParam;
+ }
+ }
+
+ @Path("/")
+ public static class Resource {
+ @Path("/a/{restPathDefault}/{restPath_Overridden}/{pathParam}")
+ @POST
+ public String beanParamWithFields(@BeanParam MyBeanParamWithFieldsAndProperties p) {
+ return null;
+ }
+
+ @Path("/b/{pathParam}")
+ @POST
+ public String beanParamWithFields(@BeanParam Top p) {
+ return null;
+ }
+ }
+}
diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/customproviders/ConditionalBeanFiltersTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/customproviders/ConditionalBeanFiltersTest.java
index 4168dabc5eadc..e1822c0da4553 100644
--- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/customproviders/ConditionalBeanFiltersTest.java
+++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/customproviders/ConditionalBeanFiltersTest.java
@@ -26,6 +26,8 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
+import io.quarkus.arc.lookup.LookupIfProperty;
+import io.quarkus.arc.lookup.LookupUnlessProperty;
import io.quarkus.arc.profile.IfBuildProfile;
import io.quarkus.arc.properties.IfBuildProperty;
import io.quarkus.test.QuarkusUnitTest;
@@ -49,14 +51,14 @@ public JavaArchive get() {
public void testExpectedFilters() {
List responseFiltersValues = get("/test/filters")
.then().statusCode(200)
- .body(Matchers.is("void-on,response-on,uni-on,always"))
+ .body(Matchers.is("void-on,response-on,uni-on,void-lookup-on,always"))
.extract()
.headers()
.getList("response-filters")
.stream()
.map(Header::getValue)
.collect(Collectors.toList());
- assertThat(responseFiltersValues).containsOnly("always", "void-on", "uni-on");
+ assertThat(responseFiltersValues).containsOnly("always", "void-lookup-on", "void-on", "uni-on");
}
@Path("test")
@@ -135,6 +137,34 @@ public Uni uniResponseFilter(ContainerResponseContext ctx) {
}
}
+ @LookupIfProperty(name = "notexistingproperty", stringValue = "true")
+ public static class WontBeEnabledLookupPropertyFilter {
+
+ @ServerRequestFilter(priority = Priorities.USER + 10)
+ public void voidRequestFilter(ContainerRequestContext requestContext) {
+ requestContext.getHeaders().add("request-filters", "void-lookup-off");
+ }
+
+ @ServerResponseFilter
+ public void voidResponseFilter(ContainerResponseContext ctx) {
+ assertFalse(true);
+ }
+ }
+
+ @LookupUnlessProperty(name = "notexistingproperty", stringValue = "true", lookupIfMissing = true)
+ public static class WillBeEnabledLookupPropertyFilter {
+
+ @ServerRequestFilter(priority = Priorities.USER + 20)
+ public void voidRequestFilter(ContainerRequestContext requestContext) {
+ requestContext.getHeaders().add("request-filters", "void-lookup-on");
+ }
+
+ @ServerResponseFilter(priority = Priorities.USER + 20)
+ public void voidResponseFilter(ContainerResponseContext ctx) {
+ ctx.getHeaders().add("response-filters", "void-lookup-on");
+ }
+ }
+
@Singleton
public static class AlwaysEnabledFilter {
diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/customproviders/InvalidConditionalBeanFiltersTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/customproviders/InvalidConditionalBeanFiltersTest.java
new file mode 100644
index 0000000000000..b101b18929538
--- /dev/null
+++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/customproviders/InvalidConditionalBeanFiltersTest.java
@@ -0,0 +1,62 @@
+package io.quarkus.resteasy.reactive.server.test.customproviders;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.util.function.Supplier;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.UriInfo;
+
+import org.jboss.resteasy.reactive.server.ServerRequestFilter;
+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.arc.profile.IfBuildProfile;
+import io.quarkus.test.QuarkusUnitTest;
+
+public class InvalidConditionalBeanFiltersTest {
+
+ @RegisterExtension
+ static QuarkusUnitTest test = new QuarkusUnitTest()
+ .setArchiveProducer(new Supplier<>() {
+ @Override
+ public JavaArchive get() {
+ return ShrinkWrap.create(JavaArchive.class)
+ .addClasses(TestResource.class, Filters.class);
+ }
+ }).assertException(t -> {
+ String message = t.getMessage();
+ assertTrue(message.contains("@IfBuildProfile"));
+ assertTrue(message.contains("request"));
+ assertTrue(message.contains(InvalidConditionalBeanFiltersTest.Filters.class.getName()));
+ });
+
+ @Test
+ public void test() {
+ fail("Should never have been called");
+ }
+
+ @Path("test")
+ public static class TestResource {
+
+ @GET
+ public String hello() {
+ return "hello";
+ }
+
+ }
+
+ public static class Filters {
+
+ @IfBuildProfile("test")
+ @ServerRequestFilter
+ public void request(UriInfo info) {
+
+ }
+
+ }
+}
diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/DevConsoleProcessor.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/DevConsoleProcessor.java
index 6c34e448daabe..c95b37cbd1302 100644
--- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/DevConsoleProcessor.java
+++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/DevConsoleProcessor.java
@@ -95,6 +95,7 @@
import io.quarkus.vertx.http.deployment.RouteBuildItem;
import io.quarkus.vertx.http.deployment.webjar.WebJarBuildItem;
import io.quarkus.vertx.http.deployment.webjar.WebJarResultsBuildItem;
+import io.quarkus.vertx.http.runtime.devmode.DevConsoleCORSFilter;
import io.quarkus.vertx.http.runtime.devmode.DevConsoleFilter;
import io.quarkus.vertx.http.runtime.devmode.DevConsoleRecorder;
import io.quarkus.vertx.http.runtime.devmode.RedirectHandler;
@@ -438,6 +439,7 @@ public DevConsoleTemplateInfoBuildItem config(List routes,
@@ -472,6 +474,12 @@ public void setupDevConsoleRoutes(
// if the handler is a proxy, then that means it's been produced by a recorder and therefore belongs in the regular runtime Vert.x instance
// otherwise this is handled in the setupDeploymentSideHandling method
if (!i.isDeploymentSide()) {
+ if (devUIConfig.cors.enabled) {
+ routeBuildItemBuildProducer.produce(nonApplicationRootPathBuildItem.routeBuilder()
+ .route("dev/*")
+ .handler(new DevConsoleCORSFilter())
+ .build());
+ }
NonApplicationRootPathBuildItem.Builder builder = nonApplicationRootPathBuildItem.routeBuilder()
.routeFunction(
"dev/" + groupAndArtifact.getKey() + "." + groupAndArtifact.getValue() + "/" + i.getPath(),
diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/DevUIConfig.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/DevUIConfig.java
index 634a1d9d7dd5a..828db66ef474c 100644
--- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/DevUIConfig.java
+++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/DevUIConfig.java
@@ -1,5 +1,6 @@
package io.quarkus.vertx.http.deployment.devmode.console;
+import io.quarkus.runtime.annotations.ConfigGroup;
import io.quarkus.runtime.annotations.ConfigItem;
import io.quarkus.runtime.annotations.ConfigRoot;
@@ -12,4 +13,19 @@ public class DevUIConfig {
@ConfigItem(defaultValue = "50")
public int historySize;
+ /**
+ * CORS configuration.
+ */
+ public Cors cors = new Cors();
+
+ @ConfigGroup
+ public static class Cors {
+
+ /**
+ * Enable CORS filter.
+ */
+ @ConfigItem(defaultValue = "true")
+ public boolean enabled = true;
+ }
+
}
diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/cors/CORSHandlerTestWildcardOriginCase.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/cors/CORSHandlerTestWildcardOriginCase.java
index a89ee9697275b..bea0ee1578301 100644
--- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/cors/CORSHandlerTestWildcardOriginCase.java
+++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/cors/CORSHandlerTestWildcardOriginCase.java
@@ -1,6 +1,7 @@
package io.quarkus.vertx.http.cors;
import static io.restassured.RestAssured.given;
+import static org.hamcrest.Matchers.nullValue;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@@ -28,6 +29,7 @@ void corsMatchingOrigin() {
.when()
.options("/test").then()
.statusCode(200)
+ .header("Access-Control-Allow-Origin", origin)
.header("Access-Control-Allow-Credentials", "true");
}
@@ -42,7 +44,8 @@ void corsNotMatchingOrigin() {
.header("Access-Control-Request-Headers", headers)
.when()
.options("/test").then()
- .statusCode(200)
+ .statusCode(403)
+ .header("Access-Control-Allow-Origin", nullValue())
.header("Access-Control-Allow-Credentials", "false");
}
@@ -58,6 +61,7 @@ void corsMatchingOriginWithWildcard() {
.when()
.options("/test").then()
.statusCode(200)
+ .header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Credentials", "false");
}
}
diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/devconsole/DevConsoleCorsTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/devconsole/DevConsoleCorsTest.java
new file mode 100644
index 0000000000000..81d6660f6c244
--- /dev/null
+++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/devconsole/DevConsoleCorsTest.java
@@ -0,0 +1,194 @@
+package io.quarkus.vertx.http.devconsole;
+
+import static org.hamcrest.Matchers.emptyOrNullString;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.nullValue;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import io.quarkus.test.QuarkusDevModeTest;
+import io.restassured.RestAssured;
+
+public class DevConsoleCorsTest {
+
+ @RegisterExtension
+ static final QuarkusDevModeTest config = new QuarkusDevModeTest()
+ .withEmptyApplication();
+
+ @Test
+ public void testPreflightHttpLocalhostOrigin() {
+ String origin = "http://localhost:8080";
+ String methods = "GET,POST";
+ RestAssured.given()
+ .header("Origin", origin)
+ .header("Access-Control-Request-Method", methods)
+ .when()
+ .options("q/dev/io.quarkus.quarkus-vertx-http/config").then()
+ .statusCode(200)
+ .header("Access-Control-Allow-Origin", origin)
+ .header("Access-Control-Allow-Methods", methods)
+ .body(emptyOrNullString());
+ }
+
+ @Test
+ public void testPreflightHttpLocalhostIpOrigin() {
+ String origin = "http://127.0.0.1:8080";
+ String methods = "GET,POST";
+ RestAssured.given()
+ .header("Origin", origin)
+ .header("Access-Control-Request-Method", methods)
+ .when()
+ .options("q/dev/io.quarkus.quarkus-vertx-http/config").then()
+ .statusCode(200)
+ .header("Access-Control-Allow-Origin", origin)
+ .header("Access-Control-Allow-Methods", methods)
+ .body(emptyOrNullString());
+ }
+
+ @Test
+ public void testPreflightHttpsLocalhostOrigin() {
+ String origin = "https://localhost:8443";
+ String methods = "GET,POST";
+ RestAssured.given()
+ .header("Origin", origin)
+ .header("Access-Control-Request-Method", methods)
+ .when()
+ .options("q/dev/io.quarkus.quarkus-vertx-http/config").then()
+ .statusCode(200)
+ .header("Access-Control-Allow-Origin", origin)
+ .header("Access-Control-Allow-Methods", methods)
+ .body(emptyOrNullString());
+ }
+
+ @Test
+ public void testPreflightHttpsLocalhostIpOrigin() {
+ String origin = "https://127.0.0.1:8443";
+ String methods = "GET,POST";
+ RestAssured.given()
+ .header("Origin", origin)
+ .header("Access-Control-Request-Method", methods)
+ .when()
+ .options("q/dev/io.quarkus.quarkus-vertx-http/config").then()
+ .statusCode(200)
+ .header("Access-Control-Allow-Origin", origin)
+ .header("Access-Control-Allow-Methods", methods)
+ .body(emptyOrNullString());
+ }
+
+ @Test
+ public void testPreflightNonLocalhostOrigin() {
+ String methods = "GET,POST";
+ RestAssured.given()
+ .header("Origin", "https://quarkus.io/http://localhost")
+ .header("Access-Control-Request-Method", methods)
+ .when()
+ .options("q/dev/io.quarkus.quarkus-vertx-http/config").then()
+ .statusCode(403)
+ .header("Access-Control-Allow-Origin", nullValue())
+ .header("Access-Control-Allow-Methods", nullValue())
+ .body(emptyOrNullString());
+ }
+
+ @Test
+ public void testPreflightBadLocalhostOrigin() {
+ String methods = "GET,POST";
+ RestAssured.given()
+ .header("Origin", "http://localhost:8080/devui")
+ .header("Access-Control-Request-Method", methods)
+ .when()
+ .options("q/dev/io.quarkus.quarkus-vertx-http/config").then()
+ .statusCode(403)
+ .header("Access-Control-Allow-Origin", nullValue())
+ .body(emptyOrNullString());
+ }
+
+ @Test
+ public void testPreflightBadLocalhostIpOrigin() {
+ String methods = "GET,POST";
+ RestAssured.given()
+ .header("Origin", "http://127.0.0.1:8080/devui")
+ .header("Access-Control-Request-Method", methods)
+ .when()
+ .options("q/dev/io.quarkus.quarkus-vertx-http/config").then()
+ .statusCode(403)
+ .header("Access-Control-Allow-Origin", nullValue())
+ .body(emptyOrNullString());
+ }
+
+ @Test
+ public void testPreflightLocalhostOriginWithoutPort() {
+ String methods = "GET,POST";
+ RestAssured.given()
+ .header("Origin", "http://localhost")
+ .header("Access-Control-Request-Method", methods)
+ .when()
+ .options("q/dev/io.quarkus.quarkus-vertx-http/config").then()
+ .statusCode(403)
+ .header("Access-Control-Allow-Origin", nullValue())
+ .body(emptyOrNullString());
+ }
+
+ @Test
+ public void testSimpleRequestHttpLocalhostOrigin() {
+ String origin = "http://localhost:8080";
+ RestAssured.given()
+ .header("Origin", origin)
+ .when()
+ .get("q/dev/io.quarkus.quarkus-vertx-http/config").then()
+ .statusCode(200)
+ .header("Access-Control-Allow-Origin", origin)
+ .header("Access-Control-Allow-Methods", nullValue())
+ .body(not(emptyOrNullString()));
+ }
+
+ @Test
+ public void testSimpleRequestHttpLocalhostIpOrigin() {
+ String origin = "http://127.0.0.1:8080";
+ RestAssured.given()
+ .header("Origin", origin)
+ .when()
+ .get("q/dev/io.quarkus.quarkus-vertx-http/config").then()
+ .statusCode(200)
+ .header("Access-Control-Allow-Origin", origin)
+ .header("Access-Control-Allow-Methods", nullValue())
+ .body(not(emptyOrNullString()));
+ }
+
+ @Test
+ public void testSimpleRequestHttpsLocalhostOrigin() {
+ String origin = "https://localhost:8443";
+ RestAssured.given()
+ .header("Origin", origin)
+ .when()
+ .get("q/dev/io.quarkus.quarkus-vertx-http/config").then()
+ .statusCode(200)
+ .header("Access-Control-Allow-Origin", origin)
+ .header("Access-Control-Allow-Methods", nullValue())
+ .body(not(emptyOrNullString()));
+ }
+
+ @Test
+ public void testSimpleRequestHttpsLocalhostIpOrigin() {
+ String origin = "https://127.0.0.1:8443";
+ RestAssured.given()
+ .header("Origin", origin)
+ .when()
+ .get("q/dev/io.quarkus.quarkus-vertx-http/config").then()
+ .statusCode(200)
+ .header("Access-Control-Allow-Origin", origin)
+ .header("Access-Control-Allow-Methods", nullValue())
+ .body(not(emptyOrNullString()));
+ }
+
+ @Test
+ public void testSimpleRequestNonLocalhostOrigin() {
+ RestAssured.given()
+ .header("Origin", "https://quarkus.io/http://localhost")
+ .when()
+ .get("q/dev/io.quarkus.quarkus-vertx-http/config").then()
+ .statusCode(403)
+ .header("Access-Control-Allow-Origin", nullValue())
+ .body(emptyOrNullString());
+ }
+}
diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/cors/CORSConfig.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/cors/CORSConfig.java
index 50f45d0b1f383..d98820d59c099 100644
--- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/cors/CORSConfig.java
+++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/cors/CORSConfig.java
@@ -24,7 +24,7 @@ public class CORSConfig {
*/
@ConfigItem
@ConvertWith(TrimmedStringConverter.class)
- public Optional> origins;
+ public Optional> origins = Optional.empty();
/**
* HTTP methods allowed for CORS
@@ -36,7 +36,7 @@ public class CORSConfig {
*/
@ConfigItem
@ConvertWith(TrimmedStringConverter.class)
- public Optional> methods;
+ public Optional> methods = Optional.empty();
/**
* HTTP headers allowed for CORS
@@ -48,7 +48,7 @@ public class CORSConfig {
*/
@ConfigItem
@ConvertWith(TrimmedStringConverter.class)
- public Optional> headers;
+ public Optional> headers = Optional.empty();
/**
* HTTP headers exposed in CORS
@@ -59,14 +59,14 @@ public class CORSConfig {
*/
@ConfigItem
@ConvertWith(TrimmedStringConverter.class)
- public Optional> exposedHeaders;
+ public Optional> exposedHeaders = Optional.empty();
/**
* The `Access-Control-Max-Age` response header value indicating
* how long the results of a pre-flight request can be cached.
*/
@ConfigItem
- public Optional accessControlMaxAge;
+ public Optional accessControlMaxAge = Optional.empty();
/**
* The `Access-Control-Allow-Credentials` header is used to tell the
@@ -77,7 +77,7 @@ public class CORSConfig {
* there is a match with the precise `Origin` header and that header is not '*'.
*/
@ConfigItem
- public Optional accessControlAllowCredentials;
+ public Optional accessControlAllowCredentials = Optional.empty();
@Override
public String toString() {
diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/cors/CORSFilter.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/cors/CORSFilter.java
index 8bb838927594e..4a7fee1a14e4f 100644
--- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/cors/CORSFilter.java
+++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/cors/CORSFilter.java
@@ -195,7 +195,11 @@ public void handle(RoutingContext event) {
String.join(",", exposedHeaders.orElse(Collections.emptyList())));
}
- if (request.method().equals(HttpMethod.OPTIONS) && (requestedHeaders != null || requestedMethods != null)) {
+ if (!allowsOrigin) {
+ response.setStatusCode(403);
+ response.setStatusMessage("CORS Rejected - Invalid origin");
+ response.end();
+ } else if (request.method().equals(HttpMethod.OPTIONS) && (requestedHeaders != null || requestedMethods != null)) {
if (corsConfig.accessControlMaxAge.isPresent()) {
response.putHeader(HttpHeaders.ACCESS_CONTROL_MAX_AGE,
String.valueOf(corsConfig.accessControlMaxAge.get().getSeconds()));
diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/DevConsoleCORSFilter.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/DevConsoleCORSFilter.java
new file mode 100644
index 0000000000000..0ef7e7627bec5
--- /dev/null
+++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/DevConsoleCORSFilter.java
@@ -0,0 +1,63 @@
+package io.quarkus.vertx.http.runtime.devmode;
+
+import java.util.List;
+import java.util.Optional;
+
+import org.eclipse.microprofile.config.ConfigProvider;
+import org.jboss.logging.Logger;
+
+import io.quarkus.vertx.http.runtime.cors.CORSConfig;
+import io.quarkus.vertx.http.runtime.cors.CORSFilter;
+import io.vertx.core.Handler;
+import io.vertx.core.http.HttpHeaders;
+import io.vertx.core.http.HttpServerRequest;
+import io.vertx.core.http.HttpServerResponse;
+import io.vertx.ext.web.RoutingContext;
+
+public class DevConsoleCORSFilter implements Handler {
+ private static final Logger LOG = Logger.getLogger(DevConsoleCORSFilter.class);
+
+ private static final String HTTP_PORT_CONFIG_PROP = "quarkus.http.port";
+ private static final String HTTPS_PORT_CONFIG_PROP = "quarkus.http.ssl-port";
+ private static final String LOCAL_HOST = "localhost";
+ private static final String LOCAL_HOST_IP = "127.0.0.1";
+ private static final String HTTP_LOCAL_HOST = "http://" + LOCAL_HOST;
+ private static final String HTTPS_LOCAL_HOST = "https://" + LOCAL_HOST;
+ private static final String HTTP_LOCAL_HOST_IP = "http://" + LOCAL_HOST_IP;
+ private static final String HTTPS_LOCAL_HOST_IP = "https://" + LOCAL_HOST_IP;
+
+ public DevConsoleCORSFilter() {
+ }
+
+ private static CORSFilter corsFilter() {
+ int httpPort = ConfigProvider.getConfig().getValue(HTTP_PORT_CONFIG_PROP, int.class);
+ int httpsPort = ConfigProvider.getConfig().getValue(HTTPS_PORT_CONFIG_PROP, int.class);
+ CORSConfig config = new CORSConfig();
+ config.origins = Optional.of(List.of(
+ HTTP_LOCAL_HOST + ":" + httpPort,
+ HTTP_LOCAL_HOST_IP + ":" + httpPort,
+ HTTPS_LOCAL_HOST + ":" + httpsPort,
+ HTTPS_LOCAL_HOST_IP + ":" + httpsPort));
+ return new CORSFilter(config);
+ }
+
+ @Override
+ public void handle(RoutingContext event) {
+ HttpServerRequest request = event.request();
+ HttpServerResponse response = event.response();
+ String origin = request.getHeader(HttpHeaders.ORIGIN);
+ if (origin == null) {
+ corsFilter().handle(event);
+ } else {
+ if (origin.startsWith(HTTP_LOCAL_HOST) || origin.startsWith(HTTPS_LOCAL_HOST)
+ || origin.startsWith(HTTP_LOCAL_HOST_IP) || origin.startsWith(HTTPS_LOCAL_HOST_IP)) {
+ corsFilter().handle(event);
+ } else {
+ LOG.errorf("Only localhost origin is allowed, but Origin header value is: %s", origin);
+ response.setStatusCode(403);
+ response.setStatusMessage("CORS Rejected - Invalid origin");
+ response.end();
+ }
+ }
+ }
+}
diff --git a/extensions/vertx/deployment/src/main/java/io/quarkus/vertx/deployment/EventBusCodecProcessor.java b/extensions/vertx/deployment/src/main/java/io/quarkus/vertx/deployment/EventBusCodecProcessor.java
index 1b6c049760a64..3d2aba17cccdf 100644
--- a/extensions/vertx/deployment/src/main/java/io/quarkus/vertx/deployment/EventBusCodecProcessor.java
+++ b/extensions/vertx/deployment/src/main/java/io/quarkus/vertx/deployment/EventBusCodecProcessor.java
@@ -42,7 +42,7 @@ public void registerCodecs(
final IndexView index = beanArchiveIndexBuildItem.getIndex();
Collection consumeEventAnnotationInstances = index.getAnnotations(CONSUME_EVENT);
- Map codecByTypes = new HashMap<>();
+ Map codecByTypes = new HashMap<>();
for (AnnotationInstance consumeEventAnnotationInstance : consumeEventAnnotationInstances) {
AnnotationTarget typeTarget = consumeEventAnnotationInstance.target();
if (typeTarget.kind() != AnnotationTarget.Kind.METHOD) {
@@ -59,7 +59,7 @@ public void registerCodecs(
if (codecTargetFromParameter == null) {
throw new IllegalStateException("Invalid `codec` argument in @ConsumeEvent - no parameter");
}
- codecByTypes.put(codecTargetFromParameter, codec.asClass().asClassType().name());
+ codecByTypes.put(codecTargetFromParameter.name(), codec.asClass().asClassType().name());
} else if (codecTargetFromParameter != null) {
// Codec is not set, check if we have a built-in codec
if (!hasBuiltInCodec(codecTargetFromParameter)) {
@@ -70,24 +70,24 @@ public void registerCodecs(
"The generic message codec can only be used for local delivery,"
+ ", implement your own event bus codec for " + codecTargetFromParameter.name()
.toString());
- } else if (!codecByTypes.containsKey(codecTargetFromParameter)) {
+ } else if (!codecByTypes.containsKey(codecTargetFromParameter.name())) {
LOGGER.infof("Local Message Codec registered for type %s",
codecTargetFromParameter.toString());
- codecByTypes.put(codecTargetFromParameter, LOCAL_EVENT_BUS_CODEC);
+ codecByTypes.put(codecTargetFromParameter.name(), LOCAL_EVENT_BUS_CODEC);
}
}
}
if (codecTargetFromReturnType != null && !hasBuiltInCodec(codecTargetFromReturnType)
- && !codecByTypes.containsKey(codecTargetFromReturnType)) {
+ && !codecByTypes.containsKey(codecTargetFromReturnType.name())) {
LOGGER.infof("Local Message Codec registered for type %s", codecTargetFromReturnType.toString());
- codecByTypes.put(codecTargetFromReturnType, LOCAL_EVENT_BUS_CODEC);
+ codecByTypes.put(codecTargetFromReturnType.name(), LOCAL_EVENT_BUS_CODEC);
}
}
// Produce the build items
- for (Map.Entry entry : codecByTypes.entrySet()) {
+ for (Map.Entry entry : codecByTypes.entrySet()) {
messageCodecs.produce(new MessageCodecBuildItem(entry.getKey().toString(), entry.getValue().toString()));
}
diff --git a/extensions/vertx/deployment/src/test/java/io/quarkus/vertx/EventBusCodecTest.java b/extensions/vertx/deployment/src/test/java/io/quarkus/vertx/EventBusCodecTest.java
index 3bb61dd42ea6f..26cbd3a1ea86a 100644
--- a/extensions/vertx/deployment/src/test/java/io/quarkus/vertx/EventBusCodecTest.java
+++ b/extensions/vertx/deployment/src/test/java/io/quarkus/vertx/EventBusCodecTest.java
@@ -2,6 +2,10 @@
import static org.assertj.core.api.Assertions.assertThat;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
@@ -73,6 +77,11 @@ String getMessage() {
}
}
+ @Retention(RetentionPolicy.CLASS)
+ @Target(ElementType.TYPE_USE)
+ @interface NonNull {
+ }
+
static class MyBean {
@ConsumeEvent("person")
public CompletionStage hello(Person p) {
@@ -83,6 +92,12 @@ public CompletionStage hello(Person p) {
public CompletionStage hello(Pet p) {
return CompletableFuture.completedFuture(new Greeting("Hello " + p.getName()));
}
+
+ // presence of this method is enough to verify that type annotation
+ // on the message type doesn't cause failure
+ @ConsumeEvent("message-type-with-type-annotation")
+ void messageTypeWithTypeAnnotation(@NonNull Person person) {
+ }
}
static class MyNonLocalBean {
diff --git a/independent-projects/arc/pom.xml b/independent-projects/arc/pom.xml
index a7d542363b9b4..c75046ffe976b 100644
--- a/independent-projects/arc/pom.xml
+++ b/independent-projects/arc/pom.xml
@@ -42,7 +42,7 @@
2.0.21.3.3
- 3.0.3
+ 3.0.45.9.13.8.63.23.1
diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/AnnotationLiteralProcessor.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/AnnotationLiteralProcessor.java
index 3446f86ad3ef2..88e8e83a89377 100644
--- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/AnnotationLiteralProcessor.java
+++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/AnnotationLiteralProcessor.java
@@ -73,6 +73,13 @@ public ResultHandle process(BytecodeCreator bytecode, ClassOutput classOutput, C
* the annotation members have the same values as the given annotation instance.
* An implementation of the annotation type will be generated automatically.
*
+ * It is expected that given annotation instance is runtime-retained; an exception is thrown
+ * if not. Further, it is expected that the annotation type is available (that is,
+ * {@code annotationClass != null}); an exception is thrown if not. Callers that expect
+ * they always deal with runtime-retained annotations whose classes are available do not
+ * have to check (and will get decent errors for free), but callers that can possibly deal
+ * with class-retained annotations or missing annotation classes must check explicitly.
+ *
* We call the generated implementation of the annotation type an annotation literal class
* and the instance produced by the generated bytecode an annotation literal instance,
* even though the generated code doesn't use CDI's {@code AnnotationLiteral} anymore.
@@ -84,6 +91,10 @@ public ResultHandle process(BytecodeCreator bytecode, ClassOutput classOutput, C
* @return an annotation literal instance result handle
*/
public ResultHandle create(BytecodeCreator bytecode, ClassInfo annotationClass, AnnotationInstance annotationInstance) {
+ if (!annotationInstance.runtimeVisible()) {
+ throw new IllegalArgumentException("Annotation does not have @Retention(RUNTIME): " + annotationInstance);
+ }
+
Objects.requireNonNull(annotationClass, "Annotation class not available: " + annotationInstance);
AnnotationLiteralClassInfo literal = cache.getValue(new CacheKey(annotationClass));
diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanGenerator.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanGenerator.java
index 698525b765051..952895860541d 100644
--- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanGenerator.java
+++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanGenerator.java
@@ -956,14 +956,15 @@ protected void implementCreate(ClassOutput classOutput, ClassCreator beanCreator
bridgeCreate.getMethodParam(0)));
}
- private List newProviderHandles(BeanInfo bean, ClassCreator beanCreator, MethodCreator createMethod,
+ private void newProviderHandles(BeanInfo bean, ClassCreator beanCreator, MethodCreator createMethod,
Map injectionPointToProviderField,
Map interceptorToProviderField,
Map decoratorToProviderSupplierField,
Map interceptorToWrap,
- List transientReferences) {
+ List transientReferences,
+ List injectableParamHandles,
+ List allOtherParamHandles) {
- List providerHandles = new ArrayList<>();
Optional constructorInjection = bean.getConstructorInjection();
if (constructorInjection.isPresent()) {
@@ -978,7 +979,7 @@ private List newProviderHandles(BeanInfo bean, ClassCreator beanCr
providerHandle, createMethod.getMethodParam(0));
ResultHandle referenceHandle = createMethod.invokeInterfaceMethod(MethodDescriptors.INJECTABLE_REF_PROVIDER_GET,
providerHandle, childCtx);
- providerHandles.add(referenceHandle);
+ injectableParamHandles.add(referenceHandle);
if (injectionPoint.isDependentTransientReference()) {
transientReferences.add(new TransientReference(providerHandle, referenceHandle, childCtx));
}
@@ -988,7 +989,7 @@ private List newProviderHandles(BeanInfo bean, ClassCreator beanCr
for (InterceptorInfo interceptor : bean.getBoundInterceptors()) {
ResultHandle wrapped = interceptorToWrap.get(interceptor);
if (wrapped != null) {
- providerHandles.add(wrapped);
+ allOtherParamHandles.add(wrapped);
} else {
ResultHandle interceptorProviderSupplierHandle = createMethod.readInstanceField(
FieldDescriptor.of(beanCreator.getClassName(), interceptorToProviderField.get(interceptor),
@@ -996,7 +997,7 @@ private List newProviderHandles(BeanInfo bean, ClassCreator beanCr
createMethod.getThis());
ResultHandle interceptorProviderHandle = createMethod.invokeInterfaceMethod(
MethodDescriptors.SUPPLIER_GET, interceptorProviderSupplierHandle);
- providerHandles.add(interceptorProviderHandle);
+ allOtherParamHandles.add(interceptorProviderHandle);
}
}
for (DecoratorInfo decorator : bean.getBoundDecorators()) {
@@ -1006,10 +1007,9 @@ private List newProviderHandles(BeanInfo bean, ClassCreator beanCr
createMethod.getThis());
ResultHandle decoratorProviderHandle = createMethod.invokeInterfaceMethod(
MethodDescriptors.SUPPLIER_GET, decoratorProviderSupplierHandle);
- providerHandles.add(decoratorProviderHandle);
+ allOtherParamHandles.add(decoratorProviderHandle);
}
}
- return providerHandles;
}
private ResultHandle newInstanceHandle(BeanInfo bean, ClassCreator beanCreator, BytecodeCreator creator,
@@ -1402,14 +1402,20 @@ void implementCreateForClassBean(ClassOutput classOutput, ClassCreator beanCreat
}
List transientReferences = new ArrayList<>();
- List providerHandles = newProviderHandles(bean, beanCreator, create,
+ // List of handles representing injectable parameters
+ List injectableCtorParams = new ArrayList<>();
+ // list of handles representing all other parameters, such as injectable interceptors
+ List allOtherCtorParams = new ArrayList<>();
+ newProviderHandles(bean, beanCreator, create,
injectionPointToProviderSupplierField, interceptorToProviderSupplierField, decoratorToProviderSupplierField,
- interceptorToWrap, transientReferences);
+ interceptorToWrap, transientReferences, injectableCtorParams, allOtherCtorParams);
// Forwarding function
// Supplier