This is an automated email from the ASF dual-hosted git repository. jamesnetherton pushed a commit to branch 2.7.x in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git
commit a12ff645711ccc9871fbb9dc5b72ee77f61e43d6 Author: James Netherton <[email protected]> AuthorDate: Fri Feb 11 14:09:57 2022 +0000 Poll for optaplanner results to avoid timeouts on slow machines --- integration-tests/optaplanner/pom.xml | 12 ++- .../optaplanner/it/OptaplannerResource.java | 85 +++++++++++++------ .../quarkus/component/optaplanner/it/Routes.java | 15 ++-- .../component/optaplanner/it/OptaplannerTest.java | 98 ++++++++++++++++++---- 4 files changed, 159 insertions(+), 51 deletions(-) diff --git a/integration-tests/optaplanner/pom.xml b/integration-tests/optaplanner/pom.xml index b9c880f..bcf5997 100644 --- a/integration-tests/optaplanner/pom.xml +++ b/integration-tests/optaplanner/pom.xml @@ -36,10 +36,6 @@ <artifactId>camel-quarkus-optaplanner</artifactId> </dependency> <dependency> - <groupId>io.quarkus</groupId> - <artifactId>quarkus-resteasy-jackson</artifactId> - </dependency> - <dependency> <groupId>org.apache.camel.quarkus</groupId> <artifactId>camel-quarkus-direct</artifactId> </dependency> @@ -47,6 +43,14 @@ <groupId>org.apache.camel.quarkus</groupId> <artifactId>camel-quarkus-mock</artifactId> </dependency> + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-resteasy</artifactId> + </dependency> + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-resteasy-jsonb</artifactId> + </dependency> <!-- test dependencies --> <dependency> diff --git a/integration-tests/optaplanner/src/main/java/org/apache/camel/quarkus/component/optaplanner/it/OptaplannerResource.java b/integration-tests/optaplanner/src/main/java/org/apache/camel/quarkus/component/optaplanner/it/OptaplannerResource.java index e1f9640..3fe3c8a 100644 --- a/integration-tests/optaplanner/src/main/java/org/apache/camel/quarkus/component/optaplanner/it/OptaplannerResource.java +++ b/integration-tests/optaplanner/src/main/java/org/apache/camel/quarkus/component/optaplanner/it/OptaplannerResource.java @@ -16,23 +16,30 @@ */ package org.apache.camel.quarkus.component.optaplanner.it; -import java.util.concurrent.CompletableFuture; +import java.util.Optional; import java.util.concurrent.ExecutionException; import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; +import javax.json.Json; +import javax.json.JsonObject; +import javax.json.JsonObjectBuilder; import javax.ws.rs.GET; +import javax.ws.rs.POST; import javax.ws.rs.Path; +import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import org.apache.camel.CamelContext; +import org.apache.camel.Exchange; import org.apache.camel.ProducerTemplate; import org.apache.camel.component.mock.MockEndpoint; import org.apache.camel.component.optaplanner.OptaPlannerConstants; import org.apache.camel.quarkus.component.optaplanner.it.bootstrap.DataGenerator; +import org.apache.camel.quarkus.component.optaplanner.it.domain.Room; import org.apache.camel.quarkus.component.optaplanner.it.domain.TimeTable; -import org.jboss.logging.Logger; +import org.apache.camel.quarkus.component.optaplanner.it.domain.Timeslot; import org.optaplanner.core.api.solver.SolverManager; import org.optaplanner.core.api.solver.SolverStatus; @@ -41,8 +48,6 @@ import org.optaplanner.core.api.solver.SolverStatus; @Produces(MediaType.APPLICATION_JSON) public class OptaplannerResource { - private static final Logger LOG = Logger.getLogger(OptaplannerResource.class); - public static final Long SINGLETON_TIME_TABLE_ID = 1L; @Inject @@ -54,38 +59,68 @@ public class OptaplannerResource { @Inject CamelContext context; - @GET + @POST @Path("solveSync") - public TimeTable solveSync() { + public void solveSync() { if (SolverStatus.NOT_SOLVING == solverManager.getSolverStatus(SINGLETON_TIME_TABLE_ID)) { - TimeTable finalSolution = producerTemplate.requestBodyAndHeader( - "direct:solveSync", DataGenerator.timeTable, - OptaPlannerConstants.SOLVER_MANAGER, solverManager, TimeTable.class); - return finalSolution; + producerTemplate.sendBodyAndHeader("direct:solveSync", DataGenerator.timeTable, OptaPlannerConstants.SOLVER_MANAGER, + solverManager); } - return DataGenerator.timeTable; } - @GET + @POST @Path("solveAsync") - public TimeTable solveAsync() throws ExecutionException, InterruptedException { + public void solveAsync() throws ExecutionException, InterruptedException { if (SolverStatus.NOT_SOLVING == solverManager.getSolverStatus(SINGLETON_TIME_TABLE_ID)) { - CompletableFuture<TimeTable> response = producerTemplate.asyncRequestBodyAndHeader( - "direct:solveAsync", DataGenerator.timeTable, OptaPlannerConstants.SOLVER_MANAGER, - solverManager, TimeTable.class); + producerTemplate.sendBodyAndHeader("direct:solveAsync", DataGenerator.timeTable, + OptaPlannerConstants.SOLVER_MANAGER, solverManager); + } + } - TimeTable finalSolution = response.get(); - return finalSolution; + @POST + @Path("consumer/{enable}") + public void mangeOptaplannerConsumer(@PathParam("enable") boolean enable) throws Exception { + if (enable) { + context.getRouteController().startRoute("optaplanner-consumer"); + } else { + context.getRouteController().stopRoute("optaplanner-consumer"); } - return DataGenerator.timeTable; } @GET - @Path("newBestSolution") - public TimeTable getNewBestSolution() { - MockEndpoint mockEndpoint = context.getEndpoint("mock:best-solution", MockEndpoint.class); - return mockEndpoint.getReceivedExchanges().stream().map( - exchange -> exchange.getMessage().getHeader(OptaPlannerConstants.BEST_SOLUTION, TimeTable.class)) - .findFirst().orElse(null); + @Path("solution/{mockEndpointUri}") + @Produces(MediaType.APPLICATION_JSON) + public JsonObject getSolution(@PathParam("mockEndpointUri") String mockEndpointUri) { + MockEndpoint mockEndpoint = context.getEndpoint("mock:" + mockEndpointUri, MockEndpoint.class); + Optional<JsonObject> result = mockEndpoint.getReceivedExchanges() + .stream() + .map(Exchange::getMessage) + .map(message -> { + if (mockEndpointUri.equals("bestSolution")) { + return message.getHeader(OptaPlannerConstants.BEST_SOLUTION, TimeTable.class); + } else { + return message.getBody(TimeTable.class); + } + }) + .map(this::extractResults) + .findFirst(); + + if (result.isPresent()) { + mockEndpoint.reset(); + return result.get(); + } + return extractResults(null); + } + + private JsonObject extractResults(TimeTable timeTable) { + JsonObjectBuilder builder = Json.createObjectBuilder(); + if (timeTable != null) { + Timeslot timeslot = timeTable.getTimeslotList().get(0); + Room room = timeTable.getRoomList().get(0); + builder.add("timeslot", timeslot.getId()).add("room", room.getId()); + } else { + builder.add("timeslot", "-1").add("room", "-1"); + } + return builder.build(); } } diff --git a/integration-tests/optaplanner/src/main/java/org/apache/camel/quarkus/component/optaplanner/it/Routes.java b/integration-tests/optaplanner/src/main/java/org/apache/camel/quarkus/component/optaplanner/it/Routes.java index 809cd59..ee83982 100644 --- a/integration-tests/optaplanner/src/main/java/org/apache/camel/quarkus/component/optaplanner/it/Routes.java +++ b/integration-tests/optaplanner/src/main/java/org/apache/camel/quarkus/component/optaplanner/it/Routes.java @@ -26,15 +26,20 @@ public class Routes extends RouteBuilder { public void configure() throws Exception { // async producer - from("direct:solveAsync").to("optaplanner:anything?useSolverManager=true&async=true&problemId=" - + OptaplannerResource.SINGLETON_TIME_TABLE_ID); + from("direct:solveAsync") + .toF("optaplanner:anything?useSolverManager=true&async=true&problemId=%d", + OptaplannerResource.SINGLETON_TIME_TABLE_ID) + .to("mock:solveAsync"); // async consumer - from("optaplanner:anything?useSolverManager=true&problemId=" + OptaplannerResource.SINGLETON_TIME_TABLE_ID) - .to("mock:best-solution"); + fromF("optaplanner:anything?useSolverManager=true&problemId=%d", OptaplannerResource.SINGLETON_TIME_TABLE_ID) + .id("optaplanner-consumer") + .autoStartup(false) + .to("mock:bestSolution"); // sync producer from("direct:solveSync") - .to("optaplanner:anything?useSolverManager=true&problemId=" + OptaplannerResource.SINGLETON_TIME_TABLE_ID); + .toF("optaplanner:anything?useSolverManager=true&problemId=%d", OptaplannerResource.SINGLETON_TIME_TABLE_ID) + .to("mock:solveSync"); } } diff --git a/integration-tests/optaplanner/src/test/java/org/apache/camel/quarkus/component/optaplanner/it/OptaplannerTest.java b/integration-tests/optaplanner/src/test/java/org/apache/camel/quarkus/component/optaplanner/it/OptaplannerTest.java index a519555..70ca5d0 100644 --- a/integration-tests/optaplanner/src/test/java/org/apache/camel/quarkus/component/optaplanner/it/OptaplannerTest.java +++ b/integration-tests/optaplanner/src/test/java/org/apache/camel/quarkus/component/optaplanner/it/OptaplannerTest.java @@ -20,40 +20,104 @@ import java.util.concurrent.TimeUnit; import io.quarkus.test.junit.QuarkusTest; import io.restassured.RestAssured; -import org.apache.camel.quarkus.component.optaplanner.it.domain.TimeTable; +import io.restassured.path.json.JsonPath; import org.junit.jupiter.api.Test; import static org.awaitility.Awaitility.await; -import static org.hamcrest.Matchers.notNullValue; @QuarkusTest class OptaplannerTest { @Test public void solveSync() { + // Initiate synchronous solving RestAssured.given() - .get("/optaplanner/solveSync") + .post("/optaplanner/solveSync") .then() - .statusCode(200) - .body("lessonList[0].timeslot", notNullValue(null)) - .body("lessonList[0].room", notNullValue(null)); + .statusCode(204); + + // Poll for results + await().pollDelay(250, TimeUnit.MILLISECONDS).atMost(1, TimeUnit.MINUTES).until(() -> { + JsonPath json = RestAssured.given() + .get("/optaplanner/solution/solveSync") + .then() + .statusCode(200) + .extract() + .body() + .jsonPath(); + + return json.getLong("timeslot") > -1 && json.getLong("room") > -1; + }); } @Test - public void solveASyncWithConsumer() { - // solve async + public void solveAsync() { + // Initiate asynchronous solving RestAssured.given() - .get("/optaplanner/solveAsync") + .post("/optaplanner/solveAsync") .then() - .statusCode(200) - .body("lessonList[0].timeslot", notNullValue(null)) - .body("lessonList[0].room", notNullValue(null)); - - await().atMost(10L, TimeUnit.SECONDS).until(() -> { - TimeTable result = RestAssured.get("/optaplanner/newBestSolution").then().extract().body().as(TimeTable.class); - return result != null && result.getLessonList().get(0).getTimeslot() != null - && result.getLessonList().get(0).getRoom() != null; + .statusCode(204); + + // Poll for results + await().pollDelay(250, TimeUnit.MILLISECONDS).atMost(1, TimeUnit.MINUTES).until(() -> { + JsonPath json = RestAssured.given() + .get("/optaplanner/solution/solveAsync") + .then() + .statusCode(200) + .extract() + .body() + .jsonPath(); + + return json.getLong("timeslot") > -1 && json.getLong("room") > -1; }); } + @Test + public void optaplannerConsumerBestSolution() { + try { + // Start optaplanner consumer + RestAssured.given() + .post("/optaplanner/consumer/true") + .then() + .statusCode(204); + + // Initiate asynchronous solving + RestAssured.given() + .post("/optaplanner/solveAsync") + .then() + .statusCode(204); + + // Poll for results + await().pollDelay(250, TimeUnit.MILLISECONDS).atMost(1, TimeUnit.MINUTES).until(() -> { + JsonPath json = RestAssured.given() + .get("/optaplanner/solution/solveAsync") + .then() + .statusCode(200) + .extract() + .body() + .jsonPath(); + + return json.getLong("timeslot") > -1 && json.getLong("room") > -1; + }); + + await().pollDelay(250, TimeUnit.MILLISECONDS).atMost(1, TimeUnit.MINUTES).until(() -> { + JsonPath json = RestAssured.given() + .get("/optaplanner/solution/bestSolution") + .then() + .statusCode(200) + .extract() + .body() + .jsonPath(); + + return json.getLong("timeslot") > -1 && json.getLong("room") > -1; + }); + } finally { + // Stop optaplanner consumer + RestAssured.given() + .post("/optaplanner/consumer/false") + .then() + .statusCode(204); + } + } + }
