From a08a999e8271e4c3cae4888d23cc6cdab871134c Mon Sep 17 00:00:00 2001 From: Manuel Friedli Date: Mon, 17 Apr 2023 00:28:01 +0200 Subject: [PATCH] Expose start and end tile; accept start and end input parameters when creating labyrinths. --- pom.xml | 2 +- .../labyrinth/server/LabyrinthServer.java | 6 +- .../fritteli/labyrinth/server/OutputType.java | 13 +++- .../ParametersToLabyrinthExtractor.java | 15 ++++- ...enderHandler.java => RenderV1Handler.java} | 8 +-- .../server/handler/RenderV2Handler.java | 60 +++++++++++++++++++ .../server/handler/RequestParameter.java | 19 +++++- 7 files changed, 111 insertions(+), 12 deletions(-) rename src/main/java/ch/fritteli/labyrinth/server/handler/{RenderHandler.java => RenderV1Handler.java} (90%) create mode 100644 src/main/java/ch/fritteli/labyrinth/server/handler/RenderV2Handler.java diff --git a/pom.xml b/pom.xml index e924ec8..2ba1c8a 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ 17 24.0.1 5.9.2 - 0.0.7 + 0.0.8 1.4.6 1.18.26 2.0.7 diff --git a/src/main/java/ch/fritteli/labyrinth/server/LabyrinthServer.java b/src/main/java/ch/fritteli/labyrinth/server/LabyrinthServer.java index 16e0aae..4c7da3e 100644 --- a/src/main/java/ch/fritteli/labyrinth/server/LabyrinthServer.java +++ b/src/main/java/ch/fritteli/labyrinth/server/LabyrinthServer.java @@ -1,7 +1,8 @@ package ch.fritteli.labyrinth.server; import ch.fritteli.labyrinth.server.handler.CreateHandler; -import ch.fritteli.labyrinth.server.handler.RenderHandler; +import ch.fritteli.labyrinth.server.handler.RenderV1Handler; +import ch.fritteli.labyrinth.server.handler.RenderV2Handler; import io.undertow.Undertow; import io.undertow.server.RoutingHandler; import io.vavr.control.Try; @@ -21,7 +22,8 @@ public class LabyrinthServer { log.info("Starting Server at http://{}:{}/", hostAddress, port); final RoutingHandler routingHandler = new RoutingHandler() .get(CreateHandler.PATH_TEMPLATE, new CreateHandler()) - .post(RenderHandler.PATH_TEMPLATE, new RenderHandler()); + .post(RenderV1Handler.PATH_TEMPLATE, new RenderV1Handler()) + .post(RenderV2Handler.PATH_TEMPLATE, new RenderV2Handler()); this.undertow = Undertow.builder() .addHttpListener(port, hostAddress) diff --git a/src/main/java/ch/fritteli/labyrinth/server/OutputType.java b/src/main/java/ch/fritteli/labyrinth/server/OutputType.java index 77fd88b..767d4da 100644 --- a/src/main/java/ch/fritteli/labyrinth/server/OutputType.java +++ b/src/main/java/ch/fritteli/labyrinth/server/OutputType.java @@ -5,7 +5,8 @@ import ch.fritteli.labyrinth.generator.renderer.html.HTMLRenderer; import ch.fritteli.labyrinth.generator.renderer.json.JsonRenderer; import ch.fritteli.labyrinth.generator.renderer.pdf.PDFRenderer; import ch.fritteli.labyrinth.generator.renderer.text.TextRenderer; -import ch.fritteli.labyrinth.generator.serialization.SerializerDeserializer; +import ch.fritteli.labyrinth.generator.serialization.v1.SerializerDeserializerV1; +import ch.fritteli.labyrinth.generator.serialization.v2.SerializerDeserializerV2; import io.vavr.collection.List; import io.vavr.collection.Stream; import io.vavr.control.Option; @@ -55,10 +56,16 @@ public enum OutputType { "pdffile"), BINARY("application/octet-stream", "laby", - SerializerDeserializer::serialize, + SerializerDeserializerV1::serialize, true, "b", - "binary"); + "binary"), + BINARY_V2("application/octet-stream", + "lab2", + SerializerDeserializerV2::serialize, + true, + "v", + "binaryv2"); @Getter @NonNull private final String contentType; diff --git a/src/main/java/ch/fritteli/labyrinth/server/handler/ParametersToLabyrinthExtractor.java b/src/main/java/ch/fritteli/labyrinth/server/handler/ParametersToLabyrinthExtractor.java index a1cf854..1000677 100644 --- a/src/main/java/ch/fritteli/labyrinth/server/handler/ParametersToLabyrinthExtractor.java +++ b/src/main/java/ch/fritteli/labyrinth/server/handler/ParametersToLabyrinthExtractor.java @@ -1,6 +1,8 @@ package ch.fritteli.labyrinth.server.handler; +import ch.fritteli.labyrinth.generator.Generator; import ch.fritteli.labyrinth.generator.model.Labyrinth; +import ch.fritteli.labyrinth.generator.model.Position; import ch.fritteli.labyrinth.server.OutputType; import io.vavr.Tuple; import io.vavr.Tuple2; @@ -25,6 +27,8 @@ class ParametersToLabyrinthExtractor { final Option width = getParameterValue(RequestParameter.WIDTH); final Option height = getParameterValue(RequestParameter.HEIGHT); final Option id = getParameterValue(RequestParameter.ID); + final Option start = getParameterValue(RequestParameter.START); + final Option end = getParameterValue(RequestParameter.END); if (output.isEmpty()) { return Try.failure(new IllegalArgumentException("Path parameter %s is required and must be one of: %s".formatted( @@ -45,7 +49,16 @@ class ParametersToLabyrinthExtractor { ))); } - return Try.of(() -> Tuple.of(output.get(), new Labyrinth(width.get(), height.get(), id.getOrElse(() -> new Random().nextLong())))); + return Try.of(() -> { + final Labyrinth labyrinth; + if (start.isDefined() && end.isDefined()) { + labyrinth = new Labyrinth(width.get(), height.get(), id.getOrElse(() -> new Random().nextLong()), start.get(), end.get()); + } else { + labyrinth = new Labyrinth(width.get(), height.get(), id.getOrElse(() -> new Random().nextLong())); + } + new Generator(labyrinth).run(); + return Tuple.of(output.get(), labyrinth); + }); } @NonNull diff --git a/src/main/java/ch/fritteli/labyrinth/server/handler/RenderHandler.java b/src/main/java/ch/fritteli/labyrinth/server/handler/RenderV1Handler.java similarity index 90% rename from src/main/java/ch/fritteli/labyrinth/server/handler/RenderHandler.java rename to src/main/java/ch/fritteli/labyrinth/server/handler/RenderV1Handler.java index 52a61d5..a02810b 100644 --- a/src/main/java/ch/fritteli/labyrinth/server/handler/RenderHandler.java +++ b/src/main/java/ch/fritteli/labyrinth/server/handler/RenderV1Handler.java @@ -1,7 +1,7 @@ package ch.fritteli.labyrinth.server.handler; import ch.fritteli.labyrinth.generator.model.Labyrinth; -import ch.fritteli.labyrinth.generator.serialization.SerializerDeserializer; +import ch.fritteli.labyrinth.generator.serialization.v1.SerializerDeserializerV1; import ch.fritteli.labyrinth.server.OutputType; import io.undertow.server.HttpServerExchange; import io.undertow.util.HeaderValues; @@ -12,9 +12,9 @@ import lombok.NonNull; import lombok.extern.slf4j.Slf4j; @Slf4j -public class RenderHandler extends AbstractHttpHandler { +public class RenderV1Handler extends AbstractHttpHandler { - public static final String PATH_TEMPLATE = "/render/{output}"; + public static final String PATH_TEMPLATE = "/render/v1/{output}"; @Override public void handle(final HttpServerExchange exchange) { @@ -28,7 +28,7 @@ public class RenderHandler extends AbstractHttpHandler { final OutputType output = this.getOutputType(httpServerExchange); final byte[] render; try { - final Labyrinth labyrinth = SerializerDeserializer.deserialize(bytes); + final Labyrinth labyrinth = SerializerDeserializerV1.deserialize(bytes); render = output.render(labyrinth); } catch (final Exception e) { log.error("Error rendering binary labyrinth data", e); diff --git a/src/main/java/ch/fritteli/labyrinth/server/handler/RenderV2Handler.java b/src/main/java/ch/fritteli/labyrinth/server/handler/RenderV2Handler.java new file mode 100644 index 0000000..80d0549 --- /dev/null +++ b/src/main/java/ch/fritteli/labyrinth/server/handler/RenderV2Handler.java @@ -0,0 +1,60 @@ +package ch.fritteli.labyrinth.server.handler; + +import ch.fritteli.labyrinth.generator.model.Labyrinth; +import ch.fritteli.labyrinth.generator.serialization.v2.SerializerDeserializerV2; +import ch.fritteli.labyrinth.server.OutputType; +import io.undertow.server.HttpServerExchange; +import io.undertow.util.HeaderValues; +import io.undertow.util.Headers; +import io.undertow.util.StatusCodes; +import java.nio.ByteBuffer; +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class RenderV2Handler extends AbstractHttpHandler { + + public static final String PATH_TEMPLATE = "/render/v2/{output}"; + + @Override + public void handle(final HttpServerExchange exchange) { + log.debug("Handling render request"); + + if (exchange.isInIoThread()) { + exchange.dispatch(this); + return; + } + exchange.getRequestReceiver().receiveFullBytes((httpServerExchange, bytes) -> { + final OutputType output = this.getOutputType(httpServerExchange); + final byte[] render; + try { + final Labyrinth labyrinth = SerializerDeserializerV2.deserialize(bytes); + render = output.render(labyrinth); + } catch (final Exception e) { + log.error("Error rendering binary labyrinth data", e); + httpServerExchange.setStatusCode(StatusCodes.INTERNAL_SERVER_ERROR) + .getResponseSender() + .send("Error rendering labyrinth: %s".formatted(e.getMessage())); + return; + } + httpServerExchange + .setStatusCode(StatusCodes.OK) + .getResponseHeaders() + .put(Headers.CONTENT_TYPE, output.getContentType()); + httpServerExchange.getResponseSender() + .send(ByteBuffer.wrap(render)); + }); + } + + @NonNull + private OutputType getOutputType(@NonNull final HttpServerExchange httpServerExchange) { + return RequestParameter.OUTPUT.getParameterValue(httpServerExchange.getQueryParameters()) + .getOrElse(() -> { + final HeaderValues accept = httpServerExchange.getRequestHeaders().get(Headers.ACCEPT); + if (accept.contains(OutputType.HTML.getContentType())) { + return OutputType.HTML; + } + return OutputType.TEXT_PLAIN; + }); + } +} diff --git a/src/main/java/ch/fritteli/labyrinth/server/handler/RequestParameter.java b/src/main/java/ch/fritteli/labyrinth/server/handler/RequestParameter.java index 314ee22..dc1c59b 100644 --- a/src/main/java/ch/fritteli/labyrinth/server/handler/RequestParameter.java +++ b/src/main/java/ch/fritteli/labyrinth/server/handler/RequestParameter.java @@ -1,5 +1,6 @@ package ch.fritteli.labyrinth.server.handler; +import ch.fritteli.labyrinth.generator.model.Position; import ch.fritteli.labyrinth.server.OutputType; import io.vavr.Tuple2; import io.vavr.collection.HashMap; @@ -26,7 +27,23 @@ enum RequestParameter { .toOption() .onEmpty(() -> log.debug("Unparseable value for parameter 'id': '{}'", p)), "i", "id"), OUTPUT(p -> OutputType.ofString(p) - .onEmpty(() -> log.debug("Unparseable value for parameter 'output': '{}'", p)), "o", "output"); + .onEmpty(() -> log.debug("Unparseable value for parameter 'output': '{}'", p)), "o", "output"), + START(p -> Try.of(() -> { + final String[] parts = p.split(","); + final int x = Integer.parseInt(parts[0]); + final int y = Integer.parseInt(parts[1]); + return new Position(x, y); + }) + .toOption() + .onEmpty(() -> log.debug("Unparseable value for parameter 'start': '{}'", p)), "s", "start"), + END(p -> Try.of(() -> { + final String[] parts = p.split(","); + final int x = Integer.parseInt(parts[0]); + final int y = Integer.parseInt(parts[1]); + return new Position(x, y); + }) + .toOption() + .onEmpty(() -> log.debug("Unparseable value for parameter 'end': '{}'", p)), "e", "end"); @NonNull private final Function> extractor; @Getter