Skip to content

Commit a7e103f

Browse files
mcollovaticlaudeZheSun88
authored
fix: add missing reflection hints for type used with ReflectTools (#23059) (CP: 24.9) (#23069)
* fix: register reflection and resource hints for error handlers in native builds (#23054) Extract and register exception types from HasErrorParameter<E> generic parameter for reflection. Register resource hints for ClassPathScanner to discover error handler classes at runtime in GraalVM native images. Fixes #23041 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: Zhe Sun <31067185+ZheSun88@users.noreply.github.com> * fix: add missing reflection hints for type used with ReflectTools (#23059) WebComponentExporter, I18NProvider, and MenuAccessControl might be instantiated via ReflectTools.createInstance and were missing reflection hints in VaadinBeanFactoryInitializationAotProcessor, which could cause issues at runtime in native executables. Fixes #23060 --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: Zhe Sun <31067185+ZheSun88@users.noreply.github.com>
1 parent 75f18d3 commit a7e103f

File tree

2 files changed

+118
-0
lines changed

2 files changed

+118
-0
lines changed

vaadin-spring/src/main/java/com/vaadin/flow/spring/springnative/VaadinBeanFactoryInitializationAotProcessor.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,17 @@
4747
import com.vaadin.flow.component.Component;
4848
import com.vaadin.flow.component.ComponentEvent;
4949
import com.vaadin.flow.component.UI;
50+
import com.vaadin.flow.component.WebComponentExporter;
5051
import com.vaadin.flow.component.page.AppShellConfigurator;
52+
import com.vaadin.flow.i18n.I18NProvider;
5153
import com.vaadin.flow.router.HasErrorParameter;
5254
import com.vaadin.flow.router.HasUrlParameter;
5355
import com.vaadin.flow.router.Layout;
5456
import com.vaadin.flow.router.Route;
5557
import com.vaadin.flow.router.RouteAlias;
5658
import com.vaadin.flow.router.RouterLayout;
5759
import com.vaadin.flow.server.PWA;
60+
import com.vaadin.flow.server.auth.MenuAccessControl;
5861

5962
public class VaadinBeanFactoryInitializationAotProcessor
6063
implements BeanFactoryInitializationAotProcessor {
@@ -121,6 +124,10 @@ public BeanFactoryInitializationAotContribution processAheadOfTime(
121124
registerSubTypes(hints, reflections, HasUrlParameter.class);
122125
registerSubTypes(hints, reflections,
123126
"com.vaadin.flow.data.converter.Converter");
127+
registerSubTypes(hints, reflections,
128+
WebComponentExporter.class);
129+
registerSubTypes(hints, reflections, I18NProvider.class);
130+
registerSubTypes(hints, reflections, MenuAccessControl.class);
124131
}
125132
};
126133
}

vaadin-spring/src/test/java/com/vaadin/flow/spring/springnative/VaadinBeanFactoryInitializationAotProcessorTest.java

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,10 @@
4242
import com.vaadin.flow.component.ComponentEvent;
4343
import com.vaadin.flow.component.Tag;
4444
import com.vaadin.flow.component.UI;
45+
import com.vaadin.flow.component.WebComponentExporter;
4546
import com.vaadin.flow.component.page.AppShellConfigurator;
47+
import com.vaadin.flow.component.webcomponent.WebComponent;
48+
import com.vaadin.flow.i18n.I18NProvider;
4649
import com.vaadin.flow.router.BeforeEnterEvent;
4750
import com.vaadin.flow.router.BeforeEvent;
4851
import com.vaadin.flow.router.ErrorParameter;
@@ -53,6 +56,7 @@
5356
import com.vaadin.flow.router.RouteAlias;
5457
import com.vaadin.flow.router.RouterLayout;
5558
import com.vaadin.flow.server.PWA;
59+
import com.vaadin.flow.server.auth.MenuAccessControl;
5660

5761
import static org.assertj.core.api.Assertions.assertThat;
5862
import static org.mockito.ArgumentMatchers.any;
@@ -621,6 +625,76 @@ void getSubtypesOf_excludesParentType() {
621625
.isNotEmpty().doesNotContain(RouterLayout.class);
622626
}
623627

628+
@Test
629+
void getSubtypesOf_findsWebComponentExporterSubclasses() {
630+
VaadinBeanFactoryInitializationAotProcessor processor = new VaadinBeanFactoryInitializationAotProcessor();
631+
632+
String packageName = getClass().getPackageName();
633+
Collection<Class<?>> exporters = processor.getSubtypesOf(
634+
new Reflections(packageName), WebComponentExporter.class);
635+
636+
assertThat(exporters).as("Should find WebComponentExporter subclasses")
637+
.contains(TestWebComponentExporter.class);
638+
}
639+
640+
@Test
641+
void getSubtypesOf_findsI18NProviderImplementations() {
642+
VaadinBeanFactoryInitializationAotProcessor processor = new VaadinBeanFactoryInitializationAotProcessor();
643+
644+
String packageName = getClass().getPackageName();
645+
Collection<Class<?>> providers = processor.getSubtypesOf(
646+
new Reflections(packageName), I18NProvider.class);
647+
648+
assertThat(providers).as("Should find I18NProvider implementations")
649+
.contains(TestI18NProvider.class);
650+
}
651+
652+
@Test
653+
void getSubtypesOf_findsMenuAccessControlImplementations() {
654+
VaadinBeanFactoryInitializationAotProcessor processor = new VaadinBeanFactoryInitializationAotProcessor();
655+
656+
String packageName = getClass().getPackageName();
657+
Collection<Class<?>> accessControls = processor.getSubtypesOf(
658+
new Reflections(packageName), MenuAccessControl.class);
659+
660+
assertThat(accessControls)
661+
.as("Should find MenuAccessControl implementations")
662+
.contains(TestMenuAccessControl.class);
663+
}
664+
665+
@Test
666+
void processAheadOfTime_webComponentExporterSubtype_reflectionHintRegistered() {
667+
RuntimeHints hints = processAotForHintsWithSubtypes(
668+
TestWebComponentExporter.class, WebComponentExporter.class);
669+
670+
assertThat(RuntimeHintsPredicates.reflection()
671+
.onType(TestWebComponentExporter.class))
672+
.as("WebComponentExporter subtype should be registered for reflection")
673+
.accepts(hints);
674+
}
675+
676+
@Test
677+
void processAheadOfTime_i18nProviderSubtype_reflectionHintRegistered() {
678+
RuntimeHints hints = processAotForHintsWithSubtypes(
679+
TestI18NProvider.class, I18NProvider.class);
680+
681+
assertThat(RuntimeHintsPredicates.reflection()
682+
.onType(TestI18NProvider.class))
683+
.as("I18NProvider subtype should be registered for reflection")
684+
.accepts(hints);
685+
}
686+
687+
@Test
688+
void processAheadOfTime_menuAccessControlSubtype_reflectionHintRegistered() {
689+
RuntimeHints hints = processAotForHintsWithSubtypes(
690+
TestMenuAccessControl.class, MenuAccessControl.class);
691+
692+
assertThat(RuntimeHintsPredicates.reflection()
693+
.onType(TestMenuAccessControl.class))
694+
.as("MenuAccessControl subtype should be registered for reflection")
695+
.accepts(hints);
696+
}
697+
624698
@Test
625699
void getAnnotatedClasses_findsClassesWithSpecificAnnotation() {
626700
VaadinBeanFactoryInitializationAotProcessor processor = new VaadinBeanFactoryInitializationAotProcessor();
@@ -903,4 +977,41 @@ public static class RouteWithUILayout extends Component {
903977
@Tag("div")
904978
public static class RouteWithDefaultLayout extends Component {
905979
}
980+
981+
public static class TestWebComponentExporter
982+
extends WebComponentExporter<Component> {
983+
public TestWebComponentExporter() {
984+
super("test-exporter");
985+
}
986+
987+
@Override
988+
protected void configureInstance(WebComponent<Component> webComponent,
989+
Component component) {
990+
}
991+
}
992+
993+
public static class TestI18NProvider implements I18NProvider {
994+
@Override
995+
public java.util.List<java.util.Locale> getProvidedLocales() {
996+
return java.util.List.of(java.util.Locale.ENGLISH);
997+
}
998+
999+
@Override
1000+
public String getTranslation(String key, java.util.Locale locale,
1001+
Object... params) {
1002+
return key;
1003+
}
1004+
}
1005+
1006+
public static class TestMenuAccessControl implements MenuAccessControl {
1007+
@Override
1008+
public void setPopulateClientSideMenu(
1009+
PopulateClientMenu populateClientMenu) {
1010+
}
1011+
1012+
@Override
1013+
public PopulateClientMenu getPopulateClientSideMenu() {
1014+
return PopulateClientMenu.AUTOMATIC;
1015+
}
1016+
}
9061017
}

0 commit comments

Comments
 (0)