From d4c3df0c959554edf467c224c5ff04bd33d67d91 Mon Sep 17 00:00:00 2001 From: Joe DiPol Date: Mon, 15 Mar 2021 13:59:43 -0700 Subject: [PATCH] Examples: multiple ports (#2834) * Add multiple port examples for WebServer and MicroProfile --- examples/microprofile/multiport/README.md | 40 +++++ examples/microprofile/multiport/pom.xml | 98 +++++++++++ .../multiport/PrivateApplication.java | 35 ++++ .../multiport/PrivateResource.java | 39 +++++ .../multiport/PublicApplication.java | 32 ++++ .../multiport/PublicResource.java | 39 +++++ .../microprofile/multiport/package-info.java | 19 +++ .../src/main/resources/META-INF/beans.xml | 25 +++ .../src/main/resources/application.yaml | 32 ++++ .../microprofile/multiport/MainTest.java | 152 ++++++++++++++++++ examples/microprofile/pom.xml | 1 + examples/webserver/multiport/README.md | 38 +++++ examples/webserver/multiport/pom.xml | 88 ++++++++++ .../examples/webserver/multiport/Main.java | 126 +++++++++++++++ .../webserver/multiport/package-info.java | 19 +++ .../META-INF/native-image/reflect-config.json | 1 + .../src/main/resources/application.yaml | 25 +++ .../src/main/resources/logging.properties | 34 ++++ .../webserver/multiport/MainTest.java | 141 ++++++++++++++++ .../src/test/resources/application-test.yaml | 25 +++ examples/webserver/pom.xml | 1 + .../server/ServerCdiExtension.java | 11 ++ 22 files changed, 1021 insertions(+) create mode 100644 examples/microprofile/multiport/README.md create mode 100644 examples/microprofile/multiport/pom.xml create mode 100644 examples/microprofile/multiport/src/main/java/io/helidon/examples/microprofile/multiport/PrivateApplication.java create mode 100644 examples/microprofile/multiport/src/main/java/io/helidon/examples/microprofile/multiport/PrivateResource.java create mode 100644 examples/microprofile/multiport/src/main/java/io/helidon/examples/microprofile/multiport/PublicApplication.java create mode 100644 examples/microprofile/multiport/src/main/java/io/helidon/examples/microprofile/multiport/PublicResource.java create mode 100644 examples/microprofile/multiport/src/main/java/io/helidon/examples/microprofile/multiport/package-info.java create mode 100644 examples/microprofile/multiport/src/main/resources/META-INF/beans.xml create mode 100644 examples/microprofile/multiport/src/main/resources/application.yaml create mode 100644 examples/microprofile/multiport/src/test/java/io/helidon/examples/microprofile/multiport/MainTest.java create mode 100644 examples/webserver/multiport/README.md create mode 100644 examples/webserver/multiport/pom.xml create mode 100644 examples/webserver/multiport/src/main/java/io/helidon/examples/webserver/multiport/Main.java create mode 100644 examples/webserver/multiport/src/main/java/io/helidon/examples/webserver/multiport/package-info.java create mode 100644 examples/webserver/multiport/src/main/resources/META-INF/native-image/reflect-config.json create mode 100644 examples/webserver/multiport/src/main/resources/application.yaml create mode 100644 examples/webserver/multiport/src/main/resources/logging.properties create mode 100644 examples/webserver/multiport/src/test/java/io/helidon/examples/webserver/multiport/MainTest.java create mode 100644 examples/webserver/multiport/src/test/resources/application-test.yaml diff --git a/examples/microprofile/multiport/README.md b/examples/microprofile/multiport/README.md new file mode 100644 index 00000000000..50dd7334a32 --- /dev/null +++ b/examples/microprofile/multiport/README.md @@ -0,0 +1,40 @@ +# Helidon MicroProfile Multiple Port Example + +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: + +- 8080: public REST endpoints of application +- 8081: private REST endpoints of application +- 8082: admin endpoints for health, metrics, etc. + +This lets you expose only the public endpoints via your +ingress controller or load balancer. + +This example shows a Helidon JAX-RS application running on three ports +as described above. + +The ports are configured in `application.yaml` by using named sockets. + +Two Applications are defined, each associated with a different socket +using @RoutingName. + +## Build and run + +With JDK11+ +```bash +mvn package +java -jar target/helidon-examples-microprofile-multiport.jar +``` + +## Exercise the application + +``` +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/metrics +``` diff --git a/examples/microprofile/multiport/pom.xml b/examples/microprofile/multiport/pom.xml new file mode 100644 index 00000000000..477efb9c9aa --- /dev/null +++ b/examples/microprofile/multiport/pom.xml @@ -0,0 +1,98 @@ + + + + 4.0.0 + + io.helidon.applications + helidon-mp + 2.3.0-SNAPSHOT + ../../../applications/mp/pom.xml + + io.helidon.examples.microprofile + helidon-examples-microprofile-multiport + Helidon Microprofile Examples Multiple Ports + + + + io.helidon.microprofile.bundles + helidon-microprofile-core + + + io.helidon.config + helidon-config-yaml + + + io.helidon.microprofile.metrics + helidon-microprofile-metrics + + + io.helidon.microprofile.health + helidon-microprofile-health + + + org.junit.jupiter + junit-jupiter-api + test + + + org.hamcrest + hamcrest-all + test + + + org.junit.jupiter + junit-jupiter-params + test + + + org.junit.jupiter + junit-jupiter-params + test + + + io.helidon.microprofile.tests + helidon-microprofile-tests-junit5 + test + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-libs + + + + + org.jboss.jandex + jandex-maven-plugin + + + make-index + + + + + + + diff --git a/examples/microprofile/multiport/src/main/java/io/helidon/examples/microprofile/multiport/PrivateApplication.java b/examples/microprofile/multiport/src/main/java/io/helidon/examples/microprofile/multiport/PrivateApplication.java new file mode 100644 index 00000000000..12e67150dae --- /dev/null +++ b/examples/microprofile/multiport/src/main/java/io/helidon/examples/microprofile/multiport/PrivateApplication.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 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.examples.microprofile.multiport; + +import java.util.Set; + +import javax.enterprise.context.ApplicationScoped; +import javax.ws.rs.core.Application; + +import io.helidon.microprofile.server.RoutingName; + +/** + * Application to expose private resource. + */ +@ApplicationScoped +@RoutingName("private") +public class PrivateApplication extends Application { + @Override + public Set> getClasses() { + return Set.of(PrivateResource.class); + } +} diff --git a/examples/microprofile/multiport/src/main/java/io/helidon/examples/microprofile/multiport/PrivateResource.java b/examples/microprofile/multiport/src/main/java/io/helidon/examples/microprofile/multiport/PrivateResource.java new file mode 100644 index 00000000000..5f4c49964fc --- /dev/null +++ b/examples/microprofile/multiport/src/main/java/io/helidon/examples/microprofile/multiport/PrivateResource.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 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.examples.microprofile.multiport; + +import javax.enterprise.context.RequestScoped; +import javax.ws.rs.GET; +import javax.ws.rs.Path; + +/** + * Simple resource. + */ +@RequestScoped +@Path("/private") +public class PrivateResource { + + /** + * Return a private worldly greeting message. + * + * @return {@link String} + */ + @Path("/hello") + @GET + public String helloWorld() { + return "Private Hello World!!"; + } +} diff --git a/examples/microprofile/multiport/src/main/java/io/helidon/examples/microprofile/multiport/PublicApplication.java b/examples/microprofile/multiport/src/main/java/io/helidon/examples/microprofile/multiport/PublicApplication.java new file mode 100644 index 00000000000..6da4ce8b825 --- /dev/null +++ b/examples/microprofile/multiport/src/main/java/io/helidon/examples/microprofile/multiport/PublicApplication.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021 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.examples.microprofile.multiport; + +import java.util.Set; + +import javax.enterprise.context.ApplicationScoped; +import javax.ws.rs.core.Application; + +/** + * Application to expose public resource. + */ +@ApplicationScoped +public class PublicApplication extends Application { + @Override + public Set> getClasses() { + return Set.of(PublicResource.class); + } +} diff --git a/examples/microprofile/multiport/src/main/java/io/helidon/examples/microprofile/multiport/PublicResource.java b/examples/microprofile/multiport/src/main/java/io/helidon/examples/microprofile/multiport/PublicResource.java new file mode 100644 index 00000000000..56d284e6018 --- /dev/null +++ b/examples/microprofile/multiport/src/main/java/io/helidon/examples/microprofile/multiport/PublicResource.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * 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.examples.microprofile.multiport; + +import javax.enterprise.context.RequestScoped; +import javax.ws.rs.GET; +import javax.ws.rs.Path; + +/** + * Simple resource. + */ +@RequestScoped +@Path("/") +public class PublicResource { + + /** + * Return a worldly greeting message. + * + * @return {@link String} + */ + @Path("/hello") + @GET + public String helloWorld() { + return "Public Hello World!!"; + } +} diff --git a/examples/microprofile/multiport/src/main/java/io/helidon/examples/microprofile/multiport/package-info.java b/examples/microprofile/multiport/src/main/java/io/helidon/examples/microprofile/multiport/package-info.java new file mode 100644 index 00000000000..f438f5b6510 --- /dev/null +++ b/examples/microprofile/multiport/src/main/java/io/helidon/examples/microprofile/multiport/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2021 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. + */ +/** + * Application that exposes multiple ports. + */ +package io.helidon.examples.microprofile.multiport; diff --git a/examples/microprofile/multiport/src/main/resources/META-INF/beans.xml b/examples/microprofile/multiport/src/main/resources/META-INF/beans.xml new file mode 100644 index 00000000000..68ffb25b5ed --- /dev/null +++ b/examples/microprofile/multiport/src/main/resources/META-INF/beans.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/examples/microprofile/multiport/src/main/resources/application.yaml b/examples/microprofile/multiport/src/main/resources/application.yaml new file mode 100644 index 00000000000..34af19c84ea --- /dev/null +++ b/examples/microprofile/multiport/src/main/resources/application.yaml @@ -0,0 +1,32 @@ +# +# Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. +# +# 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. +# +server: + port: 8080 + host: "localhost" + sockets: + - name: "private" + port: 8081 + bind-address: "localhost" + - name: "admin" + port: 8082 + bind-address: "localhost" + +# Metrics and health run on admin port +metrics: + routing: "admin" + +health: + routing: "admin" \ No newline at end of file diff --git a/examples/microprofile/multiport/src/test/java/io/helidon/examples/microprofile/multiport/MainTest.java b/examples/microprofile/multiport/src/test/java/io/helidon/examples/microprofile/multiport/MainTest.java new file mode 100644 index 00000000000..5162602786d --- /dev/null +++ b/examples/microprofile/multiport/src/test/java/io/helidon/examples/microprofile/multiport/MainTest.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * 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.examples.microprofile.multiport; + +import javax.inject.Inject; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; + +import io.helidon.microprofile.server.ServerCdiExtension; +import io.helidon.microprofile.tests.junit5.AddConfig; +import io.helidon.microprofile.tests.junit5.HelidonTest; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.List; +import java.util.stream.Stream; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * Unit test for {@link Main}. + */ +@HelidonTest +@AddConfig(key = "server.sockets.0.port", value = "0") // Force ports to be dynamically allocated +@AddConfig(key = "server.sockets.1.port", value = "0") +class MainTest { + + // Used for the default (public) port which HelidonTest will configure for us + @Inject + private WebTarget publicWebTarget; + + // Used for other (private, admin) ports + private static Client client; + + // Port names + private static final String ADMIN_PORT = "admin"; + private static final String PRIVATE_PORT = "private"; + private static final String PUBLIC_PORT = "default"; + + private static final String BASE_URL = "http://localhost:"; + + // Needed to get values of dynamically allocated admin and private ports + @Inject + private ServerCdiExtension serverCdiExtension; + + static Stream initParams() { + final String PUBLIC_PATH = "/hello"; + final String PRIVATE_PATH = "/private/hello"; + final String HEALTH_PATH = "/health"; + final String METRICS_PATH = "/health"; + + return List.of( + new Params(PUBLIC_PORT, PUBLIC_PATH, Status.OK), + new Params(PUBLIC_PORT, PRIVATE_PATH, Status.NOT_FOUND), + new Params(PUBLIC_PORT, HEALTH_PATH, Status.NOT_FOUND), + new Params(PUBLIC_PORT, METRICS_PATH, Status.NOT_FOUND), + + new Params(PRIVATE_PORT, PUBLIC_PATH, Status.NOT_FOUND), + new Params(PRIVATE_PORT, PRIVATE_PATH, Status.OK), + new Params(PRIVATE_PORT, HEALTH_PATH, Status.NOT_FOUND), + new Params(PRIVATE_PORT, METRICS_PATH, Status.NOT_FOUND), + + new Params(ADMIN_PORT, PUBLIC_PATH, Status.NOT_FOUND), + new Params(ADMIN_PORT, PRIVATE_PATH, Status.NOT_FOUND), + new Params(ADMIN_PORT, HEALTH_PATH, Status.OK), + new Params(ADMIN_PORT, METRICS_PATH, Status.OK) + ).stream(); + } + + @BeforeAll + static void initClass() { + client = ClientBuilder.newClient(); + } + + @MethodSource("initParams") + @ParameterizedTest + public void testPortAccess(Params params) { + + WebTarget webTarget = publicWebTarget; + if (!PUBLIC_PORT.equals(params.portName)) { + webTarget = client.target(BASE_URL + serverCdiExtension.port(params.portName)); + } + + Response response = webTarget.path(params.path) + .request() + .get(); + assertThat(webTarget.getUri() + " returned incorrect HTTP status", + response.getStatusInfo().toEnum(), is(params.httpStatus)); + } + + @Test + void testEndpoints() { + Response response; + + // Probe PUBLIC port + response = publicWebTarget.path("/hello") + .request() + .get(); + assertThat("default port should be serving public resource", + response.getStatusInfo().toEnum(), is(Response.Status.OK)); + assertThat("default port should return public data", + response.readEntity(String.class), is("Public Hello World!!")); + + // Probe PRIVATE port + int privatePort = serverCdiExtension.port(PRIVATE_PORT); + WebTarget baseTarget = client.target(BASE_URL + privatePort); + response = baseTarget.path("/private/hello") + .request() + .get(); + assertThat("port " + privatePort + " should be serving private resource", + response.getStatusInfo().toEnum(), is(Response.Status.OK)); + assertThat("port " + privatePort + " should return private data", + response.readEntity(String.class), is("Private Hello World!!")); + } + + private static class Params { + String portName; + String path; + Response.Status httpStatus; + + private Params(String portName, String path, Response.Status httpStatus) { + this.portName = portName; + this.path = path; + this.httpStatus = httpStatus; + } + + @Override + public String toString() { + return portName + ":" + path + " should return " + httpStatus; + } + } +} diff --git a/examples/microprofile/pom.xml b/examples/microprofile/pom.xml index cdf995dc5d6..f82733da61b 100644 --- a/examples/microprofile/pom.xml +++ b/examples/microprofile/pom.xml @@ -45,5 +45,6 @@ messaging-sse cors tls + multiport diff --git a/examples/webserver/multiport/README.md b/examples/webserver/multiport/README.md new file mode 100644 index 00000000000..f028517f3c1 --- /dev/null +++ b/examples/webserver/multiport/README.md @@ -0,0 +1,38 @@ +# Helidon WebServer Multiple Port Example + +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: + +- 8080: public REST endpoints of application +- 8081: private REST endpoints of application +- 8082: admin endpoints for health, metrics, etc. + +This lets you expose only the public endpoints via your +ingress controller or load balancer. + +This example shows a Helidon application running on three ports +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` + +## Build and run + +With JDK11+ +```bash +mvn package +java -jar target/helidon-examples-webserver-multiport.jar +``` +## Exercise the application + +``` +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/metrics +``` diff --git a/examples/webserver/multiport/pom.xml b/examples/webserver/multiport/pom.xml new file mode 100644 index 00000000000..713d0fbbca3 --- /dev/null +++ b/examples/webserver/multiport/pom.xml @@ -0,0 +1,88 @@ + + + + 4.0.0 + + io.helidon.applications + helidon-se + 2.3.0-SNAPSHOT + ../../../applications/se/pom.xml + + io.helidon.examples.webserver + helidon-examples-webserver-multiport + Helidon WebServer Examples Multiple Ports + + + io.helidon.examples.webserver.multiport.Main + + + + + io.helidon.webserver + helidon-webserver + + + io.helidon.config + helidon-config-yaml + + + io.helidon.health + helidon-health + + + io.helidon.health + helidon-health-checks + + + io.helidon.metrics + helidon-metrics + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-params + test + + + io.helidon.webclient + helidon-webclient + test + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-libs + + + + + + diff --git a/examples/webserver/multiport/src/main/java/io/helidon/examples/webserver/multiport/Main.java b/examples/webserver/multiport/src/main/java/io/helidon/examples/webserver/multiport/Main.java new file mode 100644 index 00000000000..5c46d58d0fd --- /dev/null +++ b/examples/webserver/multiport/src/main/java/io/helidon/examples/webserver/multiport/Main.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2021 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.examples.webserver.multiport; + +import io.helidon.common.LogConfig; +import io.helidon.common.reactive.Single; +import io.helidon.config.Config; +import io.helidon.health.HealthSupport; +import io.helidon.health.checks.HealthChecks; +import io.helidon.metrics.MetricsSupport; +import io.helidon.webserver.Routing; +import io.helidon.webserver.WebServer; + +/** + * The application main class. + */ +public final class Main { + + /** + * Cannot be instantiated. + */ + private Main() { + } + + /** + * Application main entry point. + * @param args command line arguments. + */ + public static void main(final String[] args) { + // By default this will pick up application.yaml from the classpath + Config config = Config.create(); + startServer(config); + } + + /** + * Start the server. + * @return the created {@link WebServer} instance + */ + static Single startServer(Config config) { + // load logging configuration + LogConfig.configureRuntime(); + + // Build server using three ports: + // default public port, admin port, private port + WebServer server = WebServer.builder(createPublicRouting()) + .config(config.get("server")) + // Add a set of routes on the named socket "admin" + .addNamedRouting("admin", createAdminRouting()) + // Add a set of routes on the named socket "private" + .addNamedRouting("private", createPrivateRouting()) + .build(); + + Single webserver = server.start(); + + // Try to start the server. If successful, print some info and arrange to + // print a message at shutdown. If unsuccessful, print the exception. + webserver.thenAccept(ws -> { + System.out.println( + "WEB server is up! http://localhost:" + ws.port()); + ws.whenShutdown().thenRun(() + -> System.out.println("WEB server is DOWN. Good bye!")); + }) + .exceptionally(t -> { + System.err.println("Startup failed: " + t.getMessage()); + t.printStackTrace(System.err); + return null; + }); + + // Server threads are not daemon. No need to block. Just react. + + return webserver; + } + + /** + * Creates private {@link Routing}. + * + * @return routing for use on "private" port + */ + private static Routing createPrivateRouting() { + return Routing.builder() + .get("/private/hello", (req, res) -> res.send("Private Hello!!")) + .build(); + } + + /** + * Creates public {@link Routing}. + * + * @return routing for use on "public" port + */ + private static Routing createPublicRouting() { + return Routing.builder() + .get("/hello", (req, res) -> res.send("Public Hello!!")) + .build(); + } + + /** + * Creates admin {@link Routing}. + * + * @return routing for use on admin port + */ + private static Routing createAdminRouting() { + MetricsSupport metrics = MetricsSupport.create(); + HealthSupport health = HealthSupport.builder() + .addLiveness(HealthChecks.healthChecks()) // Adds a convenient set of checks + .build(); + + return Routing.builder() + .register(health) + .register(metrics) + .build(); + } +} diff --git a/examples/webserver/multiport/src/main/java/io/helidon/examples/webserver/multiport/package-info.java b/examples/webserver/multiport/src/main/java/io/helidon/examples/webserver/multiport/package-info.java new file mode 100644 index 00000000000..89a5dad55f8 --- /dev/null +++ b/examples/webserver/multiport/src/main/java/io/helidon/examples/webserver/multiport/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2021 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. + */ +/** + * Application that exposes multiple ports. + */ +package io.helidon.examples.webserver.multiport; diff --git a/examples/webserver/multiport/src/main/resources/META-INF/native-image/reflect-config.json b/examples/webserver/multiport/src/main/resources/META-INF/native-image/reflect-config.json new file mode 100644 index 00000000000..fe51488c706 --- /dev/null +++ b/examples/webserver/multiport/src/main/resources/META-INF/native-image/reflect-config.json @@ -0,0 +1 @@ +[] diff --git a/examples/webserver/multiport/src/main/resources/application.yaml b/examples/webserver/multiport/src/main/resources/application.yaml new file mode 100644 index 00000000000..f77248bd3e1 --- /dev/null +++ b/examples/webserver/multiport/src/main/resources/application.yaml @@ -0,0 +1,25 @@ +# +# Copyright (c) 2021 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. +# +server: + port: 8080 + host: "localhost" + sockets: + - name: "private" + port: 8081 + bind-address: "localhost" + - name: "admin" + port: 8082 + bind-address: "localhost" diff --git a/examples/webserver/multiport/src/main/resources/logging.properties b/examples/webserver/multiport/src/main/resources/logging.properties new file mode 100644 index 00000000000..aced7e48602 --- /dev/null +++ b/examples/webserver/multiport/src/main/resources/logging.properties @@ -0,0 +1,34 @@ +# +# Copyright (c) 2021 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. +# + +# Example Logging Configuration File +# For more information see $JAVA_HOME/jre/lib/logging.properties + +# Send messages to the console +handlers=io.helidon.common.HelidonConsoleHandler + +# HelidonConsoleHandler uses a SimpleFormatter subclass that replaces "!thread!" with the current thread +java.util.logging.SimpleFormatter.format=%1$tY.%1$tm.%1$td %1$tH:%1$tM:%1$tS %4$s %3$s !thread!: %5$s%6$s%n + +# Global logging level. Can be overridden by specific loggers +.level=INFO + +# Component specific log levels +#io.helidon.webserver.level=INFO +#io.helidon.config.level=INFO +#io.helidon.security.level=INFO +#io.helidon.common.level=INFO +#io.netty.level=INFO diff --git a/examples/webserver/multiport/src/test/java/io/helidon/examples/webserver/multiport/MainTest.java b/examples/webserver/multiport/src/test/java/io/helidon/examples/webserver/multiport/MainTest.java new file mode 100644 index 00000000000..424279fcac6 --- /dev/null +++ b/examples/webserver/multiport/src/test/java/io/helidon/examples/webserver/multiport/MainTest.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2021 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.examples.webserver.multiport; + +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; + +import io.helidon.common.http.Http; +import io.helidon.common.reactive.Single; +import io.helidon.config.Config; +import io.helidon.config.ConfigSources; +import io.helidon.webclient.WebClient; +import io.helidon.webserver.WebServer; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +public class MainTest { + + private static WebServer webServer; + private static WebClient webClient; + private static int publicPort; + private static int privatePort; + private static int adminPort; + + @BeforeAll + public static void startTheServer() { + // Use test configuration so we can have ports allocated dynamically + Config config = Config.builder().addSource(ConfigSources.classpath("application-test.yaml")).build(); + Single w = Main.startServer(config); + webServer = w.await(); + webClient = WebClient.builder().build(); + + publicPort = webServer.port(); + privatePort = webServer.port("private"); + adminPort = webServer.port("admin"); + } + + static Stream initParams() { + final String PUBLIC_PATH = "/hello"; + final String PRIVATE_PATH = "/private/hello"; + final String HEALTH_PATH = "/health"; + final String METRICS_PATH = "/health"; + + return List.of( + new Params(publicPort, PUBLIC_PATH, Http.Status.OK_200), + new Params(publicPort, PRIVATE_PATH, Http.Status.NOT_FOUND_404), + new Params(publicPort, HEALTH_PATH, Http.Status.NOT_FOUND_404), + new Params(publicPort, METRICS_PATH, Http.Status.NOT_FOUND_404), + + new Params(privatePort, PUBLIC_PATH, Http.Status.NOT_FOUND_404), + new Params(privatePort, PRIVATE_PATH, Http.Status.OK_200), + new Params(privatePort, HEALTH_PATH, Http.Status.NOT_FOUND_404), + new Params(privatePort, METRICS_PATH, Http.Status.NOT_FOUND_404), + + new Params(adminPort, PUBLIC_PATH, Http.Status.NOT_FOUND_404), + new Params(adminPort, PRIVATE_PATH, Http.Status.NOT_FOUND_404), + new Params(adminPort, HEALTH_PATH, Http.Status.OK_200), + new Params(adminPort, METRICS_PATH, Http.Status.OK_200) + ).stream(); + } + + @AfterAll + public static void stopServer() throws Exception { + if (webServer != null) { + webServer.shutdown() + .toCompletableFuture() + .get(10, TimeUnit.SECONDS); + } + } + + @MethodSource("initParams") + @ParameterizedTest + public void portAccessTest(Params params) throws Exception { + // Verifies we can access endpoints only on the proper port + webClient.get() + .uri("http://localhost:" + params.port) + .path(params.path) + .request() + .thenAccept(response -> Assertions.assertEquals(params.httpStatus, response.status())) + .toCompletableFuture() + .get(); + } + + @Test + public void portTest() throws Exception { + + webClient.get() + .uri("http://localhost:" + publicPort) + .path("/hello") + .request(String.class) + .thenAccept(s -> Assertions.assertEquals("Public Hello!!", s)) + .toCompletableFuture() + .get(); + + webClient.get() + .uri("http://localhost:" + privatePort) + .path("/private/hello") + .request(String.class) + .thenAccept(s -> Assertions.assertEquals("Private Hello!!", s)) + .toCompletableFuture() + .get(); + } + + private static class Params { + int port; + String path; + Http.Status httpStatus; + + private Params(int port, String path, Http.Status httpStatus) { + this.port = port; + this.path = path; + this.httpStatus = httpStatus; + } + + @Override + public String toString() { + return port + ":" + path + " should return " + httpStatus; + } + } + +} diff --git a/examples/webserver/multiport/src/test/resources/application-test.yaml b/examples/webserver/multiport/src/test/resources/application-test.yaml new file mode 100644 index 00000000000..d7a80acca0b --- /dev/null +++ b/examples/webserver/multiport/src/test/resources/application-test.yaml @@ -0,0 +1,25 @@ +# +# Copyright (c) 2021 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. +# +server: + port: 0 + host: "localhost" + sockets: + - name: "private" + port: 0 + bind-address: "localhost" + - name: "admin" + port: 0 + bind-address: "localhost" diff --git a/examples/webserver/pom.xml b/examples/webserver/pom.xml index fc2383b2fad..43a4ce81915 100644 --- a/examples/webserver/pom.xml +++ b/examples/webserver/pom.xml @@ -43,5 +43,6 @@ tls mutual-tls fault-tolerance + multiport diff --git a/microprofile/server/src/main/java/io/helidon/microprofile/server/ServerCdiExtension.java b/microprofile/server/src/main/java/io/helidon/microprofile/server/ServerCdiExtension.java index 70a06d80d19..1f08f2d7b09 100644 --- a/microprofile/server/src/main/java/io/helidon/microprofile/server/ServerCdiExtension.java +++ b/microprofile/server/src/main/java/io/helidon/microprofile/server/ServerCdiExtension.java @@ -502,6 +502,17 @@ public int port() { return port; } + /** + * Named port the server is running on. This information is only available after the + * server is actually started. + * + * @param name Socket name + * @return Named port the server is running on + */ + public int port(String name) { + return webserver.port(name); + } + /** * State of the server. *