From 51a0e89d759ae265ce97791f189218b9b3682c1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Do=C4=9Fa=C3=A7=20Eldenk?= Date: Fri, 7 Mar 2025 21:27:37 -0600 Subject: [PATCH 1/3] =?UTF-8?q?Fix=20jetty=20auto-start=20when=20port=20is?= =?UTF-8?q?=20-1=20Signed-off-by:=20Do=C4=9Fa=C3=A7=20Eldenk=20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/embedded/jetty/JettyWebServer.java | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyWebServer.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyWebServer.java index 5fd713374a59..25a6b0349969 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyWebServer.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyWebServer.java @@ -145,24 +145,23 @@ public void start() throws WebServerException { return; } this.server.setConnectors(this.connectors); - if (!this.autoStart) { - return; - } try { this.server.start(); - for (Handler handler : this.server.getHandlers()) { - handleDeferredInitialize(handler); - } - Connector[] connectors = this.server.getConnectors(); - for (Connector connector : connectors) { - try { - connector.start(); + if (this.autoStart) { + for (Handler handler : this.server.getHandlers()) { + handleDeferredInitialize(handler); } - catch (IOException ex) { - if (connector instanceof NetworkConnector networkConnector) { - PortInUseException.throwIfPortBindingException(ex, networkConnector::getPort); + Connector[] connectors = this.server.getConnectors(); + for (Connector connector : connectors) { + try { + connector.start(); + } + catch (IOException ex) { + if (connector instanceof NetworkConnector networkConnector) { + PortInUseException.throwIfPortBindingException(ex, networkConnector::getPort); + } + throw ex; } - throw ex; } } this.started = true; From a2fc66e2af1384b427f250baf3c22a1450c08a83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Do=C4=9Fa=C3=A7=20Eldenk?= Date: Sun, 9 Mar 2025 10:01:49 -0500 Subject: [PATCH 2/3] add smoke tests for armeria jetty/tomcat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Doğaç Eldenk --- .../web/embedded/jetty/JettyWebServer.java | 6 +- .../build.gradle | 22 +++++ .../armeria/jetty/ArmeriaConfiguration.java | 78 +++++++++++++++++ .../jetty/SampleArmeriaJettyApplication.java | 29 +++++++ .../jetty/service/HelloWorldService.java | 32 +++++++ .../armeria/jetty/web/SampleController.java | 40 +++++++++ .../src/main/resources/config/application.yml | 7 ++ .../SampleArmeriaJettyApplicationTests.java | 68 +++++++++++++++ .../build.gradle | 21 +++++ .../armeria/tomcat/ArmeriaConfiguration.java | 84 +++++++++++++++++++ .../SampleArmeriaTomcatApplication.java | 54 ++++++++++++ .../tomcat/service/HelloWorldService.java | 32 +++++++ .../armeria/tomcat/web/SampleController.java | 40 +++++++++ .../src/main/resources/config/application.yml | 7 ++ .../SampleArmeriaTomcatApplicationTests.java | 71 ++++++++++++++++ 15 files changed, 588 insertions(+), 3 deletions(-) create mode 100644 spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-jetty/build.gradle create mode 100644 spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-jetty/src/main/java/smoketest/armeria/jetty/ArmeriaConfiguration.java create mode 100644 spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-jetty/src/main/java/smoketest/armeria/jetty/SampleArmeriaJettyApplication.java create mode 100644 spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-jetty/src/main/java/smoketest/armeria/jetty/service/HelloWorldService.java create mode 100644 spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-jetty/src/main/java/smoketest/armeria/jetty/web/SampleController.java create mode 100644 spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-jetty/src/main/resources/config/application.yml create mode 100644 spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-jetty/src/test/java/smoketest/armeria/jetty/SampleArmeriaJettyApplicationTests.java create mode 100644 spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-tomcat/build.gradle create mode 100644 spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-tomcat/src/main/java/smoketest/armeria/tomcat/ArmeriaConfiguration.java create mode 100644 spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-tomcat/src/main/java/smoketest/armeria/tomcat/SampleArmeriaTomcatApplication.java create mode 100644 spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-tomcat/src/main/java/smoketest/armeria/tomcat/service/HelloWorldService.java create mode 100644 spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-tomcat/src/main/java/smoketest/armeria/tomcat/web/SampleController.java create mode 100644 spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-tomcat/src/main/resources/config/application.yml create mode 100644 spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-tomcat/src/test/java/smoketest/armeria/tomcat/SampleArmeriaTomcatApplicationTests.java diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyWebServer.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyWebServer.java index 25a6b0349969..2364354dc834 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyWebServer.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyWebServer.java @@ -147,10 +147,10 @@ public void start() throws WebServerException { this.server.setConnectors(this.connectors); try { this.server.start(); + for (Handler handler : this.server.getHandlers()) { + handleDeferredInitialize(handler); + } if (this.autoStart) { - for (Handler handler : this.server.getHandlers()) { - handleDeferredInitialize(handler); - } Connector[] connectors = this.server.getConnectors(); for (Connector connector : connectors) { try { diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-jetty/build.gradle b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-jetty/build.gradle new file mode 100644 index 000000000000..5595f4b4f2c8 --- /dev/null +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-jetty/build.gradle @@ -0,0 +1,22 @@ +plugins { + id "java" +} + +description = "Spring Boot Armeria Jetty smoke test" + +ext { + armeriaVersion = "1.32.0" +} + +dependencies { + implementation("com.linecorp.armeria:armeria:$armeriaVersion") + implementation("com.linecorp.armeria:armeria-spring-boot3-autoconfigure:$armeriaVersion") + implementation("com.linecorp.armeria:armeria-jetty12:$armeriaVersion") + + implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-web")) { + exclude module: "spring-boot-starter-tomcat" + } + implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-jetty")) + + testImplementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-test")) +} diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-jetty/src/main/java/smoketest/armeria/jetty/ArmeriaConfiguration.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-jetty/src/main/java/smoketest/armeria/jetty/ArmeriaConfiguration.java new file mode 100644 index 000000000000..99e85c0462b0 --- /dev/null +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-jetty/src/main/java/smoketest/armeria/jetty/ArmeriaConfiguration.java @@ -0,0 +1,78 @@ +/* + * Copyright 2025 the original author or authors. + * + * 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 + * + * https://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 smoketest.armeria.jetty; + +import com.linecorp.armeria.common.HttpResponse; +import com.linecorp.armeria.server.ServerBuilder; +import com.linecorp.armeria.server.healthcheck.HealthChecker; +import com.linecorp.armeria.server.jetty.JettyService; +import com.linecorp.armeria.spring.ArmeriaServerConfigurator; +import org.eclipse.jetty.server.Server; + +import org.springframework.boot.web.embedded.jetty.JettyWebServer; +import org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Configures an Armeria server to redirect the incoming requests to the Jetty instance + * provided by Spring Boot. It also sets up a {@link HealthChecker} so that it works well + * with a load balancer. + */ +@Configuration +public class ArmeriaConfiguration { + + /** + * Returns a new {@link HealthChecker} that marks the server as unhealthy when Tomcat + * becomes unavailable. + */ + @Bean + public HealthChecker jettyHealthChecker(ServletWebServerApplicationContext applicationContext) { + final Server server = jettyServer(applicationContext).getServer(); + return server::isRunning; + } + + /** + * Returns a new {@link JettyService} that redirects the incoming requests to the + * Jetty instance provided by Spring Boot. + */ + @Bean + public JettyService jettyService(ServletWebServerApplicationContext applicationContext) { + final JettyWebServer jettyWebServer = jettyServer(applicationContext); + return JettyService.of(jettyWebServer.getServer(), null); + } + + /** + * Returns a new {@link ArmeriaServerConfigurator} that is responsible for configuring + * a {@link Server} using the given {@link ServerBuilder}. + */ + @Bean + public ArmeriaServerConfigurator armeriaServiceInitializer(JettyService jettyService) { + return sb -> sb.serviceUnder("/jetty", jettyService.decorate((delegate, ctx, req) -> { + ctx.addAdditionalResponseHeader("armeria-forwarded", "true"); + return delegate.serve(ctx, req); + })).serviceUnder("/armeria", (ctx, req) -> HttpResponse.of("Hello from Armeria!")); + } + + /** + * Extracts a Jetty {@link Server} from Spring webapp context. + */ + private static JettyWebServer jettyServer(ServletWebServerApplicationContext applicationContext) { + return (JettyWebServer) applicationContext.getWebServer(); + } + +} diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-jetty/src/main/java/smoketest/armeria/jetty/SampleArmeriaJettyApplication.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-jetty/src/main/java/smoketest/armeria/jetty/SampleArmeriaJettyApplication.java new file mode 100644 index 000000000000..1397efb88a09 --- /dev/null +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-jetty/src/main/java/smoketest/armeria/jetty/SampleArmeriaJettyApplication.java @@ -0,0 +1,29 @@ +/* + * Copyright 2025 the original author or authors. + * + * 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 + * + * https://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 smoketest.armeria.jetty; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SampleArmeriaJettyApplication { + + public static void main(String[] args) { + SpringApplication.run(SampleArmeriaJettyApplication.class, args); + } + +} diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-jetty/src/main/java/smoketest/armeria/jetty/service/HelloWorldService.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-jetty/src/main/java/smoketest/armeria/jetty/service/HelloWorldService.java new file mode 100644 index 000000000000..280d762365c9 --- /dev/null +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-jetty/src/main/java/smoketest/armeria/jetty/service/HelloWorldService.java @@ -0,0 +1,32 @@ +/* + * Copyright 2025 the original author or authors. + * + * 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 + * + * https://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 smoketest.armeria.jetty.service; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component +public class HelloWorldService { + + @Value("${test.name:World}") + private String name; + + public String getHelloMessage() { + return "Hello " + this.name; + } + +} diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-jetty/src/main/java/smoketest/armeria/jetty/web/SampleController.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-jetty/src/main/java/smoketest/armeria/jetty/web/SampleController.java new file mode 100644 index 000000000000..15d55ba07181 --- /dev/null +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-jetty/src/main/java/smoketest/armeria/jetty/web/SampleController.java @@ -0,0 +1,40 @@ +/* + * Copyright 2025 the original author or authors. + * + * 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 + * + * https://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 smoketest.armeria.jetty.web; + +import smoketest.armeria.jetty.service.HelloWorldService; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +public class SampleController { + + private final HelloWorldService helloWorldService; + + public SampleController(HelloWorldService helloWorldService) { + this.helloWorldService = helloWorldService; + } + + @GetMapping("/") + @ResponseBody + public String helloWorld() { + return this.helloWorldService.getHelloMessage(); + } + +} diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-jetty/src/main/resources/config/application.yml b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-jetty/src/main/resources/config/application.yml new file mode 100644 index 000000000000..2c943d7bf259 --- /dev/null +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-jetty/src/main/resources/config/application.yml @@ -0,0 +1,7 @@ +# Prevent the embedded Jetty from opening a TCP/IP port. +server.port: -1 +--- +armeria: + ports: + - port: 0 + protocols: HTTP diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-jetty/src/test/java/smoketest/armeria/jetty/SampleArmeriaJettyApplicationTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-jetty/src/test/java/smoketest/armeria/jetty/SampleArmeriaJettyApplicationTests.java new file mode 100644 index 000000000000..277f2a1723f7 --- /dev/null +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-jetty/src/test/java/smoketest/armeria/jetty/SampleArmeriaJettyApplicationTests.java @@ -0,0 +1,68 @@ +/* + * Copyright 2025 the original author or authors. + * + * 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 + * + * https://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 smoketest.armeria.jetty; + +import com.linecorp.armeria.server.Server; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.util.DefaultUriBuilderFactory; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Basic integration tests for demo application. + * + * @author Dogac Eldenk + */ +@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT) +class SampleArmeriaJettyApplicationTests { + + @Autowired + private Server server; + + @Autowired + private TestRestTemplate restTemplate; + + @BeforeEach + void setup() { + DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory("http://127.0.0.1:" + server.activeLocalPort()); + this.restTemplate.getRestTemplate().setUriTemplateHandler(factory); + } + + @Test + void testJetty() { + ResponseEntity entity = this.restTemplate.getForEntity("/jetty", String.class); + assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(entity.getBody()).isEqualTo("Hello World"); + assertThat(entity.getHeaders().getFirst("armeria-forwarded")).isEqualTo("true"); + } + + @Test + void testArmeria() { + ResponseEntity entity = this.restTemplate.getForEntity("/armeria", String.class); + assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(entity.getBody()).isEqualTo("Hello from Armeria!"); + } + +} diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-tomcat/build.gradle b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-tomcat/build.gradle new file mode 100644 index 000000000000..0665451e4abf --- /dev/null +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-tomcat/build.gradle @@ -0,0 +1,21 @@ +plugins { + id "java" +} + +description = "Spring Boot Armeria Tomcat smoke test" + +ext { + armeriaVersion = "1.32.0" +} + +dependencies { + implementation("com.linecorp.armeria:armeria:$armeriaVersion") + implementation("com.linecorp.armeria:armeria-spring-boot3-autoconfigure:$armeriaVersion") + implementation("com.linecorp.armeria:armeria-tomcat10:$armeriaVersion") + + implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter")) + implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-tomcat")) + implementation("org.springframework:spring-webmvc") + + testImplementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-test")) +} diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-tomcat/src/main/java/smoketest/armeria/tomcat/ArmeriaConfiguration.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-tomcat/src/main/java/smoketest/armeria/tomcat/ArmeriaConfiguration.java new file mode 100644 index 000000000000..2f575e9ec38e --- /dev/null +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-tomcat/src/main/java/smoketest/armeria/tomcat/ArmeriaConfiguration.java @@ -0,0 +1,84 @@ +/* + * Copyright 2025 the original author or authors. + * + * 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 + * + * https://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 smoketest.armeria.tomcat; + +import com.linecorp.armeria.common.HttpResponse; +import com.linecorp.armeria.server.Server; +import com.linecorp.armeria.server.ServerBuilder; +import com.linecorp.armeria.server.healthcheck.HealthChecker; +import com.linecorp.armeria.server.tomcat.TomcatService; +import com.linecorp.armeria.spring.ArmeriaServerConfigurator; +import org.apache.catalina.connector.Connector; + +import org.springframework.boot.web.embedded.tomcat.TomcatWebServer; +import org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Configures an Armeria {@link Server} to redirect the incoming requests to the Tomcat + * instance provided by Spring Boot. It also sets up a {@link HealthChecker} so that it + * works well with a load balancer. + */ +@Configuration +public class ArmeriaConfiguration { + + /** + * Extracts a Tomcat {@link Connector} from Spring webapp context. + */ + public static Connector getConnector(ServletWebServerApplicationContext applicationContext) { + final TomcatWebServer container = (TomcatWebServer) applicationContext.getWebServer(); + + // Start the container to make sure all connectors are available. + container.start(); + return container.getTomcat().getConnector(); + } + + /** + * Returns a new {@link HealthChecker} that marks the server as unhealthy when Tomcat + * becomes unavailable. + */ + @Bean + public HealthChecker tomcatConnectorHealthChecker(ServletWebServerApplicationContext applicationContext) { + final Connector connector = getConnector(applicationContext); + return () -> connector.getState().isAvailable(); + } + + /** + * Returns a new {@link TomcatService} that redirects the incoming requests to the + * Tomcat instance provided by Spring Boot. + */ + @Bean + public TomcatService tomcatService(ServletWebServerApplicationContext applicationContext) { + return TomcatService.of(getConnector(applicationContext)); + } + + /** + * Returns a new {@link ArmeriaServerConfigurator} that is responsible for configuring + * a {@link Server} using the given {@link ServerBuilder}. + */ + @Bean + public ArmeriaServerConfigurator armeriaServiceInitializer(TomcatService tomcatService) { + + return sb -> sb.serviceUnder("/tomcat", tomcatService.decorate((delegate, ctx, req) -> { + ctx.addAdditionalResponseHeader("armeria-forwarded", "true"); + return delegate.serve(ctx, req); + })).serviceUnder("/armeria", (ctx, req) -> HttpResponse.of("Hello from Armeria!")); + + } + +} diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-tomcat/src/main/java/smoketest/armeria/tomcat/SampleArmeriaTomcatApplication.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-tomcat/src/main/java/smoketest/armeria/tomcat/SampleArmeriaTomcatApplication.java new file mode 100644 index 000000000000..de0b06d46624 --- /dev/null +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-tomcat/src/main/java/smoketest/armeria/tomcat/SampleArmeriaTomcatApplication.java @@ -0,0 +1,54 @@ +/* + * Copyright 2025 the original author or authors. + * + * 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 + * + * https://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 smoketest.armeria.tomcat; + +import jakarta.servlet.ServletContextEvent; +import jakarta.servlet.ServletContextListener; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; + +@SpringBootApplication +public class SampleArmeriaTomcatApplication { + + private static final Log logger = LogFactory.getLog(SampleArmeriaTomcatApplication.class); + + @Bean + protected ServletContextListener listener() { + return new ServletContextListener() { + + @Override + public void contextInitialized(ServletContextEvent sce) { + logger.info("ServletContext initialized"); + } + + @Override + public void contextDestroyed(ServletContextEvent sce) { + logger.info("ServletContext destroyed"); + } + + }; + } + + public static void main(String[] args) { + SpringApplication.run(SampleArmeriaTomcatApplication.class, args); + } + +} diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-tomcat/src/main/java/smoketest/armeria/tomcat/service/HelloWorldService.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-tomcat/src/main/java/smoketest/armeria/tomcat/service/HelloWorldService.java new file mode 100644 index 000000000000..35dbf9d63287 --- /dev/null +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-tomcat/src/main/java/smoketest/armeria/tomcat/service/HelloWorldService.java @@ -0,0 +1,32 @@ +/* + * Copyright 2025 the original author or authors. + * + * 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 + * + * https://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 smoketest.armeria.tomcat.service; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component +public class HelloWorldService { + + @Value("${test.name:World}") + private String name; + + public String getHelloMessage() { + return "Hello " + this.name; + } + +} diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-tomcat/src/main/java/smoketest/armeria/tomcat/web/SampleController.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-tomcat/src/main/java/smoketest/armeria/tomcat/web/SampleController.java new file mode 100644 index 000000000000..9f9e9b9cc918 --- /dev/null +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-tomcat/src/main/java/smoketest/armeria/tomcat/web/SampleController.java @@ -0,0 +1,40 @@ +/* + * Copyright 2025 the original author or authors. + * + * 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 + * + * https://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 smoketest.armeria.tomcat.web; + +import smoketest.armeria.tomcat.service.HelloWorldService; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +public class SampleController { + + private final HelloWorldService helloWorldService; + + public SampleController(HelloWorldService helloWorldService) { + this.helloWorldService = helloWorldService; + } + + @GetMapping("/") + @ResponseBody + public String helloWorld() { + return this.helloWorldService.getHelloMessage(); + } + +} diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-tomcat/src/main/resources/config/application.yml b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-tomcat/src/main/resources/config/application.yml new file mode 100644 index 000000000000..b1c8f5f29eca --- /dev/null +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-tomcat/src/main/resources/config/application.yml @@ -0,0 +1,7 @@ +# Prevent the embedded Tomcat from opening a TCP/IP port. +server.port: -1 +--- +armeria: + ports: + - port: 0 + protocols: HTTP diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-tomcat/src/test/java/smoketest/armeria/tomcat/SampleArmeriaTomcatApplicationTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-tomcat/src/test/java/smoketest/armeria/tomcat/SampleArmeriaTomcatApplicationTests.java new file mode 100644 index 000000000000..5a61e700c103 --- /dev/null +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-tomcat/src/test/java/smoketest/armeria/tomcat/SampleArmeriaTomcatApplicationTests.java @@ -0,0 +1,71 @@ +/* + * Copyright 2012-2025 the original author or authors. + * + * 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 + * + * https://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 smoketest.armeria.tomcat; + +import com.linecorp.armeria.server.Server; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.test.system.OutputCaptureExtension; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.util.DefaultUriBuilderFactory; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Basic integration tests for demo application. + * + * @author Dogac Eldenk + */ +@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT) +@ExtendWith(OutputCaptureExtension.class) +class SampleArmeriaTomcatApplicationTests { + + @Autowired + private Server server; + + @Autowired + private TestRestTemplate restTemplate; + + @BeforeEach + void setup() { + DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory("http://127.0.0.1:" + server.activeLocalPort()); + this.restTemplate.getRestTemplate().setUriTemplateHandler(factory); + } + + @Test + void testTomcat() { + ResponseEntity entity = this.restTemplate.getForEntity("/tomcat", String.class); + assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(entity.getBody()).isEqualTo("Hello World"); + assertThat(entity.getHeaders().getFirst("armeria-forwarded")).isEqualTo("true"); + } + + @Test + void testArmeria() { + ResponseEntity entity = this.restTemplate.getForEntity("/armeria", String.class); + assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(entity.getBody()).isEqualTo("Hello from Armeria!"); + } + +} From be170fccd11758299fd1437c1a4e333b9465ac58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Do=C4=9Fa=C3=A7=20Eldenk?= Date: Sun, 9 Mar 2025 10:51:06 -0500 Subject: [PATCH 3/3] add armeria BOM to libraries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Doğaç Eldenk --- .../spring-boot-dependencies/build.gradle | 11 +++++++++++ .../spring-boot-smoke-test-armeria-jetty/build.gradle | 10 +++------- .../smoketest/armeria/jetty/ArmeriaConfiguration.java | 2 ++ .../build.gradle | 10 +++------- .../armeria/tomcat/ArmeriaConfiguration.java | 2 ++ 5 files changed, 21 insertions(+), 14 deletions(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index f9ca458b3c20..a788c9ad7d25 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -53,6 +53,17 @@ bom { releaseNotes("https://github.com/eclipse-ee4j/angus-mail/releases/tag/{version}") } } + library("Armeria", "1.32.0") { + group("com.linecorp.armeria") { + imports = [ + "armeria-bom" + ] + } + links { + site("https://armeria.dev") + releaseNotes("https://armeria.dev/release-notes/{version}") + } + } library("Artemis", "2.39.0") { group("org.apache.activemq") { imports = [ diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-jetty/build.gradle b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-jetty/build.gradle index 5595f4b4f2c8..94a72c587f50 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-jetty/build.gradle +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-jetty/build.gradle @@ -4,14 +4,10 @@ plugins { description = "Spring Boot Armeria Jetty smoke test" -ext { - armeriaVersion = "1.32.0" -} - dependencies { - implementation("com.linecorp.armeria:armeria:$armeriaVersion") - implementation("com.linecorp.armeria:armeria-spring-boot3-autoconfigure:$armeriaVersion") - implementation("com.linecorp.armeria:armeria-jetty12:$armeriaVersion") + implementation("com.linecorp.armeria:armeria") + implementation("com.linecorp.armeria:armeria-spring-boot3-autoconfigure") + implementation("com.linecorp.armeria:armeria-jetty12") implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-web")) { exclude module: "spring-boot-starter-tomcat" diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-jetty/src/main/java/smoketest/armeria/jetty/ArmeriaConfiguration.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-jetty/src/main/java/smoketest/armeria/jetty/ArmeriaConfiguration.java index 99e85c0462b0..72f472694c0a 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-jetty/src/main/java/smoketest/armeria/jetty/ArmeriaConfiguration.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-jetty/src/main/java/smoketest/armeria/jetty/ArmeriaConfiguration.java @@ -32,6 +32,8 @@ * Configures an Armeria server to redirect the incoming requests to the Jetty instance * provided by Spring Boot. It also sets up a {@link HealthChecker} so that it works well * with a load balancer. + * + * @author Dogac Eldenk */ @Configuration public class ArmeriaConfiguration { diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-tomcat/build.gradle b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-tomcat/build.gradle index 0665451e4abf..8c56fd1295df 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-tomcat/build.gradle +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-tomcat/build.gradle @@ -4,14 +4,10 @@ plugins { description = "Spring Boot Armeria Tomcat smoke test" -ext { - armeriaVersion = "1.32.0" -} - dependencies { - implementation("com.linecorp.armeria:armeria:$armeriaVersion") - implementation("com.linecorp.armeria:armeria-spring-boot3-autoconfigure:$armeriaVersion") - implementation("com.linecorp.armeria:armeria-tomcat10:$armeriaVersion") + implementation("com.linecorp.armeria:armeria") + implementation("com.linecorp.armeria:armeria-spring-boot3-autoconfigure") + implementation("com.linecorp.armeria:armeria-tomcat10") implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter")) implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-tomcat")) diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-tomcat/src/main/java/smoketest/armeria/tomcat/ArmeriaConfiguration.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-tomcat/src/main/java/smoketest/armeria/tomcat/ArmeriaConfiguration.java index 2f575e9ec38e..80defed9886c 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-tomcat/src/main/java/smoketest/armeria/tomcat/ArmeriaConfiguration.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-armeria-tomcat/src/main/java/smoketest/armeria/tomcat/ArmeriaConfiguration.java @@ -33,6 +33,8 @@ * Configures an Armeria {@link Server} to redirect the incoming requests to the Tomcat * instance provided by Spring Boot. It also sets up a {@link HealthChecker} so that it * works well with a load balancer. + * + * @author Dogac Eldenk */ @Configuration public class ArmeriaConfiguration {