Cleanup, refactoring.
This commit is contained in:
		
							parent
							
								
									f99efcdad7
								
							
						
					
					
						commit
						2e3e11fbb0
					
				
					 8 changed files with 105 additions and 199 deletions
				
			
		|  | @ -1,10 +1,9 @@ | ||||||
| package ch.fritteli.labyrinth.server; | package ch.fritteli.labyrinth.server; | ||||||
| 
 | 
 | ||||||
| import ch.fritteli.labyrinth.server.handler.CreateHandler; | import ch.fritteli.labyrinth.server.handler.CreateHandler; | ||||||
| import ch.fritteli.labyrinth.server.undertow_playground.LanyrinthRenderHandler; | import ch.fritteli.labyrinth.server.handler.LanyrinthRenderHandler; | ||||||
| import io.undertow.Undertow; | import io.undertow.Undertow; | ||||||
| import io.undertow.server.RoutingHandler; | import io.undertow.server.RoutingHandler; | ||||||
| import io.undertow.server.handlers.RedirectHandler; |  | ||||||
| import io.undertow.util.StatusCodes; | import io.undertow.util.StatusCodes; | ||||||
| import io.vavr.control.Try; | import io.vavr.control.Try; | ||||||
| import lombok.NonNull; | import lombok.NonNull; | ||||||
|  | @ -17,30 +16,27 @@ public class LabyrinthServer { | ||||||
|     @NonNull |     @NonNull | ||||||
|     private final Undertow undertow; |     private final Undertow undertow; | ||||||
| 
 | 
 | ||||||
|     public static Try<LabyrinthServer> createAndStartServer() { |  | ||||||
|         final Try<LabyrinthServer> serverOption = Try.of(ServerConfig::init).mapTry(LabyrinthServer::new); |  | ||||||
|         serverOption.forEach(LabyrinthServer::start); |  | ||||||
|         return serverOption; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private LabyrinthServer(@NonNull final ServerConfig config) { |     private LabyrinthServer(@NonNull final ServerConfig config) { | ||||||
|         log.info("Starting Server at http://{}:{}/", config.getAddress().getHostAddress(), config.getPort()); |         log.info("Starting Server at http://{}:{}/", config.getAddress().getHostAddress(), config.getPort()); | ||||||
|         final RoutingHandler routingHandler = new RoutingHandler().get("/", new RedirectHandler("/create/text")) |         final RoutingHandler routingHandler = new RoutingHandler().get(CreateHandler.PATH_TEMPLATE, new CreateHandler()) | ||||||
|                                                                   .get("/create", new RedirectHandler("/create/text")) |  | ||||||
|                                                                   .get("/create/{output}", new CreateHandler()) |  | ||||||
|                 .post("/render", new LanyrinthRenderHandler()) |                 .post("/render", new LanyrinthRenderHandler()) | ||||||
|                                                                   .setFallbackHandler(exchange -> { |                 .setFallbackHandler(exchange -> exchange.setStatusCode( | ||||||
|                                                                       exchange.setStatusCode(StatusCodes.NOT_FOUND) |                                 StatusCodes.NOT_FOUND) | ||||||
|                         .getResponseSender() |                         .getResponseSender() | ||||||
|                         .send("Resource %s not found".formatted( |                         .send("Resource %s not found".formatted( | ||||||
|                                                                                       exchange.getRequestURI())); |                                 exchange.getRequestURI()))); | ||||||
|                                                                   }); |  | ||||||
|         this.undertow = Undertow.builder() |         this.undertow = Undertow.builder() | ||||||
|                 .addHttpListener(config.getPort(), config.getAddress().getHostAddress()) |                 .addHttpListener(config.getPort(), config.getAddress().getHostAddress()) | ||||||
|                 .setHandler(routingHandler) |                 .setHandler(routingHandler) | ||||||
|                 .build(); |                 .build(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     public static Try<LabyrinthServer> createAndStartServer() { | ||||||
|  |         final Try<LabyrinthServer> serverOption = Try.of(ServerConfig::init).mapTry(LabyrinthServer::new); | ||||||
|  |         serverOption.forEach(LabyrinthServer::start); | ||||||
|  |         return serverOption; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     private void start() { |     private void start() { | ||||||
|         Runtime.getRuntime().addShutdownHook(new Thread(this::stop, "listener-stopper")); |         Runtime.getRuntime().addShutdownHook(new Thread(this::stop, "listener-stopper")); | ||||||
|         this.undertow.start(); |         this.undertow.start(); | ||||||
|  |  | ||||||
|  | @ -1,109 +0,0 @@ | ||||||
| package ch.fritteli.labyrinth.server; |  | ||||||
| 
 |  | ||||||
| import com.sun.net.httpserver.HttpExchange; |  | ||||||
| import com.sun.net.httpserver.HttpHandler; |  | ||||||
| import lombok.NonNull; |  | ||||||
| import lombok.extern.slf4j.Slf4j; |  | ||||||
| import org.apache.pdfbox.io.IOUtils; |  | ||||||
| import org.jetbrains.annotations.Nullable; |  | ||||||
| 
 |  | ||||||
| import java.io.IOException; |  | ||||||
| import java.io.InputStream; |  | ||||||
| import java.net.URI; |  | ||||||
| import java.nio.charset.StandardCharsets; |  | ||||||
| import java.util.concurrent.ExecutorService; |  | ||||||
| 
 |  | ||||||
| @Slf4j |  | ||||||
| public class StaticResourcesFileHandler implements HttpHandler { |  | ||||||
| 
 |  | ||||||
|     public static final String WEBASSETS_DIRECTORY = "webassets"; |  | ||||||
|     private final ExecutorService executorService; |  | ||||||
| 
 |  | ||||||
|     public StaticResourcesFileHandler(final ExecutorService executorService) { |  | ||||||
|         this.executorService = executorService; |  | ||||||
|         log.debug("Created {}", this.getClass().getSimpleName()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void handle(@NonNull final HttpExchange exchange) throws IOException { |  | ||||||
|         this.executorService.submit(() -> { |  | ||||||
|             try { |  | ||||||
|                 final URI requestURI = exchange.getRequestURI(); |  | ||||||
|                 final String path = requestURI.getPath(); |  | ||||||
|                 log.debug("Handling request to {}", path); |  | ||||||
|                 if ("/".equals(path)) { |  | ||||||
|                     redirect(exchange, "index.html"); |  | ||||||
|                     return; |  | ||||||
|                 } |  | ||||||
|                 if (!path.startsWith("/")) { |  | ||||||
|                     notFound(exchange, path); |  | ||||||
|                     return; |  | ||||||
|                 } |  | ||||||
|                 final String mimeType = getMimeType(path); |  | ||||||
|                 if (mimeType == null) { |  | ||||||
|                     notFound(exchange, path); |  | ||||||
|                     return; |  | ||||||
|                 } |  | ||||||
|                 final byte[] responseBytes = getBytes(path); |  | ||||||
|                 if (responseBytes.length == 0) { |  | ||||||
|                     notFound(exchange, path); |  | ||||||
|                     return; |  | ||||||
|                 } |  | ||||||
|                 log.debug( |  | ||||||
|                         "Serving {}{} with mimetype {}: {} bytes", |  | ||||||
|                         WEBASSETS_DIRECTORY, |  | ||||||
|                         path, |  | ||||||
|                         mimeType, |  | ||||||
|                         responseBytes.length |  | ||||||
|                 ); |  | ||||||
|                 exchange.getResponseHeaders().add("Content-type", mimeType); |  | ||||||
|                 exchange.sendResponseHeaders(200, 0); |  | ||||||
|                 exchange.getResponseBody().write(responseBytes); |  | ||||||
|                 exchange.getResponseBody().flush(); |  | ||||||
|             } catch (Exception e) { |  | ||||||
|                 log.error("FSCK!", e); |  | ||||||
|             } finally { |  | ||||||
|                 exchange.close(); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private static void redirect(@NonNull final HttpExchange exchange, @NonNull final String target) throws |  | ||||||
|                                                                                                      IOException { |  | ||||||
|         log.debug("Sending redirect to {}", target); |  | ||||||
|         exchange.getResponseHeaders().add("Location", target); |  | ||||||
|         exchange.sendResponseHeaders(302, -1); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private static void notFound(@NonNull final HttpExchange exchange, @NonNull final String path) throws IOException { |  | ||||||
|         log.debug("Resource '{}' not found, replying with HTTP 404", path); |  | ||||||
|         exchange.getResponseHeaders().add("Content-type", "text/plain; charset=utf-8"); |  | ||||||
|         exchange.sendResponseHeaders(404, 0); |  | ||||||
|         exchange.getResponseBody().write("404 - Not found".getBytes(StandardCharsets.UTF_8)); |  | ||||||
|         exchange.getResponseBody().flush(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Nullable |  | ||||||
|     private static String getMimeType(@NonNull final String path) { |  | ||||||
|         if (path.endsWith(".html")) { |  | ||||||
|             return "text/html"; |  | ||||||
|         } |  | ||||||
|         if (path.endsWith(".css")) { |  | ||||||
|             return "text/css"; |  | ||||||
|         } |  | ||||||
|         return null; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @NonNull |  | ||||||
|     private static byte[] getBytes(@NonNull final String path) throws IOException { |  | ||||||
|         final InputStream stream = StaticResourcesFileHandler.class.getClassLoader() |  | ||||||
|                                                                    .getResourceAsStream(WEBASSETS_DIRECTORY + path); |  | ||||||
|         if (stream == null) { |  | ||||||
|             log.debug("Resource '{}' not found in classpath.", path); |  | ||||||
|             return new byte[0]; |  | ||||||
|         } |  | ||||||
|         final byte[] response = IOUtils.toByteArray(stream); |  | ||||||
|         log.debug("Sending reply; {} bytes", response.length); |  | ||||||
|         return response; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -2,14 +2,18 @@ package ch.fritteli.labyrinth.server.handler; | ||||||
| 
 | 
 | ||||||
| import ch.fritteli.labyrinth.generator.model.Labyrinth; | import ch.fritteli.labyrinth.generator.model.Labyrinth; | ||||||
| import ch.fritteli.labyrinth.server.OutputType; | import ch.fritteli.labyrinth.server.OutputType; | ||||||
| import ch.fritteli.labyrinth.server.handler.AbstractHttpHandler; |  | ||||||
| import ch.fritteli.labyrinth.server.undertow_playground.UndertowPlayground; |  | ||||||
| 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.vavr.Tuple; | ||||||
|  | import io.vavr.Tuple2; | ||||||
|  | import io.vavr.collection.*; | ||||||
| import io.vavr.control.Option; | import io.vavr.control.Option; | ||||||
|  | import io.vavr.control.Try; | ||||||
| import lombok.NonNull; | import lombok.NonNull; | ||||||
| import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||||
|  | import org.jetbrains.annotations.Nullable; | ||||||
| 
 | 
 | ||||||
| import java.nio.ByteBuffer; | import java.nio.ByteBuffer; | ||||||
| import java.util.Deque; | import java.util.Deque; | ||||||
|  | @ -18,26 +22,69 @@ import java.util.Random; | ||||||
| 
 | 
 | ||||||
| @Slf4j | @Slf4j | ||||||
| public class CreateHandler extends AbstractHttpHandler { | public class CreateHandler extends AbstractHttpHandler { | ||||||
|     @Override |     public static final String PATH_TEMPLATE = "/create/{output}"; | ||||||
|     protected void handle(@NonNull final HttpServerExchange exchange) throws Exception { |  | ||||||
|         final Map<String, Deque<String>> queryParameters = exchange.getQueryParameters(); |  | ||||||
|         final Option<String> output = UndertowPlayground.getFirstOption(queryParameters, "output"); |  | ||||||
|         final Option<Integer> width = UndertowPlayground.getIntOption(queryParameters, "width"); |  | ||||||
|         final Option<Integer> height = UndertowPlayground.getIntOption(queryParameters, "height"); |  | ||||||
|         final Option<Integer> id = UndertowPlayground.getIntOption(queryParameters, "id"); |  | ||||||
| 
 | 
 | ||||||
|         log.info("Output: {}", output); |     @Override | ||||||
|         log.info("Width:  {}", width); |     protected void handle(@NonNull final HttpServerExchange exchange) { | ||||||
|         log.info("Height: {}", height); |         this.createLabyrinthFromRequestParameters(exchange.getQueryParameters()) | ||||||
|         log.info("Id:     {}", id); |                 .onFailure(e -> { | ||||||
|         final Integer theId = id.getOrElse(() -> new Random().nextInt()); |                     exchange.setStatusCode(StatusCodes.INTERNAL_SERVER_ERROR); | ||||||
|         final Labyrinth labyrinth = new Labyrinth(width.get(), height.get(), theId); |                     exchange.setReasonPhrase(e.getMessage()); | ||||||
|         final OutputType outputType = output.flatMap(OutputType::ofString).get(); |                 }).forEach(tuple -> { | ||||||
|         final byte[] result = outputType.render(labyrinth); |                     final OutputType outputType = tuple._1(); | ||||||
|  |                     final Labyrinth labyrinth = tuple._2(); | ||||||
|  |                     final byte[] bytes = outputType.render(labyrinth); | ||||||
|                     exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, outputType.getContentType()); |                     exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, outputType.getContentType()); | ||||||
|         exchange.getResponseHeaders().put(HttpString.tryFromString("X-Labyrinth-ID"), String.valueOf(theId)); |                     exchange.getResponseHeaders().put(HttpString.tryFromString("X-Labyrinth-ID"), String.valueOf(labyrinth.getRandomSeed())); | ||||||
|         exchange.getResponseHeaders().put(HttpString.tryFromString("X-Labyrinth-Width"), String.valueOf(width.get())); |                     exchange.getResponseHeaders().put(HttpString.tryFromString("X-Labyrinth-Width"), String.valueOf(labyrinth.getWidth())); | ||||||
|         exchange.getResponseHeaders().put(HttpString.tryFromString("X-Labyrinth-Height"), String.valueOf(height.get())); |                     exchange.getResponseHeaders().put(HttpString.tryFromString("X-Labyrinth-Height"), String.valueOf(labyrinth.getHeight())); | ||||||
|         exchange.getResponseSender().send(ByteBuffer.wrap(result)); |                     exchange.getResponseSender().send(ByteBuffer.wrap(bytes)); | ||||||
|  |                 }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private @NonNull Try<Tuple2<OutputType, Labyrinth>> createLabyrinthFromRequestParameters(final Map<String, Deque<String>> queryParameters) { | ||||||
|  |         return new ParametersToLabyrinthExtractor(queryParameters).createLabyrinth(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private enum RequestParameter { | ||||||
|  |         WIDTH("w", "width"), HEIGHT("h", "height"), ID("i", "id"), OUTPUT("o", "output"); | ||||||
|  |         private final Set<String> names; | ||||||
|  | 
 | ||||||
|  |         RequestParameter(@NonNull final String... names) { | ||||||
|  |             this.names = HashSet.of(names); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         static Option<RequestParameter> parseName(@Nullable final String name) { | ||||||
|  |             if (name == null) { | ||||||
|  |                 return Option.none(); | ||||||
|  |             } | ||||||
|  |             return Stream.of(values()).find(param -> param.names.exists(name::equalsIgnoreCase)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private static class ParametersToLabyrinthExtractor { | ||||||
|  |         @NonNull | ||||||
|  |         private final Multimap<String, String> queryParameters; | ||||||
|  | 
 | ||||||
|  |         ParametersToLabyrinthExtractor(@NonNull final Map<String, Deque<String>> queryParameters) { | ||||||
|  |             this.queryParameters = HashMap.ofAll(queryParameters).foldLeft(HashMultimap.<String>withSet().empty(), (map, tuple) -> { | ||||||
|  |                 final String key = tuple._1(); | ||||||
|  |                 return Stream.ofAll(tuple._2()).foldLeft(map, (m, value) -> m.put(key, value)); | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         @NonNull Try<Tuple2<OutputType, Labyrinth>> createLabyrinth() { | ||||||
|  |             final Option<OutputType> output = getParameterValues(RequestParameter.OUTPUT).foldLeft(Option.none(), (type, param) -> type.orElse(() -> OutputType.ofString(param))); | ||||||
|  |             final Option<Integer> width = getParameterValues(RequestParameter.WIDTH).foldLeft(Option.none(), (value, param) -> value.orElse(() -> Try.of(() -> Integer.parseInt(param)).toOption())); | ||||||
|  |             final Option<Integer> height = getParameterValues(RequestParameter.HEIGHT).foldLeft(Option.none(), (value, param) -> value.orElse(() -> Try.of(() -> Integer.parseInt(param)).toOption())); | ||||||
|  |             final Option<Long> id = getParameterValues(RequestParameter.ID).foldLeft(Option.none(), (value, param) -> value.orElse(() -> Try.of(() -> Long.parseLong(param)).toOption())); | ||||||
|  | 
 | ||||||
|  |             return Try.of(() -> Tuple.of(output.get(), new Labyrinth(width.get(), height.get(), id.getOrElse(() -> new Random().nextLong())))); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         @NonNull | ||||||
|  |         private Stream<String> getParameterValues(@NonNull final RequestParameter parameter) { | ||||||
|  |             return parameter.names.toStream().flatMap(name -> Stream.ofAll(this.queryParameters.getOrElse(name, List.empty()))); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| package ch.fritteli.labyrinth.server.undertow_playground; | package ch.fritteli.labyrinth.server.handler; | ||||||
| 
 | 
 | ||||||
| import io.undertow.server.HttpHandler; | import io.undertow.server.HttpHandler; | ||||||
| import io.undertow.server.HttpServerExchange; | import io.undertow.server.HttpServerExchange; | ||||||
|  | @ -1,28 +0,0 @@ | ||||||
| package ch.fritteli.labyrinth.server.undertow_playground; |  | ||||||
| 
 |  | ||||||
| import io.undertow.server.HttpHandler; |  | ||||||
| import io.undertow.server.HttpServerExchange; |  | ||||||
| import io.undertow.server.RoutingHandler; |  | ||||||
| import io.undertow.util.HeaderValues; |  | ||||||
| import io.undertow.util.Headers; |  | ||||||
| import io.vavr.control.Option; |  | ||||||
| import lombok.NonNull; |  | ||||||
| import lombok.extern.slf4j.Slf4j; |  | ||||||
| 
 |  | ||||||
| import java.util.Deque; |  | ||||||
| import java.util.Map; |  | ||||||
| 
 |  | ||||||
| @Slf4j |  | ||||||
| public class UndertowPlayground { |  | ||||||
|     @NonNull |  | ||||||
|     public static Option<Integer> getIntOption(@NonNull final Map<String, Deque<String>> queryParams, |  | ||||||
|                                                 @NonNull final String paramName) { |  | ||||||
|         return getFirstOption(queryParams, paramName).toTry().map(Integer::parseInt).toOption(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @NonNull |  | ||||||
|     public static Option<String> getFirstOption(@NonNull final Map<String, Deque<String>> queryParams, |  | ||||||
|                                                  @NonNull final String paramName) { |  | ||||||
|         return Option.of(queryParams.get(paramName)).map(Deque::peek).flatMap(Option::of); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -13,5 +13,5 @@ | ||||||
|     <root level="info"> |     <root level="info"> | ||||||
|         <appender-ref ref="STDOUT"/> |         <appender-ref ref="STDOUT"/> | ||||||
|     </root> |     </root> | ||||||
|     <logger name="ch.fritteli.labyrinth.server.StaticResourcesFileHandler" level="debug"/> |     <logger name="ch.fritteli.labyrinth.server.handler.CreateHandler" level="debug"/> | ||||||
| </configuration> | </configuration> | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue