From c4ac071f73e6a72a1ae132750987693b0c811358 Mon Sep 17 00:00:00 2001 From: Romain Grecourt Date: Mon, 2 Oct 2023 18:08:12 -0700 Subject: [PATCH] Add a simple way to register routing for a named socket. - Update WebServerConfigBlueprint and LoomServer - Update multiport example to use the new API - Fix multiport example README.md and dependencies (/observe and dependencies) Fixes #7702 --- examples/webserver/multiport/README.md | 8 +-- examples/webserver/multiport/pom.xml | 6 +- .../webserver/examples/multiport/Main.java | 8 +-- .../java/io/helidon/webserver/LoomServer.java | 12 +++- .../webserver/WebServerConfigBlueprint.java | 17 ++++++ .../webserver/WebServerConfigSupport.java | 57 +++++++++++++++++++ 6 files changed, 95 insertions(+), 13 deletions(-) create mode 100644 webserver/webserver/src/main/java/io/helidon/webserver/WebServerConfigSupport.java diff --git a/examples/webserver/multiport/README.md b/examples/webserver/multiport/README.md index f028517f3c1..2bb5ce5e2b7 100644 --- a/examples/webserver/multiport/README.md +++ b/examples/webserver/multiport/README.md @@ -2,7 +2,7 @@ It is common when deploying a microservice to run your service on multiple ports so that you can control the visibility of your -service's endpoints. For example you might want to use three ports: +service's endpoints. For example, you might want to use three ports: - 8080: public REST endpoints of application - 8081: private REST endpoints of application @@ -16,7 +16,7 @@ as described above. The ports are configured in `application.yaml` by using named sockets. -Seperate routing is defined for each named socket in `Main.java` +Separate routing is defined for each named socket in `Main.java` ## Build and run @@ -32,7 +32,7 @@ curl -X GET http://localhost:8080/hello curl -X GET http://localhost:8081/private/hello -curl -X GET http://localhost:8082/health +curl -X GET http://localhost:8082/observe/health -curl -X GET http://localhost:8082/metrics +curl -X GET http://localhost:8082/observe/metrics ``` diff --git a/examples/webserver/multiport/pom.xml b/examples/webserver/multiport/pom.xml index 582e86ba78a..d7f9bb3f0e9 100644 --- a/examples/webserver/multiport/pom.xml +++ b/examples/webserver/multiport/pom.xml @@ -47,6 +47,10 @@ io.helidon.config helidon-config-yaml + + io.helidon.webserver.observe + helidon-webserver-observe-health + io.helidon.health helidon-health-checks @@ -54,12 +58,10 @@ io.helidon.webserver.observe helidon-webserver-observe-metrics - runtime io.helidon.metrics helidon-metrics-system-meters - runtime io.helidon.webserver.testing.junit5 diff --git a/examples/webserver/multiport/src/main/java/io/helidon/webserver/examples/multiport/Main.java b/examples/webserver/multiport/src/main/java/io/helidon/webserver/examples/multiport/Main.java index e03f6ab5988..d3d88f87cc2 100644 --- a/examples/webserver/multiport/src/main/java/io/helidon/webserver/examples/multiport/Main.java +++ b/examples/webserver/multiport/src/main/java/io/helidon/webserver/examples/multiport/Main.java @@ -63,12 +63,8 @@ static void setup(WebServerConfig.Builder server) { // Build server using three ports: // default public port, admin port, private port server.routing(Main::publicRouting) - // Add a set of routes on the named socket "admin" - .putSocket("admin", socket -> socket.from(server.sockets().get("admin")) - .routing(Main::adminSocket)) - // Add a set of routes on the named socket "private" - .putSocket("private", socket -> socket.from(server.sockets().get("admin")) - .routing(Main::privateSocket)); + .routing("admin", Main::adminSocket) + .routing("private", Main::privateSocket); } /** diff --git a/webserver/webserver/src/main/java/io/helidon/webserver/LoomServer.java b/webserver/webserver/src/main/java/io/helidon/webserver/LoomServer.java index 438c25ac318..335d5dc9c40 100644 --- a/webserver/webserver/src/main/java/io/helidon/webserver/LoomServer.java +++ b/webserver/webserver/src/main/java/io/helidon/webserver/LoomServer.java @@ -102,14 +102,24 @@ class LoomServer implements WebServer, Startable { if (routing.isEmpty() && routings.isEmpty()) { routerBuilder.addRouting(HttpRouting.create()); } + Router router = routerBuilder.build(); Timer idleConnectionTimer = new Timer("helidon-idle-connection-timer", true); Map listenerMap = new HashMap<>(); sockets.forEach((name, socketConfig) -> { + List socketRoutings = serverConfig.namedRoutings().get(name); + Router socketRouter; + if (socketRoutings != null) { + socketRouter = Router.builder() + .update(b -> socketRoutings.forEach(b::addRouting)) + .build(); + } else { + socketRouter = router; + } listenerMap.put(name, new ServerListener(name, socketConfig, - routerBuilder.build(), + socketRouter, context, idleConnectionTimer, serverConfig.mediaContext().orElseGet(MediaContext::create), diff --git a/webserver/webserver/src/main/java/io/helidon/webserver/WebServerConfigBlueprint.java b/webserver/webserver/src/main/java/io/helidon/webserver/WebServerConfigBlueprint.java index b92a2a9162e..d480a387932 100644 --- a/webserver/webserver/src/main/java/io/helidon/webserver/WebServerConfigBlueprint.java +++ b/webserver/webserver/src/main/java/io/helidon/webserver/WebServerConfigBlueprint.java @@ -16,6 +16,7 @@ package io.helidon.webserver; +import java.util.List; import java.util.Map; import java.util.Optional; @@ -32,6 +33,7 @@ * See {@link WebServer#create(java.util.function.Consumer)}. */ @Prototype.Blueprint(decorator = WebServerConfigBlueprint.ServerConfigDecorator.class) +@Prototype.CustomMethods(WebServerConfigSupport.CustomMethods.class) @Configured(root = true, prefix = "server") @ConfigBean(wantDefault = true) interface WebServerConfigBlueprint extends ListenerConfigBlueprint, Prototype.Factory { @@ -56,6 +58,17 @@ interface WebServerConfigBlueprint extends ListenerConfigBlueprint, Prototype.Fa @Option.Singular Map sockets(); + /** + * Routing for additional sockets. + * Note that socket named {@value WebServer#DEFAULT_SOCKET_NAME} cannot be used, + * configure the routing on the server directly. + * + * @return map of routing + */ + @Option.Singular + @Option.Access("") + Map> namedRoutings(); + /** * Context for the WebServer, if none defined, a new one will be created with global context as the root. * @@ -70,6 +83,10 @@ public void decorate(WebServerConfig.BuilderBase target) { throw new ConfigException("Default socket must be configured directly on server config node, or through" + " \"ServerConfig.Builder\", not as a separated socket."); } + if (target.namedRoutings().containsKey(WebServer.DEFAULT_SOCKET_NAME)) { + throw new ConfigException("Default routing must be configured directly on server config node, or through" + + " \"ServerConfig.Builder\", not as a named routing."); + } } } } diff --git a/webserver/webserver/src/main/java/io/helidon/webserver/WebServerConfigSupport.java b/webserver/webserver/src/main/java/io/helidon/webserver/WebServerConfigSupport.java new file mode 100644 index 00000000000..5531cfb4323 --- /dev/null +++ b/webserver/webserver/src/main/java/io/helidon/webserver/WebServerConfigSupport.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.helidon.webserver; + +import java.util.function.Consumer; + +import io.helidon.builder.api.Prototype; +import io.helidon.webserver.http.HttpRouting; + +class WebServerConfigSupport { + + static class CustomMethods { + + /** + * Add Http routing for an additional socket. + * + * @param builder builder to update + * @param socket name of the socket + * @param consumer HTTP Routing for the given socket name + */ + @Prototype.BuilderMethod + static void routing(WebServerConfig.BuilderBase builder, + String socket, + Consumer consumer) { + HttpRouting.Builder routingBuilder = HttpRouting.builder(); + consumer.accept(routingBuilder); + builder.addNamedRouting(socket, routingBuilder.build()); + } + + /** + * Add Http routing for an additional socket. + * + * @param builder builder to update + * @param socket name of the socket + * @param routing HTTP Routing for the given socket name + */ + @Prototype.BuilderMethod + static void routing(WebServerConfig.BuilderBase builder, + String socket, + HttpRouting routing) { + builder.addNamedRouting(socket, routing); + } + } +}