From f4866e2762863c260196372e9300e6fd5fd4b453 Mon Sep 17 00:00:00 2001 From: Tobias Schaefer Date: Mon, 29 Nov 2021 17:02:33 +0100 Subject: [PATCH] Close #353: Register FetchAndLockHandler when initializing the REST api so that long polling works, i.e. the if there are currently no tasks then the server will keep the connection open for the given time. --- micronaut-camunda-bpm-feature/build.gradle | 2 +- .../jetty-webapp-and-rest/build.gradle | 2 ++ .../camunda/bpm/feature/test/JettyRestTest.kt | 32 +++++++++++++++++++ .../bpm/feature/JettyServerCustomizer.java | 19 +++++++---- 4 files changed, 47 insertions(+), 8 deletions(-) diff --git a/micronaut-camunda-bpm-feature/build.gradle b/micronaut-camunda-bpm-feature/build.gradle index 19935c71..c26f54f0 100644 --- a/micronaut-camunda-bpm-feature/build.gradle +++ b/micronaut-camunda-bpm-feature/build.gradle @@ -44,7 +44,7 @@ dependencies { // REST API + Webapps implementation("com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider") - implementation("org.camunda.bpm:camunda-engine-rest:$camundaVersion:classes") + implementation("org.camunda.bpm:camunda-engine-rest-jaxrs2:$camundaVersion") implementation("org.camunda.bpm.webapp:camunda-webapp-webjar:$camundaVersion") implementation("org.glassfish.jersey.inject:jersey-hk2:$jerseyVersion") implementation("org.glassfish.jersey.containers:jersey-container-servlet-core:$jerseyVersion") diff --git a/micronaut-camunda-bpm-feature/jetty-webapp-and-rest/build.gradle b/micronaut-camunda-bpm-feature/jetty-webapp-and-rest/build.gradle index cadb3e1c..40e61a54 100644 --- a/micronaut-camunda-bpm-feature/jetty-webapp-and-rest/build.gradle +++ b/micronaut-camunda-bpm-feature/jetty-webapp-and-rest/build.gradle @@ -24,6 +24,8 @@ dependencies { kaptTest("io.micronaut:micronaut-inject-java:$micronautVersion") testCompileOnly("io.micronaut.servlet:micronaut-http-server-jetty") + testImplementation("org.junit.jupiter:junit-jupiter-params") + testImplementation("org.assertj:assertj-core") testImplementation("io.micronaut:micronaut-http-client") testImplementation("org.eclipse.jetty:jetty-server:$jettyVersion") testImplementation("org.eclipse.jetty:jetty-servlet:$jettyVersion") diff --git a/micronaut-camunda-bpm-feature/jetty-webapp-and-rest/src/test/kotlin/info/novatec/micronaut/camunda/bpm/feature/test/JettyRestTest.kt b/micronaut-camunda-bpm-feature/jetty-webapp-and-rest/src/test/kotlin/info/novatec/micronaut/camunda/bpm/feature/test/JettyRestTest.kt index ff7a9b80..c2aedf78 100644 --- a/micronaut-camunda-bpm-feature/jetty-webapp-and-rest/src/test/kotlin/info/novatec/micronaut/camunda/bpm/feature/test/JettyRestTest.kt +++ b/micronaut-camunda-bpm-feature/jetty-webapp-and-rest/src/test/kotlin/info/novatec/micronaut/camunda/bpm/feature/test/JettyRestTest.kt @@ -22,9 +22,15 @@ import io.micronaut.http.client.HttpClient import io.micronaut.http.client.annotation.Client import io.micronaut.test.extensions.junit5.annotation.MicronautTest import jakarta.inject.Inject +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.within import org.eclipse.jetty.server.Server import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource +import java.time.Duration +import java.time.Instant /** * Test the REST API on Jetty. @@ -49,4 +55,30 @@ class JettyRestTest { assertEquals("""[{"name":"default"}]""", body) } + + @ParameterizedTest + @ValueSource(longs = [0, 2000, 5000]) + fun `long polling`(duration: Long) { + val request: HttpRequest = HttpRequest.POST( + configuration.rest.contextPath + "/external-task/fetchAndLock", + """ + { + "maxTasks": 1, + "workerId": "aWorkerId", + "asyncResponseTimeout": $duration, + "topics": [ + { + "topicName": "aTopicName", + "lockDuration": 1000 + } + ] + } + """ + ) + val start = Instant.now() + val body = client.toBlocking().retrieve(request) + + assertEquals("[]", body) + assertThat(Duration.between(start, Instant.now()).toMillis()).isCloseTo(duration, within(500L)) + } } diff --git a/micronaut-camunda-bpm-feature/src/main/java/info/novatec/micronaut/camunda/bpm/feature/JettyServerCustomizer.java b/micronaut-camunda-bpm-feature/src/main/java/info/novatec/micronaut/camunda/bpm/feature/JettyServerCustomizer.java index 98c587a8..078a66f8 100644 --- a/micronaut-camunda-bpm-feature/src/main/java/info/novatec/micronaut/camunda/bpm/feature/JettyServerCustomizer.java +++ b/micronaut-camunda-bpm-feature/src/main/java/info/novatec/micronaut/camunda/bpm/feature/JettyServerCustomizer.java @@ -22,9 +22,10 @@ import jakarta.inject.Singleton; import org.camunda.bpm.admin.impl.web.bootstrap.AdminContainerBootstrap; import org.camunda.bpm.cockpit.impl.web.bootstrap.CockpitContainerBootstrap; -import org.camunda.bpm.engine.rest.security.auth.ProcessEngineAuthenticationFilter; import org.camunda.bpm.engine.rest.filter.CacheControlFilter; import org.camunda.bpm.engine.rest.filter.EmptyBodyFilter; +import org.camunda.bpm.engine.rest.impl.FetchAndLockContextListener; +import org.camunda.bpm.engine.rest.security.auth.ProcessEngineAuthenticationFilter; import org.camunda.bpm.tasklist.impl.web.bootstrap.TasklistContainerBootstrap; import org.camunda.bpm.webapp.impl.engine.ProcessEnginesFilter; import org.camunda.bpm.webapp.impl.security.auth.AuthenticationFilter; @@ -50,11 +51,11 @@ import java.util.HashMap; import java.util.Map; -import static java.util.Collections.*; -import static javax.servlet.DispatcherType.*; +import static java.util.Collections.singletonMap; +import static javax.servlet.DispatcherType.REQUEST; /** - * Using Micronaut Servlet with Jetty to run the REST API as a servlet. + * Using Micronaut Servlet with Jetty to run the REST API/Webapps as a servlet. * * see https://micronaut-projects.github.io/micronaut-servlet/latest/guide/#jetty * @@ -84,6 +85,13 @@ public Server onCreated(BeanCreatedEvent event) { ServletContextHandler restServletContextHandler = new ServletContextHandler(); restServletContextHandler.setContextPath(configuration.getRest().getContextPath()); restServletContextHandler.addServlet(new ServletHolder(new ServletContainer(new RestApp())), "/*"); + restServletContextHandler.addEventListener(new ServletContextListener() { + @Override + public void contextInitialized(ServletContextEvent sce) { + // Required for long polling + new FetchAndLockContextListener().contextInitialized(sce); + } + }); if (configuration.getRest().isBasicAuthEnabled()) { // see https://docs.camunda.org/manual/latest/reference/rest/overview/authentication/ @@ -167,9 +175,6 @@ public void contextInitialized(ServletContextEvent sce) { registerFilter("CacheControlFilter", CacheControlFilter.class, "/api/*", "/app/*"); } - @Override - public void contextDestroyed(ServletContextEvent sce) {} - protected Map getCsrfInitParams(){ Map initParams = new HashMap<>();