methodsToImplement = new ArrayList<>();
+
+ // search this interface and its super interfaces for jaxrs methods
+ searchForJaxRsMethods(methodsToImplement, jaxrsInterface, index);
+ // search this interface for default methods
+ // we could search for default methods in super interfaces too,
+ // but emitting the correct invokespecial instruction would become convoluted
+ // (as invokespecial may only reference a method from a _direct_ super interface)
+ for (MethodInfo method : jaxrsInterface.methods()) {
+ boolean isDefault = !Modifier.isAbstract(method.flags());
+ if (isDefault) {
+ methodsToImplement.add(method);
+ }
+ }
+ if (methodsToImplement.isEmpty()) {
continue;
}
@@ -384,11 +394,16 @@ void addRestClientBeans(Capabilities capabilities,
constructor.returnValue(null);
// METHODS:
- for (MethodInfo method : restMethods) {
+ for (MethodInfo method : methodsToImplement) {
// for each method that corresponds to making a rest call, create a method like:
// public JsonArray get() {
// return ((InterfaceClass)this.getDelegate()).get();
// }
+ //
+ // for each default method, create a method like:
+ // public JsonArray get() {
+ // return InterfaceClass.super.get();
+ // }
MethodCreator methodCreator = classCreator.getMethodCreator(MethodDescriptor.of(method));
methodCreator.setSignature(AsmUtil.getSignatureIfRequired(method));
@@ -401,22 +416,25 @@ void addRestClientBeans(Capabilities capabilities,
}
}
- ResultHandle delegate = methodCreator.invokeVirtualMethod(
- MethodDescriptor.ofMethod(RestClientReactiveCDIWrapperBase.class, "getDelegate",
- Object.class),
- methodCreator.getThis());
+ ResultHandle result;
int parameterCount = method.parameters().size();
- ResultHandle result;
- if (parameterCount == 0) {
- result = methodCreator.invokeInterfaceMethod(method, delegate);
- } else {
- ResultHandle[] params = new ResultHandle[parameterCount];
- for (int i = 0; i < parameterCount; i++) {
- params[i] = methodCreator.getMethodParam(i);
- }
+ ResultHandle[] params = new ResultHandle[parameterCount];
+ for (int i = 0; i < parameterCount; i++) {
+ params[i] = methodCreator.getMethodParam(i);
+ }
+
+ if (Modifier.isAbstract(method.flags())) { // RestClient method
+ ResultHandle delegate = methodCreator.invokeVirtualMethod(
+ MethodDescriptor.ofMethod(RestClientReactiveCDIWrapperBase.class, "getDelegate",
+ Object.class),
+ methodCreator.getThis());
+
result = methodCreator.invokeInterfaceMethod(method, delegate, params);
+ } else { // default method
+ result = methodCreator.invokeSpecialInterfaceMethod(method, methodCreator.getThis(), params);
}
+
methodCreator.returnValue(result);
}
}
diff --git a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/beanparam/BeanPathParamTest.java b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/beanparam/BeanPathParamTest.java
index 1c05c09c7ac23..54503b61b1973 100644
--- a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/beanparam/BeanPathParamTest.java
+++ b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/beanparam/BeanPathParamTest.java
@@ -35,6 +35,13 @@ void shouldPassPathParamFromBeanParamAndMethod() {
assertThat(client.getWithBeanParam("foo", new MyBeanParam("123"))).isEqualTo("it works with method too!");
}
+ @Test
+ void shouldResolvePathParamsWhenBeanParamClassExtendsAnother() {
+ Client client = RestClientBuilder.newBuilder().baseUri(baseUri).build(Client.class);
+ assertThat(client.getWithBeanParamInheritance(new MyChildBeanParam("child", "123"))).isEqualTo(
+ "it works with inheritance too!");
+ }
+
@Path("/my/{id}/resource")
public interface Client {
@GET
@@ -43,6 +50,10 @@ public interface Client {
@GET
@Path("/{name}")
String getWithBeanParam(@PathParam("name") String name, @BeanParam MyBeanParam beanParam);
+
+ @GET
+ @Path("/item/{base}")
+ String getWithBeanParamInheritance(@BeanParam MyChildBeanParam beanParam);
}
public static class MyBeanParam {
@@ -58,6 +69,40 @@ public String getId() {
}
}
+ public static class MyChildBeanParam extends MyBeanParam {
+ private final String base;
+
+ public MyChildBeanParam(String base, String id) {
+ super(id);
+ this.base = base;
+ }
+
+ @PathParam("base")
+ public String getBase() {
+ return base;
+ }
+ }
+
+ public static class TestMyChildBeanParam {
+ private final String id;
+ private final String base;
+
+ public TestMyChildBeanParam(String base, String id) {
+ this.id = id;
+ this.base = base;
+ }
+
+ @PathParam("id")
+ public String getId() {
+ return id;
+ }
+
+ @PathParam("base")
+ public String getBase() {
+ return base;
+ }
+ }
+
@Path("/my/123/resource")
public static class Resource {
@GET
@@ -70,5 +115,11 @@ public String get() {
public String getWithLongerPath() {
return "it works with method too!";
}
+
+ @Path("/item/child")
+ @GET
+ public String getWithInheritance() {
+ return "it works with inheritance too!";
+ }
}
}
diff --git a/independent-projects/arc/pom.xml b/independent-projects/arc/pom.xml
index 0aa535307f31d..17d1d30507ccc 100644
--- a/independent-projects/arc/pom.xml
+++ b/independent-projects/arc/pom.xml
@@ -44,7 +44,7 @@
3.21.0
3.4.2.Final
1.3.5
- 1.0.9.Final
+ 1.0.10.Final
2.2.3
3.0.0-M5
diff --git a/independent-projects/qute/core/src/main/java/io/quarkus/qute/TemplateExtension.java b/independent-projects/qute/core/src/main/java/io/quarkus/qute/TemplateExtension.java
index 7d8dbda352744..b66e07cf9162e 100644
--- a/independent-projects/qute/core/src/main/java/io/quarkus/qute/TemplateExtension.java
+++ b/independent-projects/qute/core/src/main/java/io/quarkus/qute/TemplateExtension.java
@@ -107,7 +107,7 @@
String namespace() default "";
/**
- * Used to annotated a template extension method parameter that should be obtained via
+ * Used to annotate a template extension method parameter that should be obtained via
* {@link TemplateInstance#getAttribute(String)}. The parameter type must be {@link java.lang.Object}.
*
*
diff --git a/independent-projects/qute/generator/src/main/java/io/quarkus/qute/generator/ExtensionMethodGenerator.java b/independent-projects/qute/generator/src/main/java/io/quarkus/qute/generator/ExtensionMethodGenerator.java
index 508b75e9ab578..4faedb0025429 100644
--- a/independent-projects/qute/generator/src/main/java/io/quarkus/qute/generator/ExtensionMethodGenerator.java
+++ b/independent-projects/qute/generator/src/main/java/io/quarkus/qute/generator/ExtensionMethodGenerator.java
@@ -670,11 +670,11 @@ static Type box(Primitive primitive) {
}
}
- static class Parameters implements Iterable {
+ public static final class Parameters implements Iterable {
final List params;
- Parameters(MethodInfo method, boolean matchAnyOrRegex, boolean hasNamespace) {
+ public Parameters(MethodInfo method, boolean matchAnyOrRegex, boolean hasNamespace) {
List parameters = method.parameters();
Map attributeParamNames = new HashMap<>();
for (AnnotationInstance annotation : method.annotations()) {
@@ -721,20 +721,20 @@ static class Parameters implements Iterable {
if (matchAnyOrRegex) {
Param nameParam = getFirst(ParamKind.NAME);
if (nameParam == null || !nameParam.type.name().equals(DotNames.STRING)) {
- throw new IllegalStateException(
+ throw new TemplateException(
"Template extension method declared on " + method.declaringClass().name()
+ " must accept at least one string parameter to match the name: " + method);
}
}
if (!hasNamespace && getFirst(ParamKind.BASE) == null) {
- throw new IllegalStateException(
+ throw new TemplateException(
"Template extension method declared on " + method.declaringClass().name()
+ " must accept at least one parameter to match the base object: " + method);
}
for (Param param : params) {
if (param.kind == ParamKind.ATTR && !param.type.name().equals(DotNames.OBJECT)) {
- throw new IllegalStateException(
+ throw new TemplateException(
"Template extension method parameter annotated with @TemplateAttribute declared on "
+ method.declaringClass().name()
+ " must be of type java.lang.Object: " + method);
@@ -742,7 +742,7 @@ static class Parameters implements Iterable {
}
}
- String[] parameterTypesAsStringArray() {
+ public String[] parameterTypesAsStringArray() {
String[] types = new String[params.size()];
for (int i = 0; i < params.size(); i++) {
types[i] = params.get(i).type.name().toString();
@@ -750,7 +750,7 @@ String[] parameterTypesAsStringArray() {
return types;
}
- Param getFirst(ParamKind kind) {
+ public Param getFirst(ParamKind kind) {
for (Param param : params) {
if (param.kind == kind) {
return param;
@@ -759,15 +759,15 @@ Param getFirst(ParamKind kind) {
return null;
}
- Param get(int index) {
+ public Param get(int index) {
return params.get(index);
}
- int size() {
+ public int size() {
return params.size();
}
- boolean needsEvaluation() {
+ public boolean needsEvaluation() {
for (Param param : params) {
if (param.kind == ParamKind.EVAL) {
return true;
@@ -776,7 +776,7 @@ boolean needsEvaluation() {
return false;
}
- List evaluated() {
+ public List evaluated() {
if (params.isEmpty()) {
return Collections.emptyList();
}
@@ -796,12 +796,12 @@ public Iterator iterator() {
}
- static class Param {
+ public static final class Param {
- final String name;
- final Type type;
- final int position;
- final ParamKind kind;
+ public final String name;
+ public final Type type;
+ public final int position;
+ public final ParamKind kind;
public Param(String name, Type type, int position, ParamKind paramKind) {
this.name = name;
diff --git a/independent-projects/qute/pom.xml b/independent-projects/qute/pom.xml
index 08cb5873f5f9d..edfbbe43fde7b 100644
--- a/independent-projects/qute/pom.xml
+++ b/independent-projects/qute/pom.xml
@@ -38,7 +38,7 @@
11
5.8.1
3.21.0
- 1.0.9.Final
+ 1.0.10.Final
3.4.2.Final
3.0.0-M5
1.6.8
diff --git a/independent-projects/resteasy-reactive/client/processor/src/main/java/org/jboss/resteasy/reactive/client/processor/beanparam/BeanParamParser.java b/independent-projects/resteasy-reactive/client/processor/src/main/java/org/jboss/resteasy/reactive/client/processor/beanparam/BeanParamParser.java
index a6e66a234eb29..a09a7d7e12a23 100644
--- a/independent-projects/resteasy-reactive/client/processor/src/main/java/org/jboss/resteasy/reactive/client/processor/beanparam/BeanParamParser.java
+++ b/independent-projects/resteasy-reactive/client/processor/src/main/java/org/jboss/resteasy/reactive/client/processor/beanparam/BeanParamParser.java
@@ -17,10 +17,18 @@
import org.jboss.jandex.IndexView;
import org.jboss.jandex.MethodInfo;
import org.jboss.jandex.Type;
+import org.jboss.resteasy.reactive.common.processor.JandexUtil;
public class BeanParamParser {
+
public static List- parse(ClassInfo beanParamClass, IndexView index) {
List
- resultList = new ArrayList<>();
+
+ // Parse class tree recursively
+ if (!JandexUtil.DOTNAME_OBJECT.equals(beanParamClass.superName())) {
+ resultList.addAll(parse(index.getClassByName(beanParamClass.superName()), index));
+ }
+
Map> annotations = beanParamClass.annotations();
List queryParams = annotations.get(QUERY_PARAM);
if (queryParams != null) {
diff --git a/independent-projects/resteasy-reactive/pom.xml b/independent-projects/resteasy-reactive/pom.xml
index 40561c536a58c..69c7eff2deacb 100644
--- a/independent-projects/resteasy-reactive/pom.xml
+++ b/independent-projects/resteasy-reactive/pom.xml
@@ -43,7 +43,7 @@
3.21.0
3.4.2.Final
1.3.5
- 1.0.9.Final
+ 1.0.10.Final
2.2.3
3.0.0-M5
diff --git a/integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/CustomTenantResolver.java b/integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/CustomTenantResolver.java
index 957f2e65f7565..08961376f000c 100644
--- a/integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/CustomTenantResolver.java
+++ b/integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/CustomTenantResolver.java
@@ -11,6 +11,9 @@ public class CustomTenantResolver implements TenantResolver {
@Override
public String resolve(RoutingContext context) {
String path = context.normalizedPath();
+ if (path.contains("recovered-no-discovery")) {
+ return "no-discovery";
+ }
if (path.endsWith("code-flow")) {
return "code-flow";
}
diff --git a/integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/UsersResourceOidcRecoveredNoDiscovery.java b/integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/UsersResourceOidcRecoveredNoDiscovery.java
new file mode 100644
index 0000000000000..1b1f29f27ebf9
--- /dev/null
+++ b/integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/UsersResourceOidcRecoveredNoDiscovery.java
@@ -0,0 +1,28 @@
+package io.quarkus.it.keycloak;
+
+import javax.annotation.security.RolesAllowed;
+import javax.inject.Inject;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.eclipse.microprofile.jwt.JsonWebToken;
+
+import io.quarkus.it.keycloak.model.User;
+import io.quarkus.security.identity.SecurityIdentity;
+
+@Path("/recovered-no-discovery/api/users")
+public class UsersResourceOidcRecoveredNoDiscovery {
+
+ @Inject
+ SecurityIdentity identity;
+
+ @GET
+ @Path("/preferredUserName")
+ @RolesAllowed("user")
+ @Produces(MediaType.APPLICATION_JSON)
+ public User preferredUserName() {
+ return new User(((JsonWebToken) identity.getPrincipal()).getClaim("preferred_username"));
+ }
+}
diff --git a/integration-tests/oidc-wiremock/src/main/resources/application.properties b/integration-tests/oidc-wiremock/src/main/resources/application.properties
index 8e5296fa397cf..347269c186f0a 100644
--- a/integration-tests/oidc-wiremock/src/main/resources/application.properties
+++ b/integration-tests/oidc-wiremock/src/main/resources/application.properties
@@ -4,6 +4,13 @@ quarkus.oidc.client-id=quarkus-app
quarkus.oidc.credentials.secret=secret
quarkus.oidc.authentication.scopes=profile,email,phone
+quarkus.oidc.no-discovery.auth-server-url=http://localhost:8180/auth/realms/quarkus2/
+quarkus.oidc.no-discovery.discovery-enabled=false
+quarkus.oidc.no-discovery.jwks-path=protocol/openid-connect/certs
+quarkus.oidc.no-discovery.client-id=quarkus-app
+quarkus.oidc.no-discovery.credentials.secret=secret
+quarkus.oidc.no-discovery.authentication.scopes=profile,email,phone
+
quarkus.oidc.code-flow.auth-server-url=${keycloak.url}/realms/quarkus/
quarkus.oidc.code-flow.client-id=quarkus-web-app
quarkus.oidc.code-flow.credentials.secret=secret
diff --git a/integration-tests/oidc-wiremock/src/test/java/io/quarkus/it/keycloak/BearerTokenOidcRecoveredTest.java b/integration-tests/oidc-wiremock/src/test/java/io/quarkus/it/keycloak/BearerTokenOidcRecoveredTest.java
index 5e9f58289038c..19e6646124186 100644
--- a/integration-tests/oidc-wiremock/src/test/java/io/quarkus/it/keycloak/BearerTokenOidcRecoveredTest.java
+++ b/integration-tests/oidc-wiremock/src/test/java/io/quarkus/it/keycloak/BearerTokenOidcRecoveredTest.java
@@ -16,12 +16,20 @@
public class BearerTokenOidcRecoveredTest {
@Test
- public void testSecureAccessSuccessPreferredUsername() {
+ public void testOidcRecoveredWithDiscovery() {
+ String token = getAccessToken("alice", new HashSet<>(Arrays.asList("user", "admin")));
+ // Server has not started
+ RestAssured.given().auth().oauth2(token)
+ .when().get("/recovered/api/users/preferredUserName")
+ .then()
+ .statusCode(500);
+
+ // Server is starting now
WiremockTestResource server = new WiremockTestResource();
server.start();
try {
- RestAssured.given().auth().oauth2(getAccessToken("alice", new HashSet<>(Arrays.asList("user", "admin"))))
+ RestAssured.given().auth().oauth2(token)
.when().get("/recovered/api/users/preferredUserName")
.then()
.statusCode(200)
@@ -31,6 +39,30 @@ public void testSecureAccessSuccessPreferredUsername() {
}
}
+ @Test
+ public void testOidcRecoveredWithNoDiscovery() {
+ String token = getAccessToken("alice", new HashSet<>(Arrays.asList("user", "admin")));
+
+ // Server has not started
+ RestAssured.given().auth().oauth2(token)
+ .when().get("/recovered-no-discovery/api/users/preferredUserName")
+ .then()
+ .statusCode(500);
+
+ // Server is starting now
+ WiremockTestResource server = new WiremockTestResource();
+ server.start();
+ try {
+ RestAssured.given().auth().oauth2(token)
+ .when().get("/recovered-no-discovery/api/users/preferredUserName")
+ .then()
+ .statusCode(200)
+ .body("userName", equalTo("alice"));
+ } finally {
+ server.stop();
+ }
+ }
+
private String getAccessToken(String userName, Set groups) {
return Jwt.preferredUserName(userName)
.groups(groups)
diff --git a/integration-tests/rest-client-reactive/pom.xml b/integration-tests/rest-client-reactive/pom.xml
index 415a74cc99558..8271d622234ff 100644
--- a/integration-tests/rest-client-reactive/pom.xml
+++ b/integration-tests/rest-client-reactive/pom.xml
@@ -12,7 +12,6 @@
Quarkus - Integration Tests - REST Client Reactive
-
@@ -26,6 +25,11 @@
quarkus-rest-client-reactive-jackson
+
+ io.quarkus
+ quarkus-smallrye-fault-tolerance
+
+
io.quarkus
@@ -78,6 +82,19 @@
+
+ io.quarkus
+ quarkus-smallrye-fault-tolerance-deployment
+ ${project.version}
+ pom
+ test
+
+
+ *
+ *
+
+
+
io.quarkus
quarkus-vertx-http-deployment
diff --git a/integration-tests/rest-client-reactive/src/main/java/io/quarkus/it/rest/client/main/ClientCallingResource.java b/integration-tests/rest-client-reactive/src/main/java/io/quarkus/it/rest/client/main/ClientCallingResource.java
index 2c67f67c4507c..0a9a432219503 100644
--- a/integration-tests/rest-client-reactive/src/main/java/io/quarkus/it/rest/client/main/ClientCallingResource.java
+++ b/integration-tests/rest-client-reactive/src/main/java/io/quarkus/it/rest/client/main/ClientCallingResource.java
@@ -38,6 +38,9 @@ public class ClientCallingResource {
@RestClient
ClientWithExceptionMapper clientWithExceptionMapper;
+ @RestClient
+ FaultToleranceClient faultToleranceClient;
+
@Inject
InMemorySpanExporter inMemorySpanExporter;
@@ -132,6 +135,10 @@ void init(@Observes Router router) {
.stream().filter(sd -> !sd.getName().contains("export"))
.collect(Collectors.toList())));
});
+
+ router.route("/call-with-fault-tolerance").blockingHandler(rc -> {
+ rc.end(faultToleranceClient.helloWithFallback());
+ });
}
private Future success(RoutingContext rc, String body) {
diff --git a/integration-tests/rest-client-reactive/src/main/java/io/quarkus/it/rest/client/main/FaultToleranceClient.java b/integration-tests/rest-client-reactive/src/main/java/io/quarkus/it/rest/client/main/FaultToleranceClient.java
new file mode 100644
index 0000000000000..ceab46764717b
--- /dev/null
+++ b/integration-tests/rest-client-reactive/src/main/java/io/quarkus/it/rest/client/main/FaultToleranceClient.java
@@ -0,0 +1,28 @@
+package io.quarkus.it.rest.client.main;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.eclipse.microprofile.faulttolerance.Fallback;
+import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
+
+@Path("/unprocessable")
+@RegisterRestClient(configKey = "w-fault-tolerance")
+public interface FaultToleranceClient {
+ @GET
+ @Produces(MediaType.TEXT_PLAIN)
+ @Consumes(MediaType.TEXT_PLAIN)
+ String hello();
+
+ @Fallback(fallbackMethod = "fallback")
+ default String helloWithFallback() {
+ return hello();
+ }
+
+ default String fallback() {
+ return "Hello fallback!";
+ }
+}
diff --git a/integration-tests/rest-client-reactive/src/main/resources/application.properties b/integration-tests/rest-client-reactive/src/main/resources/application.properties
index c8425d08b6e93..dca0a78fb80fc 100644
--- a/integration-tests/rest-client-reactive/src/main/resources/application.properties
+++ b/integration-tests/rest-client-reactive/src/main/resources/application.properties
@@ -1,2 +1,3 @@
w-exception-mapper/mp-rest/url=${test.url}
+w-fault-tolerance/mp-rest/url=${test.url}
io.quarkus.it.rest.client.multipart.MultipartClient/mp-rest/url=${test.url}
\ No newline at end of file
diff --git a/integration-tests/rest-client-reactive/src/test/java/io/quarkus/it/rest/client/BasicTest.java b/integration-tests/rest-client-reactive/src/test/java/io/quarkus/it/rest/client/BasicTest.java
index 82f666e6bad9e..0f1d890c01da6 100644
--- a/integration-tests/rest-client-reactive/src/test/java/io/quarkus/it/rest/client/BasicTest.java
+++ b/integration-tests/rest-client-reactive/src/test/java/io/quarkus/it/rest/client/BasicTest.java
@@ -84,6 +84,14 @@ void shouldMapExceptionCdi() {
.statusCode(200);
}
+ @Test
+ void shouldInterceptDefaultMethod() {
+ RestAssured.with().body(baseUrl).post("/call-with-fault-tolerance")
+ .then()
+ .statusCode(200)
+ .body(equalTo("Hello fallback!"));
+ }
+
@Test
void shouldCreateClientSpans() {
// Reset captured traces