From dbdfb0c8a46e4f3f184949b235fe8c6f4c1ae47c Mon Sep 17 00:00:00 2001 From: Manuel Friedli Date: Sat, 18 May 2024 02:20:31 +0200 Subject: [PATCH] Java21 and some refactoring. --- .drone.yml | 6 +-- docker/Dockerfile | 2 +- maven-settings.xml | 2 +- pom.xml | 6 +-- .../InvalidRequestParameterException.java | 9 +++++ .../ch/fritteli/maze/server/MazeServer.java | 15 ++++---- .../ch/fritteli/maze/server/OutputType.java | 28 +++++++------- .../ch/fritteli/maze/server/ServerConfig.java | 8 ++-- .../server/handler/AbstractHttpHandler.java | 8 ++-- .../maze/server/handler/CreateHandler.java | 38 ++++++++++--------- .../handler/ParametersToMazeExtractor.java | 28 +++++++------- .../maze/server/handler/RenderV1Handler.java | 10 ++--- .../maze/server/handler/RenderV2Handler.java | 10 ++--- .../maze/server/handler/RequestParameter.java | 18 ++++----- 14 files changed, 101 insertions(+), 87 deletions(-) create mode 100644 src/main/java/ch/fritteli/maze/server/InvalidRequestParameterException.java diff --git a/.drone.yml b/.drone.yml index 934778b..a282745 100644 --- a/.drone.yml +++ b/.drone.yml @@ -3,7 +3,7 @@ type: docker name: default steps: - name: build - image: maven:3.8-openjdk-18-slim + image: maven:3.9-eclipse-temurin-21 commands: - mvn clean install -DskipTests=true -Dmaven.javadoc.skip=true -B -V when: @@ -13,7 +13,7 @@ steps: - refs/head/feature/** - refs/tags/** - name: test - image: maven:3.8-openjdk-18-slim + image: maven:3.9-eclipse-temurin-21 commands: - mvn test -B when: @@ -22,7 +22,7 @@ steps: - master - feature/* - name: deploy - image: maven:3.8-openjdk-18-slim + image: maven:3.9-eclipse-temurin-21 environment: REPO_TOKEN: from_secret: repo-token diff --git a/docker/Dockerfile b/docker/Dockerfile index 8ba6147..9680592 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM openjdk:17-slim +FROM eclipse-temurin:21-jre COPY target/maze-server-*.jar /app/ RUN rm /app/*-sources.jar diff --git a/maven-settings.xml b/maven-settings.xml index 9e840fd..b98bb47 100644 --- a/maven-settings.xml +++ b/maven-settings.xml @@ -10,7 +10,7 @@ ossrh - fritteli + 4etdRvZF ${env.REPO_TOKEN_OSSRH} diff --git a/pom.xml b/pom.xml index a2181cc..3ec91a4 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ ch.fritteli fritteli-build-parent - 5.0.0 + 5.1.0 ch.fritteli.a-maze-r @@ -55,9 +55,9 @@ - 0.1.0 + 0.2.1 4.0.0-M8 - 2.3.5.Final + 2.3.13.Final diff --git a/src/main/java/ch/fritteli/maze/server/InvalidRequestParameterException.java b/src/main/java/ch/fritteli/maze/server/InvalidRequestParameterException.java new file mode 100644 index 0000000..a103796 --- /dev/null +++ b/src/main/java/ch/fritteli/maze/server/InvalidRequestParameterException.java @@ -0,0 +1,9 @@ +package ch.fritteli.maze.server; + +import org.wildfly.common.annotation.NotNull; + +public class InvalidRequestParameterException extends RuntimeException { + public InvalidRequestParameterException(@NotNull final String s) { + super(s); + } +} diff --git a/src/main/java/ch/fritteli/maze/server/MazeServer.java b/src/main/java/ch/fritteli/maze/server/MazeServer.java index 183db6d..b41da69 100644 --- a/src/main/java/ch/fritteli/maze/server/MazeServer.java +++ b/src/main/java/ch/fritteli/maze/server/MazeServer.java @@ -6,17 +6,18 @@ import ch.fritteli.maze.server.handler.RenderV2Handler; import io.undertow.Undertow; import io.undertow.server.RoutingHandler; import io.vavr.control.Try; -import java.net.InetSocketAddress; -import lombok.NonNull; import lombok.extern.slf4j.Slf4j; +import org.wildfly.common.annotation.NotNull; + +import java.net.InetSocketAddress; @Slf4j public class MazeServer { - @NonNull + @NotNull private final Undertow undertow; - private MazeServer(@NonNull final ServerConfig config) { + private MazeServer(@NotNull final ServerConfig config) { final String hostAddress = config.getAddress().getHostAddress(); final int port = config.getPort(); log.info("Starting Server at http://{}:{}/", hostAddress, port); @@ -31,14 +32,14 @@ public class MazeServer { .build(); } - @NonNull + @NotNull public static Try createAndStartServer() { return Try.of(ServerConfig::init) .flatMapTry(MazeServer::createAndStartServer); } - @NonNull - public static Try createAndStartServer(@NonNull final ServerConfig config) { + @NotNull + public static Try createAndStartServer(@NotNull final ServerConfig config) { return Try.of(() -> new MazeServer(config)) .peek(MazeServer::start); } diff --git a/src/main/java/ch/fritteli/maze/server/OutputType.java b/src/main/java/ch/fritteli/maze/server/OutputType.java index 75c292f..cbc23f1 100644 --- a/src/main/java/ch/fritteli/maze/server/OutputType.java +++ b/src/main/java/ch/fritteli/maze/server/OutputType.java @@ -1,17 +1,17 @@ package ch.fritteli.maze.server; +import ch.fritteli.maze.generator.model.Maze; import ch.fritteli.maze.generator.renderer.html.HTMLRenderer; import ch.fritteli.maze.generator.renderer.json.JsonRenderer; import ch.fritteli.maze.generator.renderer.pdf.PDFRenderer; import ch.fritteli.maze.generator.renderer.text.TextRenderer; import ch.fritteli.maze.generator.serialization.v1.SerializerDeserializerV1; import ch.fritteli.maze.generator.serialization.v2.SerializerDeserializerV2; -import ch.fritteli.maze.generator.model.Maze; import io.vavr.collection.List; import io.vavr.collection.Stream; import io.vavr.control.Option; import lombok.Getter; -import lombok.NonNull; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.nio.charset.StandardCharsets; @@ -67,24 +67,24 @@ public enum OutputType { "v", "binaryv2"); @Getter - @NonNull + @NotNull private final String contentType; @Getter - @NonNull + @NotNull private final String fileExtension; - @NonNull + @NotNull private final Function render; @Getter private final boolean attachment; @Getter - @NonNull + @NotNull private final List names; - OutputType(@NonNull final String contentType, - @NonNull final String fileExtension, - @NonNull final Function render, + OutputType(@NotNull final String contentType, + @NotNull final String fileExtension, + @NotNull final Function render, final boolean attachment, - @NonNull final String... names) { + @NotNull final String... names) { this.contentType = contentType; this.render = render; this.fileExtension = fileExtension; @@ -92,7 +92,7 @@ public enum OutputType { this.names = List.of(names); } - @NonNull + @NotNull public static Option ofString(@Nullable final String name) { return Option.of(name) .map(String::toLowerCase) @@ -101,14 +101,14 @@ public enum OutputType { } - @NonNull + @NotNull @Override public String toString() { return this.names.last(); } - @NonNull - public byte[] render(@NonNull final Maze maze) { + @NotNull + public byte[] render(@NotNull final Maze maze) { return this.render.apply(maze); } } diff --git a/src/main/java/ch/fritteli/maze/server/ServerConfig.java b/src/main/java/ch/fritteli/maze/server/ServerConfig.java index 0811126..ce25a6b 100644 --- a/src/main/java/ch/fritteli/maze/server/ServerConfig.java +++ b/src/main/java/ch/fritteli/maze/server/ServerConfig.java @@ -3,10 +3,10 @@ package ch.fritteli.maze.server; import io.vavr.control.Try; import lombok.AccessLevel; import lombok.AllArgsConstructor; -import lombok.NonNull; import lombok.Value; import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.Nullable; +import org.wildfly.common.annotation.NotNull; import java.net.InetAddress; @@ -17,7 +17,7 @@ public class ServerConfig { public static final String SYSPROP_HOST = "fritteli.maze.server.host"; public static final String SYSPROP_PORT = "fritteli.maze.server.port"; - @NonNull + @NotNull InetAddress address; int port; @@ -27,7 +27,7 @@ public class ServerConfig { log.debug("host={}, port={}", this.address, this.port); } - @NonNull + @NotNull public static ServerConfig init() throws ConfigurationException { final String host = System.getProperty(SYSPROP_HOST); final String portString = System.getProperty(SYSPROP_PORT); @@ -35,7 +35,7 @@ public class ServerConfig { return new ServerConfig(host, port); } - @NonNull + @NotNull private static InetAddress validateAddress(@Nullable final String address) { return Try.of(() -> InetAddress.getByName(address)) .getOrElseThrow(cause -> new ConfigurationException( diff --git a/src/main/java/ch/fritteli/maze/server/handler/AbstractHttpHandler.java b/src/main/java/ch/fritteli/maze/server/handler/AbstractHttpHandler.java index ac90f39..f129b5f 100644 --- a/src/main/java/ch/fritteli/maze/server/handler/AbstractHttpHandler.java +++ b/src/main/java/ch/fritteli/maze/server/handler/AbstractHttpHandler.java @@ -3,8 +3,8 @@ package ch.fritteli.maze.server.handler; import io.undertow.server.HttpHandler; import io.undertow.server.HttpServerExchange; import io.undertow.util.StatusCodes; -import lombok.NonNull; import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; import org.slf4j.MDC; import java.time.Instant; @@ -14,7 +14,7 @@ import java.util.UUID; @Slf4j public abstract class AbstractHttpHandler implements HttpHandler { @Override - public final void handleRequest(@NonNull final HttpServerExchange exchange) { + public final void handleRequest(@NotNull final HttpServerExchange exchange) { final Instant start = Instant.now(); try (final MDC.MDCCloseable closeable = MDC.putCloseable("correlationId", UUID.randomUUID().toString())) { @@ -25,7 +25,7 @@ public abstract class AbstractHttpHandler implements HttpHandler { } try { this.handle(exchange); - } catch (@NonNull final Exception e) { + } catch (final Exception e) { log.error("Error handling request", e); exchange.setStatusCode(StatusCodes.INTERNAL_SERVER_ERROR) .getResponseSender() @@ -35,5 +35,5 @@ public abstract class AbstractHttpHandler implements HttpHandler { } } - protected abstract void handle(@NonNull final HttpServerExchange exchange) throws Exception; + protected abstract void handle(@NotNull final HttpServerExchange exchange) throws Exception; } diff --git a/src/main/java/ch/fritteli/maze/server/handler/CreateHandler.java b/src/main/java/ch/fritteli/maze/server/handler/CreateHandler.java index a5a43a0..a14954b 100644 --- a/src/main/java/ch/fritteli/maze/server/handler/CreateHandler.java +++ b/src/main/java/ch/fritteli/maze/server/handler/CreateHandler.java @@ -1,21 +1,22 @@ package ch.fritteli.maze.server.handler; -import ch.fritteli.maze.server.OutputType; import ch.fritteli.maze.generator.model.Maze; +import ch.fritteli.maze.server.InvalidRequestParameterException; +import ch.fritteli.maze.server.OutputType; import io.undertow.server.HttpServerExchange; import io.undertow.util.Headers; import io.undertow.util.HttpString; import io.undertow.util.StatusCodes; -import io.vavr.Tuple2; import io.vavr.control.Try; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; +import org.slf4j.MDC; + import java.nio.ByteBuffer; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Deque; import java.util.Map; -import lombok.NonNull; -import lombok.extern.slf4j.Slf4j; -import org.slf4j.MDC; @Slf4j public class CreateHandler extends AbstractHttpHandler { @@ -23,27 +24,29 @@ public class CreateHandler extends AbstractHttpHandler { public static final String PATH_TEMPLATE = "/create/{output}"; @Override - protected void handle(@NonNull final HttpServerExchange exchange) { + protected void handle(@NotNull final HttpServerExchange exchange) { final Instant start = Instant.now(); log.debug("Handling create request"); this.createMazeFromRequestParameters(exchange.getQueryParameters()) .onFailure(e -> { log.error("Error creating maze from request", e); - exchange.setStatusCode(StatusCodes.INTERNAL_SERVER_ERROR) - .setReasonPhrase(StatusCodes.INTERNAL_SERVER_ERROR_STRING) - .getResponseSender() + if (e instanceof InvalidRequestParameterException) { + exchange.setStatusCode(StatusCodes.BAD_REQUEST); + } else { + exchange.setStatusCode(StatusCodes.INTERNAL_SERVER_ERROR); + } + exchange.getResponseSender() .send(e.getMessage()); }) - .forEach(tuple -> { - final OutputType outputType = tuple._1(); - final Maze maze = tuple._2(); + .forEach(generatedMaze -> { + final OutputType outputType = generatedMaze.outputType(); + final Maze maze = generatedMaze.maze(); final byte[] bytes; try { bytes = outputType.render(maze); - } catch (@NonNull final Exception e) { + } catch (final Exception e) { log.error("Error rendering Maze", e); exchange.setStatusCode(StatusCodes.INTERNAL_SERVER_ERROR) - .setReasonPhrase(StatusCodes.INTERNAL_SERVER_ERROR_STRING) .getResponseSender() .send("Error creating the maze. Please contact the administrator. Request id=%s".formatted(MDC.get("correlationId"))); return; @@ -54,8 +57,7 @@ public class CreateHandler extends AbstractHttpHandler { .put(HttpString.tryFromString("X-Maze-ID"), String.valueOf(maze.getRandomSeed())) .put(HttpString.tryFromString("X-Maze-Width"), String.valueOf(maze.getWidth())) .put(HttpString.tryFromString("X-Maze-Height"), String.valueOf(maze.getHeight())) - // TODO read the algorithm from the maze once this is supported. - .put(HttpString.tryFromString("X-Maze-Algorithm"), "RandomDepthFirst") + .put(HttpString.tryFromString("X-Maze-Algorithm"), generatedMaze.generatorName()) .put(HttpString.tryFromString("X-Maze-Generation-Duration-millis"), String.valueOf(durationMillis)); if (outputType.isAttachment()) { exchange.getResponseHeaders() @@ -71,8 +73,8 @@ public class CreateHandler extends AbstractHttpHandler { }); } - @NonNull - private Try> createMazeFromRequestParameters(final Map> queryParameters) { + @NotNull + private Try createMazeFromRequestParameters(final Map> queryParameters) { return new ParametersToMazeExtractor(queryParameters).createMaze(); } } diff --git a/src/main/java/ch/fritteli/maze/server/handler/ParametersToMazeExtractor.java b/src/main/java/ch/fritteli/maze/server/handler/ParametersToMazeExtractor.java index f873490..bca9efa 100644 --- a/src/main/java/ch/fritteli/maze/server/handler/ParametersToMazeExtractor.java +++ b/src/main/java/ch/fritteli/maze/server/handler/ParametersToMazeExtractor.java @@ -1,16 +1,15 @@ package ch.fritteli.maze.server.handler; -import ch.fritteli.maze.server.OutputType; import ch.fritteli.maze.generator.algorithm.RandomDepthFirst; import ch.fritteli.maze.generator.model.Maze; import ch.fritteli.maze.generator.model.Position; -import io.vavr.Tuple; -import io.vavr.Tuple2; +import ch.fritteli.maze.server.InvalidRequestParameterException; +import ch.fritteli.maze.server.OutputType; import io.vavr.collection.Stream; import io.vavr.control.Option; import io.vavr.control.Try; -import lombok.NonNull; import lombok.RequiredArgsConstructor; +import org.jetbrains.annotations.NotNull; import java.util.Deque; import java.util.Map; @@ -19,11 +18,11 @@ import java.util.Random; @RequiredArgsConstructor class ParametersToMazeExtractor { - @NonNull + @NotNull private final Map> queryParameters; - @NonNull - Try> createMaze() { + @NotNull + Try createMaze() { final Option output = getParameterValue(RequestParameter.OUTPUT); final Option width = getParameterValue(RequestParameter.WIDTH); final Option height = getParameterValue(RequestParameter.HEIGHT); @@ -32,7 +31,7 @@ class ParametersToMazeExtractor { 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( + return Try.failure(new InvalidRequestParameterException("Path parameter %s is required and must be one of: %s".formatted( RequestParameter.OUTPUT.getNames().mkString("'", " / ", "'"), Stream.of(OutputType.values()) .flatMap(OutputType::getNames) @@ -40,12 +39,12 @@ class ParametersToMazeExtractor { ))); } if (width.isEmpty()) { - return Try.failure(new IllegalArgumentException("Query parameter %s is required and must be a positive integer value".formatted( + return Try.failure(new InvalidRequestParameterException("Query parameter %s is required and must be a positive integer value".formatted( RequestParameter.WIDTH.getNames().mkString("'", " / ", "'") ))); } if (height.isEmpty()) { - return Try.failure(new IllegalArgumentException("Query parameter %s is required and must be a positive integer value".formatted( + return Try.failure(new InvalidRequestParameterException("Query parameter %s is required and must be a positive integer value".formatted( RequestParameter.HEIGHT.getNames().mkString("'", " / ", "'") ))); } @@ -58,12 +57,15 @@ class ParametersToMazeExtractor { maze = new Maze(width.get(), height.get(), id.getOrElse(() -> new Random().nextLong())); } new RandomDepthFirst(maze).run(); - return Tuple.of(output.get(), maze); + return new GeneratedMaze(maze, output.get(), RandomDepthFirst.class.getSimpleName()); }); } - @NonNull - private Option getParameterValue(@NonNull final RequestParameter parameter) { + @NotNull + private Option getParameterValue(@NotNull final RequestParameter parameter) { return parameter.getParameterValue(this.queryParameters); } + + public record GeneratedMaze(@NotNull Maze maze, @NotNull OutputType outputType, @NotNull String generatorName) { + } } diff --git a/src/main/java/ch/fritteli/maze/server/handler/RenderV1Handler.java b/src/main/java/ch/fritteli/maze/server/handler/RenderV1Handler.java index 5b67423..82165df 100644 --- a/src/main/java/ch/fritteli/maze/server/handler/RenderV1Handler.java +++ b/src/main/java/ch/fritteli/maze/server/handler/RenderV1Handler.java @@ -1,14 +1,14 @@ package ch.fritteli.maze.server.handler; -import ch.fritteli.maze.server.OutputType; import ch.fritteli.maze.generator.model.Maze; import ch.fritteli.maze.generator.serialization.v1.SerializerDeserializerV1; +import ch.fritteli.maze.server.OutputType; import io.undertow.server.HttpServerExchange; import io.undertow.util.HeaderValues; import io.undertow.util.Headers; import io.undertow.util.StatusCodes; -import lombok.NonNull; import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; import java.nio.ByteBuffer; @@ -18,7 +18,7 @@ public class RenderV1Handler extends AbstractHttpHandler { public static final String PATH_TEMPLATE = "/render/v1/{output}"; @Override - public void handle(final HttpServerExchange exchange) { + public void handle(@NotNull final HttpServerExchange exchange) { log.debug("Handling render request"); if (exchange.isInIoThread()) { @@ -47,8 +47,8 @@ public class RenderV1Handler extends AbstractHttpHandler { }); } - @NonNull - private OutputType getOutputType(@NonNull final HttpServerExchange httpServerExchange) { + @NotNull + private OutputType getOutputType(@NotNull final HttpServerExchange httpServerExchange) { return RequestParameter.OUTPUT.getParameterValue(httpServerExchange.getQueryParameters()) .getOrElse(() -> { final HeaderValues accept = httpServerExchange.getRequestHeaders().get(Headers.ACCEPT); diff --git a/src/main/java/ch/fritteli/maze/server/handler/RenderV2Handler.java b/src/main/java/ch/fritteli/maze/server/handler/RenderV2Handler.java index 0ac2721..bd4a856 100644 --- a/src/main/java/ch/fritteli/maze/server/handler/RenderV2Handler.java +++ b/src/main/java/ch/fritteli/maze/server/handler/RenderV2Handler.java @@ -1,14 +1,14 @@ package ch.fritteli.maze.server.handler; -import ch.fritteli.maze.server.OutputType; import ch.fritteli.maze.generator.model.Maze; import ch.fritteli.maze.generator.serialization.v2.SerializerDeserializerV2; +import ch.fritteli.maze.server.OutputType; import io.undertow.server.HttpServerExchange; import io.undertow.util.HeaderValues; import io.undertow.util.Headers; import io.undertow.util.StatusCodes; -import lombok.NonNull; import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; import java.nio.ByteBuffer; @@ -18,7 +18,7 @@ public class RenderV2Handler extends AbstractHttpHandler { public static final String PATH_TEMPLATE = "/render/v2/{output}"; @Override - public void handle(final HttpServerExchange exchange) { + public void handle(@NotNull final HttpServerExchange exchange) { log.debug("Handling render request"); if (exchange.isInIoThread()) { @@ -47,8 +47,8 @@ public class RenderV2Handler extends AbstractHttpHandler { }); } - @NonNull - private OutputType getOutputType(@NonNull final HttpServerExchange httpServerExchange) { + @NotNull + private OutputType getOutputType(@NotNull final HttpServerExchange httpServerExchange) { return RequestParameter.OUTPUT.getParameterValue(httpServerExchange.getQueryParameters()) .getOrElse(() -> { final HeaderValues accept = httpServerExchange.getRequestHeaders().get(Headers.ACCEPT); diff --git a/src/main/java/ch/fritteli/maze/server/handler/RequestParameter.java b/src/main/java/ch/fritteli/maze/server/handler/RequestParameter.java index 1051182..3808313 100644 --- a/src/main/java/ch/fritteli/maze/server/handler/RequestParameter.java +++ b/src/main/java/ch/fritteli/maze/server/handler/RequestParameter.java @@ -1,7 +1,7 @@ package ch.fritteli.maze.server.handler; -import ch.fritteli.maze.server.OutputType; import ch.fritteli.maze.generator.model.Position; +import ch.fritteli.maze.server.OutputType; import io.vavr.Tuple2; import io.vavr.collection.HashMap; import io.vavr.collection.HashSet; @@ -9,8 +9,8 @@ import io.vavr.collection.Set; import io.vavr.control.Option; import io.vavr.control.Try; import lombok.Getter; -import lombok.NonNull; import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; import java.util.Deque; import java.util.Map; @@ -45,24 +45,24 @@ enum RequestParameter { }) .toOption() .onEmpty(() -> log.debug("Unparseable value for parameter 'end': '{}'", p)), "e", "end"); - @NonNull + @NotNull private final Function> extractor; @Getter - @NonNull + @NotNull private final Set names; - RequestParameter(@NonNull final Function> extractor, @NonNull final String... names) { + RequestParameter(@NotNull final Function> extractor, @NotNull final String... names) { this.extractor = extractor; this.names = HashSet.of(names); } - @NonNull - Option extractParameterValue(@NonNull final String parameter) { + @NotNull + Option extractParameterValue(@NotNull final String parameter) { return this.extractor.apply(parameter); } - @NonNull - public Option getParameterValue(@NonNull final Map> queryParameters) { + @NotNull + public Option getParameterValue(@NotNull final Map> queryParameters) { return (Option) HashMap.ofAll(queryParameters) .filterKeys(this.names::contains) .flatMap(Tuple2::_2)