Java21 and some refactoring.
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
ba85f54b16
commit
dbdfb0c8a4
14 changed files with 101 additions and 87 deletions
|
@ -3,7 +3,7 @@ type: docker
|
||||||
name: default
|
name: default
|
||||||
steps:
|
steps:
|
||||||
- name: build
|
- name: build
|
||||||
image: maven:3.8-openjdk-18-slim
|
image: maven:3.9-eclipse-temurin-21
|
||||||
commands:
|
commands:
|
||||||
- mvn clean install -DskipTests=true -Dmaven.javadoc.skip=true -B -V
|
- mvn clean install -DskipTests=true -Dmaven.javadoc.skip=true -B -V
|
||||||
when:
|
when:
|
||||||
|
@ -13,7 +13,7 @@ steps:
|
||||||
- refs/head/feature/**
|
- refs/head/feature/**
|
||||||
- refs/tags/**
|
- refs/tags/**
|
||||||
- name: test
|
- name: test
|
||||||
image: maven:3.8-openjdk-18-slim
|
image: maven:3.9-eclipse-temurin-21
|
||||||
commands:
|
commands:
|
||||||
- mvn test -B
|
- mvn test -B
|
||||||
when:
|
when:
|
||||||
|
@ -22,7 +22,7 @@ steps:
|
||||||
- master
|
- master
|
||||||
- feature/*
|
- feature/*
|
||||||
- name: deploy
|
- name: deploy
|
||||||
image: maven:3.8-openjdk-18-slim
|
image: maven:3.9-eclipse-temurin-21
|
||||||
environment:
|
environment:
|
||||||
REPO_TOKEN:
|
REPO_TOKEN:
|
||||||
from_secret: repo-token
|
from_secret: repo-token
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM openjdk:17-slim
|
FROM eclipse-temurin:21-jre
|
||||||
|
|
||||||
COPY target/maze-server-*.jar /app/
|
COPY target/maze-server-*.jar /app/
|
||||||
RUN rm /app/*-sources.jar
|
RUN rm /app/*-sources.jar
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
</server>
|
</server>
|
||||||
<server>
|
<server>
|
||||||
<id>ossrh</id>
|
<id>ossrh</id>
|
||||||
<username>fritteli</username>
|
<username>4etdRvZF</username>
|
||||||
<password>${env.REPO_TOKEN_OSSRH}</password>
|
<password>${env.REPO_TOKEN_OSSRH}</password>
|
||||||
</server>
|
</server>
|
||||||
</servers>
|
</servers>
|
||||||
|
|
6
pom.xml
6
pom.xml
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ch.fritteli</groupId>
|
<groupId>ch.fritteli</groupId>
|
||||||
<artifactId>fritteli-build-parent</artifactId>
|
<artifactId>fritteli-build-parent</artifactId>
|
||||||
<version>5.0.0</version>
|
<version>5.1.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<groupId>ch.fritteli.a-maze-r</groupId>
|
<groupId>ch.fritteli.a-maze-r</groupId>
|
||||||
|
@ -55,9 +55,9 @@
|
||||||
</distributionManagement>
|
</distributionManagement>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<maze-generator.version>0.1.0</maze-generator.version>
|
<maze-generator.version>0.2.1</maze-generator.version>
|
||||||
<maven-site-plugin.version>4.0.0-M8</maven-site-plugin.version>
|
<maven-site-plugin.version>4.0.0-M8</maven-site-plugin.version>
|
||||||
<undertow.version>2.3.5.Final</undertow.version>
|
<undertow.version>2.3.13.Final</undertow.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,17 +6,18 @@ import ch.fritteli.maze.server.handler.RenderV2Handler;
|
||||||
import io.undertow.Undertow;
|
import io.undertow.Undertow;
|
||||||
import io.undertow.server.RoutingHandler;
|
import io.undertow.server.RoutingHandler;
|
||||||
import io.vavr.control.Try;
|
import io.vavr.control.Try;
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import lombok.NonNull;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.wildfly.common.annotation.NotNull;
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class MazeServer {
|
public class MazeServer {
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
private final Undertow undertow;
|
private final Undertow undertow;
|
||||||
|
|
||||||
private MazeServer(@NonNull final ServerConfig config) {
|
private MazeServer(@NotNull final ServerConfig config) {
|
||||||
final String hostAddress = config.getAddress().getHostAddress();
|
final String hostAddress = config.getAddress().getHostAddress();
|
||||||
final int port = config.getPort();
|
final int port = config.getPort();
|
||||||
log.info("Starting Server at http://{}:{}/", hostAddress, port);
|
log.info("Starting Server at http://{}:{}/", hostAddress, port);
|
||||||
|
@ -31,14 +32,14 @@ public class MazeServer {
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
public static Try<MazeServer> createAndStartServer() {
|
public static Try<MazeServer> createAndStartServer() {
|
||||||
return Try.of(ServerConfig::init)
|
return Try.of(ServerConfig::init)
|
||||||
.flatMapTry(MazeServer::createAndStartServer);
|
.flatMapTry(MazeServer::createAndStartServer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
public static Try<MazeServer> createAndStartServer(@NonNull final ServerConfig config) {
|
public static Try<MazeServer> createAndStartServer(@NotNull final ServerConfig config) {
|
||||||
return Try.of(() -> new MazeServer(config))
|
return Try.of(() -> new MazeServer(config))
|
||||||
.peek(MazeServer::start);
|
.peek(MazeServer::start);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
package ch.fritteli.maze.server;
|
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.html.HTMLRenderer;
|
||||||
import ch.fritteli.maze.generator.renderer.json.JsonRenderer;
|
import ch.fritteli.maze.generator.renderer.json.JsonRenderer;
|
||||||
import ch.fritteli.maze.generator.renderer.pdf.PDFRenderer;
|
import ch.fritteli.maze.generator.renderer.pdf.PDFRenderer;
|
||||||
import ch.fritteli.maze.generator.renderer.text.TextRenderer;
|
import ch.fritteli.maze.generator.renderer.text.TextRenderer;
|
||||||
import ch.fritteli.maze.generator.serialization.v1.SerializerDeserializerV1;
|
import ch.fritteli.maze.generator.serialization.v1.SerializerDeserializerV1;
|
||||||
import ch.fritteli.maze.generator.serialization.v2.SerializerDeserializerV2;
|
import ch.fritteli.maze.generator.serialization.v2.SerializerDeserializerV2;
|
||||||
import ch.fritteli.maze.generator.model.Maze;
|
|
||||||
import io.vavr.collection.List;
|
import io.vavr.collection.List;
|
||||||
import io.vavr.collection.Stream;
|
import io.vavr.collection.Stream;
|
||||||
import io.vavr.control.Option;
|
import io.vavr.control.Option;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NonNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
@ -67,24 +67,24 @@ public enum OutputType {
|
||||||
"v",
|
"v",
|
||||||
"binaryv2");
|
"binaryv2");
|
||||||
@Getter
|
@Getter
|
||||||
@NonNull
|
@NotNull
|
||||||
private final String contentType;
|
private final String contentType;
|
||||||
@Getter
|
@Getter
|
||||||
@NonNull
|
@NotNull
|
||||||
private final String fileExtension;
|
private final String fileExtension;
|
||||||
@NonNull
|
@NotNull
|
||||||
private final Function<Maze, byte[]> render;
|
private final Function<Maze, byte[]> render;
|
||||||
@Getter
|
@Getter
|
||||||
private final boolean attachment;
|
private final boolean attachment;
|
||||||
@Getter
|
@Getter
|
||||||
@NonNull
|
@NotNull
|
||||||
private final List<String> names;
|
private final List<String> names;
|
||||||
|
|
||||||
OutputType(@NonNull final String contentType,
|
OutputType(@NotNull final String contentType,
|
||||||
@NonNull final String fileExtension,
|
@NotNull final String fileExtension,
|
||||||
@NonNull final Function<Maze, byte[]> render,
|
@NotNull final Function<Maze, byte[]> render,
|
||||||
final boolean attachment,
|
final boolean attachment,
|
||||||
@NonNull final String... names) {
|
@NotNull final String... names) {
|
||||||
this.contentType = contentType;
|
this.contentType = contentType;
|
||||||
this.render = render;
|
this.render = render;
|
||||||
this.fileExtension = fileExtension;
|
this.fileExtension = fileExtension;
|
||||||
|
@ -92,7 +92,7 @@ public enum OutputType {
|
||||||
this.names = List.of(names);
|
this.names = List.of(names);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
public static Option<OutputType> ofString(@Nullable final String name) {
|
public static Option<OutputType> ofString(@Nullable final String name) {
|
||||||
return Option.of(name)
|
return Option.of(name)
|
||||||
.map(String::toLowerCase)
|
.map(String::toLowerCase)
|
||||||
|
@ -101,14 +101,14 @@ public enum OutputType {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return this.names.last();
|
return this.names.last();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
public byte[] render(@NonNull final Maze maze) {
|
public byte[] render(@NotNull final Maze maze) {
|
||||||
return this.render.apply(maze);
|
return this.render.apply(maze);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,10 @@ package ch.fritteli.maze.server;
|
||||||
import io.vavr.control.Try;
|
import io.vavr.control.Try;
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.NonNull;
|
|
||||||
import lombok.Value;
|
import lombok.Value;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.wildfly.common.annotation.NotNull;
|
||||||
|
|
||||||
import java.net.InetAddress;
|
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_HOST = "fritteli.maze.server.host";
|
||||||
public static final String SYSPROP_PORT = "fritteli.maze.server.port";
|
public static final String SYSPROP_PORT = "fritteli.maze.server.port";
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
InetAddress address;
|
InetAddress address;
|
||||||
int port;
|
int port;
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ public class ServerConfig {
|
||||||
log.debug("host={}, port={}", this.address, this.port);
|
log.debug("host={}, port={}", this.address, this.port);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
public static ServerConfig init() throws ConfigurationException {
|
public static ServerConfig init() throws ConfigurationException {
|
||||||
final String host = System.getProperty(SYSPROP_HOST);
|
final String host = System.getProperty(SYSPROP_HOST);
|
||||||
final String portString = System.getProperty(SYSPROP_PORT);
|
final String portString = System.getProperty(SYSPROP_PORT);
|
||||||
|
@ -35,7 +35,7 @@ public class ServerConfig {
|
||||||
return new ServerConfig(host, port);
|
return new ServerConfig(host, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
private static InetAddress validateAddress(@Nullable final String address) {
|
private static InetAddress validateAddress(@Nullable final String address) {
|
||||||
return Try.of(() -> InetAddress.getByName(address))
|
return Try.of(() -> InetAddress.getByName(address))
|
||||||
.getOrElseThrow(cause -> new ConfigurationException(
|
.getOrElseThrow(cause -> new ConfigurationException(
|
||||||
|
|
|
@ -3,8 +3,8 @@ package ch.fritteli.maze.server.handler;
|
||||||
import io.undertow.server.HttpHandler;
|
import io.undertow.server.HttpHandler;
|
||||||
import io.undertow.server.HttpServerExchange;
|
import io.undertow.server.HttpServerExchange;
|
||||||
import io.undertow.util.StatusCodes;
|
import io.undertow.util.StatusCodes;
|
||||||
import lombok.NonNull;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.slf4j.MDC;
|
import org.slf4j.MDC;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
@ -14,7 +14,7 @@ import java.util.UUID;
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public abstract class AbstractHttpHandler implements HttpHandler {
|
public abstract class AbstractHttpHandler implements HttpHandler {
|
||||||
@Override
|
@Override
|
||||||
public final void handleRequest(@NonNull final HttpServerExchange exchange) {
|
public final void handleRequest(@NotNull final HttpServerExchange exchange) {
|
||||||
final Instant start = Instant.now();
|
final Instant start = Instant.now();
|
||||||
try (final MDC.MDCCloseable closeable = MDC.putCloseable("correlationId", UUID.randomUUID().toString())) {
|
try (final MDC.MDCCloseable closeable = MDC.putCloseable("correlationId", UUID.randomUUID().toString())) {
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ public abstract class AbstractHttpHandler implements HttpHandler {
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
this.handle(exchange);
|
this.handle(exchange);
|
||||||
} catch (@NonNull final Exception e) {
|
} catch (final Exception e) {
|
||||||
log.error("Error handling request", e);
|
log.error("Error handling request", e);
|
||||||
exchange.setStatusCode(StatusCodes.INTERNAL_SERVER_ERROR)
|
exchange.setStatusCode(StatusCodes.INTERNAL_SERVER_ERROR)
|
||||||
.getResponseSender()
|
.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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,22 @@
|
||||||
package ch.fritteli.maze.server.handler;
|
package ch.fritteli.maze.server.handler;
|
||||||
|
|
||||||
import ch.fritteli.maze.server.OutputType;
|
|
||||||
import ch.fritteli.maze.generator.model.Maze;
|
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.server.HttpServerExchange;
|
||||||
import io.undertow.util.Headers;
|
import io.undertow.util.Headers;
|
||||||
import io.undertow.util.HttpString;
|
import io.undertow.util.HttpString;
|
||||||
import io.undertow.util.StatusCodes;
|
import io.undertow.util.StatusCodes;
|
||||||
import io.vavr.Tuple2;
|
|
||||||
import io.vavr.control.Try;
|
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.nio.ByteBuffer;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import lombok.NonNull;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.slf4j.MDC;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class CreateHandler extends AbstractHttpHandler {
|
public class CreateHandler extends AbstractHttpHandler {
|
||||||
|
@ -23,27 +24,29 @@ public class CreateHandler extends AbstractHttpHandler {
|
||||||
public static final String PATH_TEMPLATE = "/create/{output}";
|
public static final String PATH_TEMPLATE = "/create/{output}";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void handle(@NonNull final HttpServerExchange exchange) {
|
protected void handle(@NotNull final HttpServerExchange exchange) {
|
||||||
final Instant start = Instant.now();
|
final Instant start = Instant.now();
|
||||||
log.debug("Handling create request");
|
log.debug("Handling create request");
|
||||||
this.createMazeFromRequestParameters(exchange.getQueryParameters())
|
this.createMazeFromRequestParameters(exchange.getQueryParameters())
|
||||||
.onFailure(e -> {
|
.onFailure(e -> {
|
||||||
log.error("Error creating maze from request", e);
|
log.error("Error creating maze from request", e);
|
||||||
exchange.setStatusCode(StatusCodes.INTERNAL_SERVER_ERROR)
|
if (e instanceof InvalidRequestParameterException) {
|
||||||
.setReasonPhrase(StatusCodes.INTERNAL_SERVER_ERROR_STRING)
|
exchange.setStatusCode(StatusCodes.BAD_REQUEST);
|
||||||
.getResponseSender()
|
} else {
|
||||||
|
exchange.setStatusCode(StatusCodes.INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
exchange.getResponseSender()
|
||||||
.send(e.getMessage());
|
.send(e.getMessage());
|
||||||
})
|
})
|
||||||
.forEach(tuple -> {
|
.forEach(generatedMaze -> {
|
||||||
final OutputType outputType = tuple._1();
|
final OutputType outputType = generatedMaze.outputType();
|
||||||
final Maze maze = tuple._2();
|
final Maze maze = generatedMaze.maze();
|
||||||
final byte[] bytes;
|
final byte[] bytes;
|
||||||
try {
|
try {
|
||||||
bytes = outputType.render(maze);
|
bytes = outputType.render(maze);
|
||||||
} catch (@NonNull final Exception e) {
|
} catch (final Exception e) {
|
||||||
log.error("Error rendering Maze", e);
|
log.error("Error rendering Maze", e);
|
||||||
exchange.setStatusCode(StatusCodes.INTERNAL_SERVER_ERROR)
|
exchange.setStatusCode(StatusCodes.INTERNAL_SERVER_ERROR)
|
||||||
.setReasonPhrase(StatusCodes.INTERNAL_SERVER_ERROR_STRING)
|
|
||||||
.getResponseSender()
|
.getResponseSender()
|
||||||
.send("Error creating the maze. Please contact the administrator. Request id=%s".formatted(MDC.get("correlationId")));
|
.send("Error creating the maze. Please contact the administrator. Request id=%s".formatted(MDC.get("correlationId")));
|
||||||
return;
|
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-ID"), String.valueOf(maze.getRandomSeed()))
|
||||||
.put(HttpString.tryFromString("X-Maze-Width"), String.valueOf(maze.getWidth()))
|
.put(HttpString.tryFromString("X-Maze-Width"), String.valueOf(maze.getWidth()))
|
||||||
.put(HttpString.tryFromString("X-Maze-Height"), String.valueOf(maze.getHeight()))
|
.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"), generatedMaze.generatorName())
|
||||||
.put(HttpString.tryFromString("X-Maze-Algorithm"), "RandomDepthFirst")
|
|
||||||
.put(HttpString.tryFromString("X-Maze-Generation-Duration-millis"), String.valueOf(durationMillis));
|
.put(HttpString.tryFromString("X-Maze-Generation-Duration-millis"), String.valueOf(durationMillis));
|
||||||
if (outputType.isAttachment()) {
|
if (outputType.isAttachment()) {
|
||||||
exchange.getResponseHeaders()
|
exchange.getResponseHeaders()
|
||||||
|
@ -71,8 +73,8 @@ public class CreateHandler extends AbstractHttpHandler {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
private Try<Tuple2<OutputType, Maze>> createMazeFromRequestParameters(final Map<String, Deque<String>> queryParameters) {
|
private Try<ParametersToMazeExtractor.GeneratedMaze> createMazeFromRequestParameters(final Map<String, Deque<String>> queryParameters) {
|
||||||
return new ParametersToMazeExtractor(queryParameters).createMaze();
|
return new ParametersToMazeExtractor(queryParameters).createMaze();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
package ch.fritteli.maze.server.handler;
|
package ch.fritteli.maze.server.handler;
|
||||||
|
|
||||||
import ch.fritteli.maze.server.OutputType;
|
|
||||||
import ch.fritteli.maze.generator.algorithm.RandomDepthFirst;
|
import ch.fritteli.maze.generator.algorithm.RandomDepthFirst;
|
||||||
import ch.fritteli.maze.generator.model.Maze;
|
import ch.fritteli.maze.generator.model.Maze;
|
||||||
import ch.fritteli.maze.generator.model.Position;
|
import ch.fritteli.maze.generator.model.Position;
|
||||||
import io.vavr.Tuple;
|
import ch.fritteli.maze.server.InvalidRequestParameterException;
|
||||||
import io.vavr.Tuple2;
|
import ch.fritteli.maze.server.OutputType;
|
||||||
import io.vavr.collection.Stream;
|
import io.vavr.collection.Stream;
|
||||||
import io.vavr.control.Option;
|
import io.vavr.control.Option;
|
||||||
import io.vavr.control.Try;
|
import io.vavr.control.Try;
|
||||||
import lombok.NonNull;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -19,11 +18,11 @@ import java.util.Random;
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
class ParametersToMazeExtractor {
|
class ParametersToMazeExtractor {
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
private final Map<String, Deque<String>> queryParameters;
|
private final Map<String, Deque<String>> queryParameters;
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
Try<Tuple2<OutputType, Maze>> createMaze() {
|
Try<GeneratedMaze> createMaze() {
|
||||||
final Option<OutputType> output = getParameterValue(RequestParameter.OUTPUT);
|
final Option<OutputType> output = getParameterValue(RequestParameter.OUTPUT);
|
||||||
final Option<Integer> width = getParameterValue(RequestParameter.WIDTH);
|
final Option<Integer> width = getParameterValue(RequestParameter.WIDTH);
|
||||||
final Option<Integer> height = getParameterValue(RequestParameter.HEIGHT);
|
final Option<Integer> height = getParameterValue(RequestParameter.HEIGHT);
|
||||||
|
@ -32,7 +31,7 @@ class ParametersToMazeExtractor {
|
||||||
final Option<Position> end = getParameterValue(RequestParameter.END);
|
final Option<Position> end = getParameterValue(RequestParameter.END);
|
||||||
|
|
||||||
if (output.isEmpty()) {
|
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("'", " / ", "'"),
|
RequestParameter.OUTPUT.getNames().mkString("'", " / ", "'"),
|
||||||
Stream.of(OutputType.values())
|
Stream.of(OutputType.values())
|
||||||
.flatMap(OutputType::getNames)
|
.flatMap(OutputType::getNames)
|
||||||
|
@ -40,12 +39,12 @@ class ParametersToMazeExtractor {
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
if (width.isEmpty()) {
|
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("'", " / ", "'")
|
RequestParameter.WIDTH.getNames().mkString("'", " / ", "'")
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
if (height.isEmpty()) {
|
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("'", " / ", "'")
|
RequestParameter.HEIGHT.getNames().mkString("'", " / ", "'")
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
@ -58,12 +57,15 @@ class ParametersToMazeExtractor {
|
||||||
maze = new Maze(width.get(), height.get(), id.getOrElse(() -> new Random().nextLong()));
|
maze = new Maze(width.get(), height.get(), id.getOrElse(() -> new Random().nextLong()));
|
||||||
}
|
}
|
||||||
new RandomDepthFirst(maze).run();
|
new RandomDepthFirst(maze).run();
|
||||||
return Tuple.of(output.get(), maze);
|
return new GeneratedMaze(maze, output.get(), RandomDepthFirst.class.getSimpleName());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
private <T> Option<T> getParameterValue(@NonNull final RequestParameter parameter) {
|
private <T> Option<T> getParameterValue(@NotNull final RequestParameter parameter) {
|
||||||
return parameter.getParameterValue(this.queryParameters);
|
return parameter.getParameterValue(this.queryParameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public record GeneratedMaze(@NotNull Maze maze, @NotNull OutputType outputType, @NotNull String generatorName) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
package ch.fritteli.maze.server.handler;
|
package ch.fritteli.maze.server.handler;
|
||||||
|
|
||||||
import ch.fritteli.maze.server.OutputType;
|
|
||||||
import ch.fritteli.maze.generator.model.Maze;
|
import ch.fritteli.maze.generator.model.Maze;
|
||||||
import ch.fritteli.maze.generator.serialization.v1.SerializerDeserializerV1;
|
import ch.fritteli.maze.generator.serialization.v1.SerializerDeserializerV1;
|
||||||
|
import ch.fritteli.maze.server.OutputType;
|
||||||
import io.undertow.server.HttpServerExchange;
|
import io.undertow.server.HttpServerExchange;
|
||||||
import io.undertow.util.HeaderValues;
|
import io.undertow.util.HeaderValues;
|
||||||
import io.undertow.util.Headers;
|
import io.undertow.util.Headers;
|
||||||
import io.undertow.util.StatusCodes;
|
import io.undertow.util.StatusCodes;
|
||||||
import lombok.NonNull;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ public class RenderV1Handler extends AbstractHttpHandler {
|
||||||
public static final String PATH_TEMPLATE = "/render/v1/{output}";
|
public static final String PATH_TEMPLATE = "/render/v1/{output}";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(final HttpServerExchange exchange) {
|
public void handle(@NotNull final HttpServerExchange exchange) {
|
||||||
log.debug("Handling render request");
|
log.debug("Handling render request");
|
||||||
|
|
||||||
if (exchange.isInIoThread()) {
|
if (exchange.isInIoThread()) {
|
||||||
|
@ -47,8 +47,8 @@ public class RenderV1Handler extends AbstractHttpHandler {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
private OutputType getOutputType(@NonNull final HttpServerExchange httpServerExchange) {
|
private OutputType getOutputType(@NotNull final HttpServerExchange httpServerExchange) {
|
||||||
return RequestParameter.OUTPUT.<OutputType>getParameterValue(httpServerExchange.getQueryParameters())
|
return RequestParameter.OUTPUT.<OutputType>getParameterValue(httpServerExchange.getQueryParameters())
|
||||||
.getOrElse(() -> {
|
.getOrElse(() -> {
|
||||||
final HeaderValues accept = httpServerExchange.getRequestHeaders().get(Headers.ACCEPT);
|
final HeaderValues accept = httpServerExchange.getRequestHeaders().get(Headers.ACCEPT);
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
package ch.fritteli.maze.server.handler;
|
package ch.fritteli.maze.server.handler;
|
||||||
|
|
||||||
import ch.fritteli.maze.server.OutputType;
|
|
||||||
import ch.fritteli.maze.generator.model.Maze;
|
import ch.fritteli.maze.generator.model.Maze;
|
||||||
import ch.fritteli.maze.generator.serialization.v2.SerializerDeserializerV2;
|
import ch.fritteli.maze.generator.serialization.v2.SerializerDeserializerV2;
|
||||||
|
import ch.fritteli.maze.server.OutputType;
|
||||||
import io.undertow.server.HttpServerExchange;
|
import io.undertow.server.HttpServerExchange;
|
||||||
import io.undertow.util.HeaderValues;
|
import io.undertow.util.HeaderValues;
|
||||||
import io.undertow.util.Headers;
|
import io.undertow.util.Headers;
|
||||||
import io.undertow.util.StatusCodes;
|
import io.undertow.util.StatusCodes;
|
||||||
import lombok.NonNull;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ public class RenderV2Handler extends AbstractHttpHandler {
|
||||||
public static final String PATH_TEMPLATE = "/render/v2/{output}";
|
public static final String PATH_TEMPLATE = "/render/v2/{output}";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(final HttpServerExchange exchange) {
|
public void handle(@NotNull final HttpServerExchange exchange) {
|
||||||
log.debug("Handling render request");
|
log.debug("Handling render request");
|
||||||
|
|
||||||
if (exchange.isInIoThread()) {
|
if (exchange.isInIoThread()) {
|
||||||
|
@ -47,8 +47,8 @@ public class RenderV2Handler extends AbstractHttpHandler {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
private OutputType getOutputType(@NonNull final HttpServerExchange httpServerExchange) {
|
private OutputType getOutputType(@NotNull final HttpServerExchange httpServerExchange) {
|
||||||
return RequestParameter.OUTPUT.<OutputType>getParameterValue(httpServerExchange.getQueryParameters())
|
return RequestParameter.OUTPUT.<OutputType>getParameterValue(httpServerExchange.getQueryParameters())
|
||||||
.getOrElse(() -> {
|
.getOrElse(() -> {
|
||||||
final HeaderValues accept = httpServerExchange.getRequestHeaders().get(Headers.ACCEPT);
|
final HeaderValues accept = httpServerExchange.getRequestHeaders().get(Headers.ACCEPT);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package ch.fritteli.maze.server.handler;
|
package ch.fritteli.maze.server.handler;
|
||||||
|
|
||||||
import ch.fritteli.maze.server.OutputType;
|
|
||||||
import ch.fritteli.maze.generator.model.Position;
|
import ch.fritteli.maze.generator.model.Position;
|
||||||
|
import ch.fritteli.maze.server.OutputType;
|
||||||
import io.vavr.Tuple2;
|
import io.vavr.Tuple2;
|
||||||
import io.vavr.collection.HashMap;
|
import io.vavr.collection.HashMap;
|
||||||
import io.vavr.collection.HashSet;
|
import io.vavr.collection.HashSet;
|
||||||
|
@ -9,8 +9,8 @@ import io.vavr.collection.Set;
|
||||||
import io.vavr.control.Option;
|
import io.vavr.control.Option;
|
||||||
import io.vavr.control.Try;
|
import io.vavr.control.Try;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NonNull;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -45,24 +45,24 @@ enum RequestParameter {
|
||||||
})
|
})
|
||||||
.toOption()
|
.toOption()
|
||||||
.onEmpty(() -> log.debug("Unparseable value for parameter 'end': '{}'", p)), "e", "end");
|
.onEmpty(() -> log.debug("Unparseable value for parameter 'end': '{}'", p)), "e", "end");
|
||||||
@NonNull
|
@NotNull
|
||||||
private final Function<String, Option<?>> extractor;
|
private final Function<String, Option<?>> extractor;
|
||||||
@Getter
|
@Getter
|
||||||
@NonNull
|
@NotNull
|
||||||
private final Set<String> names;
|
private final Set<String> names;
|
||||||
|
|
||||||
RequestParameter(@NonNull final Function<String, Option<?>> extractor, @NonNull final String... names) {
|
RequestParameter(@NotNull final Function<String, Option<?>> extractor, @NotNull final String... names) {
|
||||||
this.extractor = extractor;
|
this.extractor = extractor;
|
||||||
this.names = HashSet.of(names);
|
this.names = HashSet.of(names);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
Option<?> extractParameterValue(@NonNull final String parameter) {
|
Option<?> extractParameterValue(@NotNull final String parameter) {
|
||||||
return this.extractor.apply(parameter);
|
return this.extractor.apply(parameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NotNull
|
||||||
public <T> Option<T> getParameterValue(@NonNull final Map<String, Deque<String>> queryParameters) {
|
public <T> Option<T> getParameterValue(@NotNull final Map<String, Deque<String>> queryParameters) {
|
||||||
return (Option<T>) HashMap.ofAll(queryParameters)
|
return (Option<T>) HashMap.ofAll(queryParameters)
|
||||||
.filterKeys(this.names::contains)
|
.filterKeys(this.names::contains)
|
||||||
.flatMap(Tuple2::_2)
|
.flatMap(Tuple2::_2)
|
||||||
|
|
Loading…
Reference in a new issue