diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowWebServer.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowWebServer.java index a75fd71378..5fc9b1d2d7 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowWebServer.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowWebServer.java @@ -34,6 +34,8 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.xnio.channels.BoundChannel; +import org.springframework.aot.hint.RuntimeHints; +import org.springframework.aot.hint.RuntimeHintsRegistrar; import org.springframework.boot.web.server.GracefulShutdownCallback; import org.springframework.boot.web.server.GracefulShutdownResult; import org.springframework.boot.web.server.PortInUseException; @@ -401,4 +403,22 @@ public class UndertowWebServer implements WebServer { } + /** + * {@link RuntimeHintsRegistrar} that allows Undertow's configured and actual ports to + * be retrieved at runtime in a native image. + */ + static class UndertowWebServerRuntimeHints implements RuntimeHintsRegistrar { + + @Override + public void registerHints(RuntimeHints hints, ClassLoader classLoader) { + hints.reflection().registerTypeIfPresent(classLoader, "io.undertow.Undertow", + (hint) -> hint.withField("listeners").withField("channels")); + hints.reflection().registerTypeIfPresent(classLoader, "io.undertow.Undertow$ListenerConfig", + (hint) -> hint.withField("type").withField("port")); + hints.reflection().registerTypeIfPresent(classLoader, + "io.undertow.protocols.ssl.UndertowAcceptingSslChannel", (hint) -> hint.withField("ssl")); + } + + } + } diff --git a/spring-boot-project/spring-boot/src/main/resources/META-INF/spring/aot.factories b/spring-boot-project/spring-boot/src/main/resources/META-INF/spring/aot.factories index 557835873c..d60b07ea90 100644 --- a/spring-boot-project/spring-boot/src/main/resources/META-INF/spring/aot.factories +++ b/spring-boot-project/spring-boot/src/main/resources/META-INF/spring/aot.factories @@ -6,6 +6,7 @@ org.springframework.boot.env.PropertySourceRuntimeHints,\ org.springframework.boot.json.JacksonRuntimeHints,\ org.springframework.boot.logging.java.JavaLoggingSystemRuntimeHints,\ org.springframework.boot.logging.logback.LogbackRuntimeHints,\ +org.springframework.boot.web.embedded.undertow.UndertowWebServer.UndertowWebServerRuntimeHints,\ org.springframework.boot.web.server.MimeMappings.MimeMappingsRuntimeHints org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor=\ diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/undertow/UndertowWebServerRuntimeHintsTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/undertow/UndertowWebServerRuntimeHintsTests.java new file mode 100644 index 0000000000..f48502fcf0 --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/undertow/UndertowWebServerRuntimeHintsTests.java @@ -0,0 +1,54 @@ +/* + * Copyright 2012-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.boot.web.embedded.undertow; + +import io.undertow.Undertow; +import org.junit.jupiter.api.Test; + +import org.springframework.aot.hint.RuntimeHints; +import org.springframework.aot.hint.predicate.ReflectionHintsPredicates.FieldHintPredicate; +import org.springframework.aot.hint.predicate.RuntimeHintsPredicates; +import org.springframework.boot.web.embedded.undertow.UndertowWebServer.UndertowWebServerRuntimeHints; +import org.springframework.util.ReflectionUtils; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link UndertowWebServerRuntimeHints}. + * + * @author Andy Wilkinson + */ +public class UndertowWebServerRuntimeHintsTests { + + @Test + void registersHints() throws ClassNotFoundException { + RuntimeHints runtimeHints = new RuntimeHints(); + new UndertowWebServerRuntimeHints().registerHints(runtimeHints, getClass().getClassLoader()); + assertThat(RuntimeHintsPredicates.reflection().onField(Undertow.class, "listeners")).accepts(runtimeHints); + assertThat(RuntimeHintsPredicates.reflection().onField(Undertow.class, "channels")).accepts(runtimeHints); + assertThat(reflectionOnField("io.undertow.Undertow$ListenerConfig", "type")).accepts(runtimeHints); + assertThat(reflectionOnField("io.undertow.Undertow$ListenerConfig", "port")).accepts(runtimeHints); + assertThat(reflectionOnField("io.undertow.protocols.ssl.UndertowAcceptingSslChannel", "ssl")) + .accepts(runtimeHints); + } + + private FieldHintPredicate reflectionOnField(String className, String fieldName) throws ClassNotFoundException { + return RuntimeHintsPredicates.reflection() + .onField(ReflectionUtils.findField(Class.forName(className), fieldName)); + } + +}