From f6f5e9e8da893b77f8c10aa5cf3d34e4a0508e3b Mon Sep 17 00:00:00 2001 From: Dmytro Nosan Date: Thu, 30 Jan 2025 01:26:05 +0200 Subject: [PATCH] Update SpringBootJoranConfigurator to use proper GraalVM format Before this commit, the generated name for the inner class had a wrong format .. (canonical name). GraalVM expects $ to separate the parent from the inner class. This commit updates SpringBootJoranConfigurator to generate an appropriate format for a class name. Specifically, an inner class should be separated by a dollar sign, not a dot. See gh-44016 Signed-off-by: Dmytro Nosan --- .../logback/SpringBootJoranConfigurator.java | 15 +++-- ...backConfigurationAotContributionTests.java | 60 ++++++++++++++++++- 2 files changed, 65 insertions(+), 10 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/SpringBootJoranConfigurator.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/SpringBootJoranConfigurator.java index 2945e2bc7edc..24cd20fa827f 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/SpringBootJoranConfigurator.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/SpringBootJoranConfigurator.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2024 the original author or authors. + * 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. @@ -231,12 +231,12 @@ private Set> serializationTypes(Model model) { return modelClasses; } - private Set reflectionTypes(Model model) { + private Set> reflectionTypes(Model model) { return reflectionTypes(model, () -> null); } - private Set reflectionTypes(Model model, Supplier parent) { - Set reflectionTypes = new HashSet<>(); + private Set> reflectionTypes(Model model, Supplier parent) { + Set> reflectionTypes = new HashSet<>(); Class componentType = determineType(model, parent); if (componentType != null) { processComponent(componentType, reflectionTypes); @@ -306,15 +306,15 @@ private Object instantiate(Class type) { } } - private void processComponent(Class componentType, Set reflectionTypes) { + private void processComponent(Class componentType, Set> reflectionTypes) { BeanDescription beanDescription = this.modelInterpretationContext.getBeanDescriptionCache() .getBeanDescription(componentType); reflectionTypes.addAll(parameterTypesNames(beanDescription.getPropertyNameToAdder().values())); reflectionTypes.addAll(parameterTypesNames(beanDescription.getPropertyNameToSetter().values())); - reflectionTypes.add(componentType.getCanonicalName()); + reflectionTypes.add(componentType); } - private Collection parameterTypesNames(Collection methods) { + private Collection> parameterTypesNames(Collection methods) { return methods.stream() .filter((method) -> !method.getDeclaringClass().equals(ContextAware.class) && !method.getDeclaringClass().equals(ContextAwareBase.class)) @@ -322,7 +322,6 @@ private Collection parameterTypesNames(Collection methods) { .flatMap(Stream::of) .filter((type) -> !type.isPrimitive() && !type.equals(String.class)) .map((type) -> type.isArray() ? type.getComponentType() : type) - .map(Class::getName) .toList(); } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackConfigurationAotContributionTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackConfigurationAotContributionTests.java index 7d2e4944cfc2..d710fa221e1f 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackConfigurationAotContributionTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackConfigurationAotContributionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2024 the original author or authors. + * 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. @@ -23,6 +23,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Properties; import java.util.function.Consumer; import java.util.function.Predicate; @@ -54,6 +55,7 @@ import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.SerializationHints; +import org.springframework.aot.hint.TypeHint; import org.springframework.aot.hint.TypeReference; import org.springframework.aot.hint.predicate.RuntimeHintsPredicates; import org.springframework.aot.test.generate.TestGenerationContext; @@ -158,6 +160,11 @@ void componentModelClassAndSetterParametersAreRegisteredForReflection() { assertThat(invokePublicConstructorsAndInspectAndInvokePublicMethodsOf( TimeBasedFileNamingAndTriggeringPolicy.class)) .accepts(generationContext.getRuntimeHints()); + + assertThat(hasValidTypeName(SizeAndTimeBasedRollingPolicy.class)).accepts(generationContext.getRuntimeHints()); + assertThat(hasValidTypeName(FileSize.class)).accepts(generationContext.getRuntimeHints()); + assertThat(hasValidTypeName(FileAppender.class)).accepts(generationContext.getRuntimeHints()); + } @Test @@ -173,6 +180,10 @@ void implicitModelClassAndSetterParametersAreRegisteredForReflection() { .accepts(generationContext.getRuntimeHints()); assertThat(invokePublicConstructorsAndInspectAndInvokePublicMethodsOf(Charset.class)) .accepts(generationContext.getRuntimeHints()); + + assertThat(hasValidTypeName(PatternLayoutEncoder.class)).accepts(generationContext.getRuntimeHints()); + assertThat(hasValidTypeName(Layout.class)).accepts(generationContext.getRuntimeHints()); + assertThat(hasValidTypeName(Charset.class)).accepts(generationContext.getRuntimeHints()); } @Test @@ -186,6 +197,8 @@ void componentModelReferencingImportedClassNameIsRegisteredForReflection() { TestGenerationContext generationContext = applyContribution(model); assertThat(invokePublicConstructorsAndInspectAndInvokePublicMethodsOf(SizeAndTimeBasedRollingPolicy.class)) .accepts(generationContext.getRuntimeHints()); + + assertThat(hasValidTypeName(SizeAndTimeBasedRollingPolicy.class)).accepts(generationContext.getRuntimeHints()); } @Test @@ -200,6 +213,10 @@ void typeFromParentsSetterIsRegisteredForReflection() { .accepts(generationContext.getRuntimeHints()); assertThat(invokePublicConstructorsAndInspectAndInvokePublicMethodsOf(Implementation.class)) .accepts(generationContext.getRuntimeHints()); + + assertThat(hasValidTypeName(Outer.class)).accepts(generationContext.getRuntimeHints()); + assertThat(hasValidTypeName(Implementation.class)).accepts(generationContext.getRuntimeHints()); + } @Test @@ -214,6 +231,13 @@ void typeFromParentsDefaultClassAnnotatedSetterIsRegisteredForReflection() { .accepts(generationContext.getRuntimeHints()); assertThat(invokePublicConstructorsAndInspectAndInvokePublicMethodsOf(Implementation.class)) .accepts(generationContext.getRuntimeHints()); + assertThat(invokePublicConstructorsAndInspectAndInvokePublicMethodsOf(BaseImplementation.Details.class)) + .accepts(generationContext.getRuntimeHints()); + + assertThat(hasValidTypeName(OuterWithDefaultClass.class)).accepts(generationContext.getRuntimeHints()); + assertThat(hasValidTypeName(Implementation.class)).accepts(generationContext.getRuntimeHints()); + assertThat(hasValidTypeName(BaseImplementation.Details.class)).accepts(generationContext.getRuntimeHints()); + } @Test @@ -223,6 +247,10 @@ void componentTypesOfArraysAreRegisteredForReflection() { TestGenerationContext generationContext = applyContribution(component); assertThat(invokePublicConstructorsAndInspectAndInvokePublicMethodsOf(InetSocketAddress.class)) .accepts(generationContext.getRuntimeHints()); + + assertThat(hasValidTypeName(InetSocketAddress.class)).accepts(generationContext.getRuntimeHints()); + assertThat(hasValidTypeName(ArrayParameters.class)).accepts(generationContext.getRuntimeHints()); + } @Test @@ -235,6 +263,10 @@ void placeholdersInComponentClassAttributeAreReplaced() { .accepts(generationContext.getRuntimeHints()); assertThat(invokePublicConstructorsAndInspectAndInvokePublicMethodsOf(Implementation.class)) .accepts(generationContext.getRuntimeHints()); + + assertThat(hasValidTypeName(Outer.class)).accepts(generationContext.getRuntimeHints()); + assertThat(hasValidTypeName(Implementation.class)).accepts(generationContext.getRuntimeHints()); + } private Predicate invokePublicConstructorsOf(String name) { @@ -250,6 +282,16 @@ private Predicate invokePublicConstructorsAndInspectAndInvokePubli MemberCategory.INVOKE_PUBLIC_METHODS); } + private Predicate hasValidTypeName(Class type) { + return (runtimeHints) -> { + String name = Optional.ofNullable(runtimeHints.reflection().getTypeHint(type)) + .map(TypeHint::getType) + .map(TypeReference::getName) + .orElse(""); + return type.getTypeName().equals(name); + }; + } + private Properties load(InputStreamSource source) { try (InputStream inputStream = source.getInputStream()) { Properties properties = new Properties(); @@ -323,7 +365,21 @@ public void setContract(Contract contract) { } - public static class Implementation implements Contract { + public static class BaseImplementation implements Contract { + + private Details details; + + public void setDetails(Details details) { + this.details = details; + } + + public static final class Details { + + } + + } + + public static class Implementation extends BaseImplementation { }