From e54abc31683a04d214a8652dc2b3fc5dc2057862 Mon Sep 17 00:00:00 2001 From: Stuart Douglas Date: Wed, 29 Sep 2021 13:37:31 +1000 Subject: [PATCH] Dispatch back to IO thread for non blocking requests --- .../runtime/kotlin/FlowToPublisherHandler.kt | 5 ++++- .../runtime/ServletRequestContext.java | 6 ++++++ .../core/AbstractResteasyReactiveContext.java | 2 +- .../startup/RuntimeResourceDeployment.java | 2 ++ .../server/handlers/NonBlockingHandler.java | 18 +++++++++++++++++ .../server/spi/ServerHttpRequest.java | 2 ++ .../VertxResteasyReactiveRequestContext.java | 14 +++++++++++-- .../hibernate-reactive-postgresql/pom.xml | 17 ++++++++++++++++ .../HibernateReactiveTestEndpoint.java | 2 ++ .../src/main/resources/application.properties | 5 +++++ .../postgresql/HibernateReactiveTest.java | 20 +++++++++++++------ 11 files changed, 83 insertions(+), 10 deletions(-) create mode 100644 independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/NonBlockingHandler.java diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-kotlin/runtime/src/main/kotlin/org/jboss/resteasy/reactive/server/runtime/kotlin/FlowToPublisherHandler.kt b/extensions/resteasy-reactive/quarkus-resteasy-reactive-kotlin/runtime/src/main/kotlin/org/jboss/resteasy/reactive/server/runtime/kotlin/FlowToPublisherHandler.kt index 7ae72f6c13a6e..6d32cb6e9395d 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-kotlin/runtime/src/main/kotlin/org/jboss/resteasy/reactive/server/runtime/kotlin/FlowToPublisherHandler.kt +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-kotlin/runtime/src/main/kotlin/org/jboss/resteasy/reactive/server/runtime/kotlin/FlowToPublisherHandler.kt @@ -7,6 +7,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.launch import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext import org.jboss.resteasy.reactive.server.spi.ServerRestHandler +import java.util.concurrent.Executor import javax.enterprise.inject.spi.CDI class FlowToPublisherHandler : ServerRestHandler { @@ -27,7 +28,9 @@ class FlowToPublisherHandler : ServerRestHandler { // ensure the proper CL is not lost in dev-mode Thread.currentThread().contextClassLoader = originalTCCL requestContext.result = result.asMulti() - requestContext.resume() + //run in a direct invocation executor to run the rest of the invocation in the co-route scope + //feels a bit fragile, but let's see how it goes + requestContext.resume(Executor { it.run() }) } } } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-servlet/runtime/src/main/java/io/quarkus/resteasy/reactive/server/servlet/runtime/ServletRequestContext.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-servlet/runtime/src/main/java/io/quarkus/resteasy/reactive/server/servlet/runtime/ServletRequestContext.java index f8f54895427da..365e76f3336d1 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-servlet/runtime/src/main/java/io/quarkus/resteasy/reactive/server/servlet/runtime/ServletRequestContext.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-servlet/runtime/src/main/java/io/quarkus/resteasy/reactive/server/servlet/runtime/ServletRequestContext.java @@ -317,6 +317,12 @@ public T unwrap(Class theType) { return null; } + @Override + public boolean isOnIoThread() { + //does not really apply to Servlet + return true; + } + @Override public ServerHttpResponse setStatusCode(int code) { response.setStatus(code); diff --git a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/core/AbstractResteasyReactiveContext.java b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/core/AbstractResteasyReactiveContext.java index 6560d9b203c45..e8d1f3976addf 100644 --- a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/core/AbstractResteasyReactiveContext.java +++ b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/core/AbstractResteasyReactiveContext.java @@ -117,7 +117,7 @@ public Throwable getThrowable() { protected abstract Executor getEventLoop(); - protected Executor getContextExecutor() { + public Executor getContextExecutor() { return null; } 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 2f4edae3592e6..44775f7230f60 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 @@ -65,6 +65,7 @@ import org.jboss.resteasy.reactive.server.handlers.InputHandler; import org.jboss.resteasy.reactive.server.handlers.InstanceHandler; import org.jboss.resteasy.reactive.server.handlers.InvocationHandler; +import org.jboss.resteasy.reactive.server.handlers.NonBlockingHandler; import org.jboss.resteasy.reactive.server.handlers.ParameterHandler; import org.jboss.resteasy.reactive.server.handlers.PerRequestInstanceHandler; import org.jboss.resteasy.reactive.server.handlers.RequestDeserializeHandler; @@ -184,6 +185,7 @@ public RuntimeResource buildResourceMethod(ResourceClass clazz, blockingHandlerIndex = Optional.of(handlers.size() - 1); score.add(ScoreSystem.Category.Execution, ScoreSystem.Diagnostic.ExecutionBlocking); } else { + handlers.add(NonBlockingHandler.INSTANCE); score.add(ScoreSystem.Category.Execution, ScoreSystem.Diagnostic.ExecutionNonBlocking); } } diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/NonBlockingHandler.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/NonBlockingHandler.java new file mode 100644 index 0000000000000..c4fad4c9f0d7e --- /dev/null +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/NonBlockingHandler.java @@ -0,0 +1,18 @@ +package org.jboss.resteasy.reactive.server.handlers; + +import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext; +import org.jboss.resteasy.reactive.server.spi.ServerRestHandler; + +public class NonBlockingHandler implements ServerRestHandler { + + public static final NonBlockingHandler INSTANCE = new NonBlockingHandler(); + + @Override + public void handle(ResteasyReactiveRequestContext requestContext) throws Exception { + if (requestContext.serverRequest().isOnIoThread()) { + return; + } + requestContext.suspend(); + requestContext.resume(requestContext.getContextExecutor()); + } +} diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/spi/ServerHttpRequest.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/spi/ServerHttpRequest.java index aae070aed46a5..34721ac8dbec4 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/spi/ServerHttpRequest.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/spi/ServerHttpRequest.java @@ -69,6 +69,8 @@ default FormData getExistingParsedForm() { return null; } + boolean isOnIoThread(); + interface ReadCallback { void done(); diff --git a/independent-projects/resteasy-reactive/server/vertx/src/main/java/org/jboss/resteasy/reactive/server/vertx/VertxResteasyReactiveRequestContext.java b/independent-projects/resteasy-reactive/server/vertx/src/main/java/org/jboss/resteasy/reactive/server/vertx/VertxResteasyReactiveRequestContext.java index 25934998c399c..d04c06db0d509 100644 --- a/independent-projects/resteasy-reactive/server/vertx/src/main/java/org/jboss/resteasy/reactive/server/vertx/VertxResteasyReactiveRequestContext.java +++ b/independent-projects/resteasy-reactive/server/vertx/src/main/java/org/jboss/resteasy/reactive/server/vertx/VertxResteasyReactiveRequestContext.java @@ -66,7 +66,12 @@ public VertxResteasyReactiveRequestContext(Deployment deployment, ProvidersImpl this.contextExecutor = new Executor() { @Override public void execute(Runnable command) { - internal.execute(command); + internal.runOnContext(new Handler() { + @Override + public void handle(Void unused) { + command.run(); + } + }); } }; } @@ -101,7 +106,7 @@ protected EventLoop getEventLoop() { return ((ConnectionBase) context.request().connection()).channel().eventLoop(); } - protected Executor getContextExecutor() { + public Executor getContextExecutor() { return contextExecutor; } @@ -283,6 +288,11 @@ public FormData getExistingParsedForm() { return ret; } + @Override + public boolean isOnIoThread() { + return ((ConnectionBase) request.connection()).channel().eventLoop().inEventLoop(); + } + @SuppressWarnings("unchecked") @Override public T unwrap(Class theType) { diff --git a/integration-tests/hibernate-reactive-postgresql/pom.xml b/integration-tests/hibernate-reactive-postgresql/pom.xml index d4bf47eaee933..a41a5e81d6055 100644 --- a/integration-tests/hibernate-reactive-postgresql/pom.xml +++ b/integration-tests/hibernate-reactive-postgresql/pom.xml @@ -31,6 +31,10 @@ io.quarkus quarkus-resteasy-reactive-jsonb + + io.quarkus + quarkus-elytron-security-properties-file + @@ -84,6 +88,19 @@ + + io.quarkus + quarkus-elytron-security-properties-file-deployment + ${project.version} + pom + test + + + * + * + + + diff --git a/integration-tests/hibernate-reactive-postgresql/src/main/java/io/quarkus/it/hibernate/reactive/postgresql/HibernateReactiveTestEndpoint.java b/integration-tests/hibernate-reactive-postgresql/src/main/java/io/quarkus/it/hibernate/reactive/postgresql/HibernateReactiveTestEndpoint.java index 9e2f53d553300..64f5e3ab99847 100644 --- a/integration-tests/hibernate-reactive-postgresql/src/main/java/io/quarkus/it/hibernate/reactive/postgresql/HibernateReactiveTestEndpoint.java +++ b/integration-tests/hibernate-reactive-postgresql/src/main/java/io/quarkus/it/hibernate/reactive/postgresql/HibernateReactiveTestEndpoint.java @@ -6,6 +6,7 @@ import org.hibernate.reactive.mutiny.Mutiny; +import io.quarkus.security.Authenticated; import io.smallrye.mutiny.Uni; import io.vertx.mutiny.pgclient.PgPool; import io.vertx.mutiny.sqlclient.Row; @@ -13,6 +14,7 @@ import io.vertx.mutiny.sqlclient.Tuple; @Path("/tests") +@Authenticated public class HibernateReactiveTestEndpoint { @Inject diff --git a/integration-tests/hibernate-reactive-postgresql/src/main/resources/application.properties b/integration-tests/hibernate-reactive-postgresql/src/main/resources/application.properties index 2c1d5c864e2a1..0b909d455370e 100644 --- a/integration-tests/hibernate-reactive-postgresql/src/main/resources/application.properties +++ b/integration-tests/hibernate-reactive-postgresql/src/main/resources/application.properties @@ -10,3 +10,8 @@ quarkus.hibernate-orm.database.generation=drop-and-create quarkus.datasource.reactive=true quarkus.datasource.reactive.url=${postgres.reactive.url} + +quarkus.security.users.embedded.enabled=true +quarkus.security.users.embedded.users.scott=jb0ss +quarkus.security.users.embedded.plain-text=true +quarkus.security.users.embedded.roles.scott=Admin,admin,Tester,user diff --git a/integration-tests/hibernate-reactive-postgresql/src/test/java/io/quarkus/it/hibernate/reactive/postgresql/HibernateReactiveTest.java b/integration-tests/hibernate-reactive-postgresql/src/test/java/io/quarkus/it/hibernate/reactive/postgresql/HibernateReactiveTest.java index b489a2ee14207..ff08d1733d6bd 100644 --- a/integration-tests/hibernate-reactive-postgresql/src/test/java/io/quarkus/it/hibernate/reactive/postgresql/HibernateReactiveTest.java +++ b/integration-tests/hibernate-reactive-postgresql/src/test/java/io/quarkus/it/hibernate/reactive/postgresql/HibernateReactiveTest.java @@ -11,6 +11,8 @@ /** * Test various JPA operations running in Quarkus + * + * Also makes sure that these work with a blocking security implementation */ @QuarkusTest @TestHTTPEndpoint(HibernateReactiveTestEndpoint.class) @@ -18,7 +20,8 @@ public class HibernateReactiveTest { @Test public void reactiveCowPersist() { - RestAssured.when() + RestAssured.given().when() + .auth().preemptive().basic("scott", "jb0ss") .get("/reactiveCowPersist") .then() .body(containsString("\"name\":\"Carolina\"}")); //Use containsString as we don't know the Id this object will have @@ -26,7 +29,8 @@ public void reactiveCowPersist() { @Test public void reactiveFindMutiny() { - RestAssured.when() + RestAssured.given().when() + .auth().preemptive().basic("scott", "jb0ss") .get("/reactiveFindMutiny") .then() .body(is("{\"id\":5,\"name\":\"Aloi\"}")); @@ -34,7 +38,8 @@ public void reactiveFindMutiny() { @Test public void reactivePersist() { - RestAssured.when() + RestAssured.given().when() + .auth().preemptive().basic("scott", "jb0ss") .get("/reactivePersist") .then() .body(is("Tulip")); @@ -42,7 +47,8 @@ public void reactivePersist() { @Test public void reactiveRemoveTransientEntity() { - RestAssured.when() + RestAssured.given().when() + .auth().preemptive().basic("scott", "jb0ss") .get("/reactiveRemoveTransientEntity") .then() .body(is("OK")); @@ -50,7 +56,8 @@ public void reactiveRemoveTransientEntity() { @Test public void reactiveRemoveManagedEntity() { - RestAssured.when() + RestAssured.given().when() + .auth().preemptive().basic("scott", "jb0ss") .get("/reactiveRemoveManagedEntity") .then() .body(is("OK")); @@ -58,7 +65,8 @@ public void reactiveRemoveManagedEntity() { @Test public void reactiveUpdate() { - RestAssured.when() + RestAssured.given().when() + .auth().preemptive().basic("scott", "jb0ss") .get("/reactiveUpdate") .then() .body(is("Tina"));