diff --git a/aot-smoke-test-support/src/main/java/org/springframework/aot/smoketest/support/junit/ApplicationUnderTest.java b/aot-smoke-test-support/src/main/java/org/springframework/aot/smoketest/support/junit/ApplicationUnderTest.java index 216b8316..241c60b3 100644 --- a/aot-smoke-test-support/src/main/java/org/springframework/aot/smoketest/support/junit/ApplicationUnderTest.java +++ b/aot-smoke-test-support/src/main/java/org/springframework/aot/smoketest/support/junit/ApplicationUnderTest.java @@ -48,7 +48,8 @@ final class ApplicationUnderTest { } List portPatterns = List.of(Pattern.compile("Tomcat started on port\\(s\\): ([0-9]+)"), Pattern.compile("Netty started on port ([0-9]+)"), - Pattern.compile("Jetty started on port\\(s\\) ([0-9]+)")); + Pattern.compile("Jetty started on port\\(s\\) ([0-9]+)"), + Pattern.compile("Undertow started on port\\(s\\) ([0-9]+)")); List lines = Output.current().lines(); for (String line : lines) { for (Pattern portPattern : portPatterns) { diff --git a/aot-smoke-test-third-party-hints/src/main/java/org/springframework/aot/smoketest/thirdpartyhints/UndertowRuntimeHints.java b/aot-smoke-test-third-party-hints/src/main/java/org/springframework/aot/smoketest/thirdpartyhints/UndertowRuntimeHints.java new file mode 100644 index 00000000..2e27eba6 --- /dev/null +++ b/aot-smoke-test-third-party-hints/src/main/java/org/springframework/aot/smoketest/thirdpartyhints/UndertowRuntimeHints.java @@ -0,0 +1,61 @@ +/* + * Copyright 2022 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 org.springframework.aot.smoketest.thirdpartyhints; + +import org.springframework.aot.hint.MemberCategory; +import org.springframework.aot.hint.RuntimeHints; +import org.springframework.aot.hint.RuntimeHintsRegistrar; +import org.springframework.aot.hint.TypeReference; + +/** + * A {@link RuntimeHintsRegistrar} for Undertow. + * + * @author Andy Wilkinson + */ +public class UndertowRuntimeHints implements RuntimeHintsRegistrar { + + @Override + public void registerHints(RuntimeHints hints, ClassLoader classLoader) { + invokePublicConstructorsOf(hints, "io.undertow.UndertowLogger_$logger", + "io.undertow.server.protocol.http.HttpRequestParser$$generated", + "io.undertow.servlet.UndertowServletLogger_$logger", "io.undertow.servlet.handlers.DefaultServlet", + "io.undertow.util.FastConcurrentDirectDeque", "io.undertow.util.PortableConcurrentDirectDeque", + "io.undertow.websockets.core.WebSocketLogger_$logger", "io.undertow.websockets.jsr.JsrWebSocketFilter", + "io.undertow.websockets.jsr.JsrWebSocketFilter$LogoutListener", + "io.undertow.websockets.jsr.JsrWebSocketLogger_$logger", "org.xnio._private.Messages_$logger", + "org.xnio.nio.Log_$logger"); + registerMessages(hints, "io.undertow.UndertowMessages_$bundle", + "io.undertow.servlet.UndertowServletMessages_$bundle", + "io.undertow.websockets.core.WebSocketMessages_$bundle", + "io.undertow.websockets.jsr.JsrWebSocketMessages_$bundle"); + hints.reflection().registerType(TypeReference.of("io.undertow.websockets.jsr.Bootstrap$WebSocketListener"), + MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); + } + + private void invokePublicConstructorsOf(RuntimeHints hints, String... typeNames) { + for (String typeName : typeNames) { + hints.reflection().registerType(TypeReference.of(typeName), MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS); + } + } + + private void registerMessages(RuntimeHints hints, String... names) { + for (String name : names) { + hints.reflection().registerType(TypeReference.of(name), (hint) -> hint.withField("INSTANCE")); + } + } + +} diff --git a/ci/smoke-tests.yml b/ci/smoke-tests.yml index c1c7f156..c6277f66 100644 --- a/ci/smoke-tests.yml +++ b/ci/smoke-tests.yml @@ -21,6 +21,7 @@ groups: - mustache-webmvc - servlet-jetty - servlet-tomcat + - servlet-undertow - thymeleaf-webmvc - thymeleaf-webflux - tracing-brave-zipkin diff --git a/servlet-undertow/README.adoc b/servlet-undertow/README.adoc new file mode 100644 index 00000000..fbedcb62 --- /dev/null +++ b/servlet-undertow/README.adoc @@ -0,0 +1 @@ +Tests if Servlets with Undertow works diff --git a/servlet-undertow/build.gradle b/servlet-undertow/build.gradle new file mode 100644 index 00000000..1c3f5766 --- /dev/null +++ b/servlet-undertow/build.gradle @@ -0,0 +1,27 @@ +plugins { + id 'java' + id 'org.springframework.boot' + id 'org.springframework.aot.smoke-test' + id 'org.graalvm.buildtools.native' +} + +dependencies { + implementation(platform(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES)) + implementation(project(":aot-smoke-test-third-party-hints")) + implementation("org.springframework.boot:spring-boot-starter-undertow") + implementation("org.springframework.boot:spring-boot-starter-web") + modules { + module("org.springframework.boot:spring-boot-starter-tomcat") { + replacedBy("org.springframework.boot:spring-boot-starter-undertow", "Use Undertow instead of Tomcat") + } + } + + testImplementation("org.springframework.boot:spring-boot-starter-test") + + aotSmokeTestImplementation(project(":aot-smoke-test-support")) + aotSmokeTestImplementation("org.awaitility:awaitility:4.2.0") +} + +aotSmokeTest { + webApplication = true +} diff --git a/servlet-undertow/src/aotSmokeTest/java/com/example/servlet/undertow/ServletUndertowApplicationAotTests.java b/servlet-undertow/src/aotSmokeTest/java/com/example/servlet/undertow/ServletUndertowApplicationAotTests.java new file mode 100644 index 00000000..c61581f6 --- /dev/null +++ b/servlet-undertow/src/aotSmokeTest/java/com/example/servlet/undertow/ServletUndertowApplicationAotTests.java @@ -0,0 +1,19 @@ +package com.example.servlet.undertow; + +import org.junit.jupiter.api.Test; + +import org.springframework.aot.smoketest.support.junit.AotSmokeTest; +import org.springframework.test.web.reactive.server.WebTestClient; + +import static org.assertj.core.api.Assertions.assertThat; + +@AotSmokeTest +class ServletUndertowApplicationAotTests { + + @Test + void servletIsInvokable(WebTestClient client) { + client.get().uri("/?name=Servlet").exchange().expectStatus().isOk().expectBody().consumeWith( + (result) -> assertThat(new String(result.getResponseBodyContent())).isEqualTo("Hello Servlet")); + } + +} diff --git a/servlet-undertow/src/main/java/com/example/servlet/undertow/ServletConfiguration.java b/servlet-undertow/src/main/java/com/example/servlet/undertow/ServletConfiguration.java new file mode 100644 index 00000000..61410464 --- /dev/null +++ b/servlet-undertow/src/main/java/com/example/servlet/undertow/ServletConfiguration.java @@ -0,0 +1,15 @@ +package com.example.servlet.undertow; + +import org.springframework.boot.web.servlet.ServletRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +class ServletConfiguration { + + @Bean + ServletRegistrationBean testServletServletRegistrationBean() { + return new ServletRegistrationBean<>(new TestServlet(), "/*"); + } + +} diff --git a/servlet-undertow/src/main/java/com/example/servlet/undertow/ServletUndertowApplication.java b/servlet-undertow/src/main/java/com/example/servlet/undertow/ServletUndertowApplication.java new file mode 100644 index 00000000..39d96dd5 --- /dev/null +++ b/servlet-undertow/src/main/java/com/example/servlet/undertow/ServletUndertowApplication.java @@ -0,0 +1,16 @@ +package com.example.servlet.undertow; + +import org.springframework.aot.smoketest.thirdpartyhints.UndertowRuntimeHints; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ImportRuntimeHints; + +@SpringBootApplication +@ImportRuntimeHints(UndertowRuntimeHints.class) +public class ServletUndertowApplication { + + public static void main(String[] args) throws InterruptedException { + SpringApplication.run(ServletUndertowApplication.class, args); + } + +} diff --git a/servlet-undertow/src/main/java/com/example/servlet/undertow/TestServlet.java b/servlet-undertow/src/main/java/com/example/servlet/undertow/TestServlet.java new file mode 100644 index 00000000..185759c6 --- /dev/null +++ b/servlet-undertow/src/main/java/com/example/servlet/undertow/TestServlet.java @@ -0,0 +1,24 @@ +package com.example.servlet.undertow; + +import java.io.IOException; + +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +class TestServlet extends HttpServlet { + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { + String[] names = req.getParameterValues("name"); + String name; + if (names == null || names.length == 0) { + name = "world"; + } + else { + name = names[0]; + } + resp.getWriter().printf("Hello %s", name); + } + +} diff --git a/servlet-undertow/src/test/java/com/example/servlet/undertow/ServletUndertowApplicationTests.java b/servlet-undertow/src/test/java/com/example/servlet/undertow/ServletUndertowApplicationTests.java new file mode 100644 index 00000000..7aa9e00d --- /dev/null +++ b/servlet-undertow/src/test/java/com/example/servlet/undertow/ServletUndertowApplicationTests.java @@ -0,0 +1,14 @@ +package com.example.servlet.undertow; + +import org.junit.jupiter.api.Test; + +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class ServletUndertowApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/settings.gradle b/settings.gradle index 490ec956..856d6043 100644 --- a/settings.gradle +++ b/settings.gradle @@ -94,6 +94,7 @@ include "security-oauth2-authorization-server" include "security-oauth2-resource-server" include "servlet-jetty" include "servlet-tomcat" +include "servlet-undertow" include "session-jdbc" include "session-redis-webflux" include "spring-amqp-rabbit"