Skip to content

Commit 519e98a

Browse files
fix: add missing reflection hints for type used with ReflectTools (#23059) (#23063)
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: Marco Collovati <marco@vaadin.com>
1 parent dc64baf commit 519e98a

File tree

2 files changed

+114
-0
lines changed

2 files changed

+114
-0
lines changed

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,17 @@
4343
import com.vaadin.flow.component.Component;
4444
import com.vaadin.flow.component.ComponentEvent;
4545
import com.vaadin.flow.component.UI;
46+
import com.vaadin.flow.component.WebComponentExporter;
4647
import com.vaadin.flow.component.page.AppShellConfigurator;
48+
import com.vaadin.flow.i18n.I18NProvider;
4749
import com.vaadin.flow.router.HasErrorParameter;
4850
import com.vaadin.flow.router.HasUrlParameter;
4951
import com.vaadin.flow.router.Layout;
5052
import com.vaadin.flow.router.Route;
5153
import com.vaadin.flow.router.RouteAlias;
5254
import com.vaadin.flow.router.RouterLayout;
5355
import com.vaadin.flow.server.PWA;
56+
import com.vaadin.flow.server.auth.MenuAccessControl;
5457

5558
/**
5659
* Bean factory initialization AOT processor for Vaadin applications.
@@ -122,6 +125,9 @@ public BeanFactoryInitializationAotContribution processAheadOfTime(
122125
registerSubTypes(hints, pkg, HasUrlParameter.class);
123126
registerSubTypes(hints, pkg,
124127
"com.vaadin.flow.data.converter.Converter");
128+
registerSubTypes(hints, pkg, WebComponentExporter.class);
129+
registerSubTypes(hints, pkg, I18NProvider.class);
130+
registerSubTypes(hints, pkg, MenuAccessControl.class);
125131
}
126132
};
127133
}

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

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,10 @@
3939
import com.vaadin.flow.component.ComponentEvent;
4040
import com.vaadin.flow.component.Tag;
4141
import com.vaadin.flow.component.UI;
42+
import com.vaadin.flow.component.WebComponentExporter;
4243
import com.vaadin.flow.component.page.AppShellConfigurator;
44+
import com.vaadin.flow.component.webcomponent.WebComponent;
45+
import com.vaadin.flow.i18n.I18NProvider;
4346
import com.vaadin.flow.router.BeforeEnterEvent;
4447
import com.vaadin.flow.router.BeforeEvent;
4548
import com.vaadin.flow.router.ErrorParameter;
@@ -50,6 +53,7 @@
5053
import com.vaadin.flow.router.RouteAlias;
5154
import com.vaadin.flow.router.RouterLayout;
5255
import com.vaadin.flow.server.PWA;
56+
import com.vaadin.flow.server.auth.MenuAccessControl;
5357

5458
import static org.assertj.core.api.Assertions.assertThat;
5559
import static org.mockito.ArgumentMatchers.any;
@@ -550,6 +554,73 @@ void getSubtypesOf_excludesParentType() {
550554
.isNotEmpty().doesNotContain(RouterLayout.class);
551555
}
552556

557+
@Test
558+
void getSubtypesOf_findsWebComponentExporterSubclasses() {
559+
VaadinBeanFactoryInitializationAotProcessor processor = new VaadinBeanFactoryInitializationAotProcessor();
560+
561+
Collection<Class<?>> exporters = processor.getSubtypesOf(
562+
getClass().getPackageName(), WebComponentExporter.class);
563+
564+
assertThat(exporters).as("Should find WebComponentExporter subclasses")
565+
.contains(TestWebComponentExporter.class);
566+
}
567+
568+
@Test
569+
void getSubtypesOf_findsI18NProviderImplementations() {
570+
VaadinBeanFactoryInitializationAotProcessor processor = new VaadinBeanFactoryInitializationAotProcessor();
571+
572+
Collection<Class<?>> providers = processor
573+
.getSubtypesOf(getClass().getPackageName(), I18NProvider.class);
574+
575+
assertThat(providers).as("Should find I18NProvider implementations")
576+
.contains(TestI18NProvider.class);
577+
}
578+
579+
@Test
580+
void getSubtypesOf_findsMenuAccessControlImplementations() {
581+
VaadinBeanFactoryInitializationAotProcessor processor = new VaadinBeanFactoryInitializationAotProcessor();
582+
583+
Collection<Class<?>> accessControls = processor.getSubtypesOf(
584+
getClass().getPackageName(), MenuAccessControl.class);
585+
586+
assertThat(accessControls)
587+
.as("Should find MenuAccessControl implementations")
588+
.contains(TestMenuAccessControl.class);
589+
}
590+
591+
@Test
592+
void processAheadOfTime_webComponentExporterSubtype_reflectionHintRegistered() {
593+
RuntimeHints hints = processAotForHintsWithSubtypes(
594+
TestWebComponentExporter.class, WebComponentExporter.class);
595+
596+
assertThat(RuntimeHintsPredicates.reflection()
597+
.onType(TestWebComponentExporter.class))
598+
.as("WebComponentExporter subtype should be registered for reflection")
599+
.accepts(hints);
600+
}
601+
602+
@Test
603+
void processAheadOfTime_i18nProviderSubtype_reflectionHintRegistered() {
604+
RuntimeHints hints = processAotForHintsWithSubtypes(
605+
TestI18NProvider.class, I18NProvider.class);
606+
607+
assertThat(RuntimeHintsPredicates.reflection()
608+
.onType(TestI18NProvider.class))
609+
.as("I18NProvider subtype should be registered for reflection")
610+
.accepts(hints);
611+
}
612+
613+
@Test
614+
void processAheadOfTime_menuAccessControlSubtype_reflectionHintRegistered() {
615+
RuntimeHints hints = processAotForHintsWithSubtypes(
616+
TestMenuAccessControl.class, MenuAccessControl.class);
617+
618+
assertThat(RuntimeHintsPredicates.reflection()
619+
.onType(TestMenuAccessControl.class))
620+
.as("MenuAccessControl subtype should be registered for reflection")
621+
.accepts(hints);
622+
}
623+
553624
@Test
554625
void getAnnotatedClasses_findsClassesWithSpecificAnnotation() {
555626
VaadinBeanFactoryInitializationAotProcessor processor = new VaadinBeanFactoryInitializationAotProcessor();
@@ -809,4 +880,41 @@ public static class RouteWithUILayout extends Component {
809880
@Tag("div")
810881
public static class RouteWithDefaultLayout extends Component {
811882
}
883+
884+
public static class TestWebComponentExporter
885+
extends WebComponentExporter<Component> {
886+
public TestWebComponentExporter() {
887+
super("test-exporter");
888+
}
889+
890+
@Override
891+
protected void configureInstance(WebComponent<Component> webComponent,
892+
Component component) {
893+
}
894+
}
895+
896+
public static class TestI18NProvider implements I18NProvider {
897+
@Override
898+
public java.util.List<java.util.Locale> getProvidedLocales() {
899+
return java.util.List.of(java.util.Locale.ENGLISH);
900+
}
901+
902+
@Override
903+
public String getTranslation(String key, java.util.Locale locale,
904+
Object... params) {
905+
return key;
906+
}
907+
}
908+
909+
public static class TestMenuAccessControl implements MenuAccessControl {
910+
@Override
911+
public void setPopulateClientSideMenu(
912+
PopulateClientMenu populateClientMenu) {
913+
}
914+
915+
@Override
916+
public PopulateClientMenu getPopulateClientSideMenu() {
917+
return PopulateClientMenu.AUTOMATIC;
918+
}
919+
}
812920
}

0 commit comments

Comments
 (0)