From 824da42ecfb2e43e5331990f3338d7920735cb31 Mon Sep 17 00:00:00 2001 From: Luciano Vernaschi Date: Fri, 27 Dec 2024 15:47:17 +0100 Subject: [PATCH 1/3] Fix endpoint registration after hotswap --- .../vaadin/hilla/EndpointCodeGenerator.java | 9 +---- .../com/vaadin/hilla/EndpointController.java | 29 +++------------ .../startup/EndpointRegistryInitializer.java | 37 +------------------ .../vaadin/hilla/EndpointControllerTest.java | 11 ++---- .../rest/EndpointWithRestControllerTest.java | 2 +- .../BaseTypeConversionTest.java | 6 +-- 6 files changed, 14 insertions(+), 80 deletions(-) diff --git a/packages/java/endpoint/src/main/java/com/vaadin/hilla/EndpointCodeGenerator.java b/packages/java/endpoint/src/main/java/com/vaadin/hilla/EndpointCodeGenerator.java index f1fc342927..90573d0f84 100644 --- a/packages/java/endpoint/src/main/java/com/vaadin/hilla/EndpointCodeGenerator.java +++ b/packages/java/endpoint/src/main/java/com/vaadin/hilla/EndpointCodeGenerator.java @@ -96,14 +96,7 @@ public void update() { GeneratorProcessor generator = new GeneratorProcessor( engineConfiguration); generator.process(); - - try { - var openApiPath = engineConfiguration.getOpenAPIFile(); - this.endpointController - .registerEndpoints(openApiPath.toUri().toURL()); - } catch (IOException e) { - throw new RuntimeException(e); - } + this.endpointController.registerEndpoints(); }); } diff --git a/packages/java/endpoint/src/main/java/com/vaadin/hilla/EndpointController.java b/packages/java/endpoint/src/main/java/com/vaadin/hilla/EndpointController.java index ddebfa1da9..3c1802b309 100644 --- a/packages/java/endpoint/src/main/java/com/vaadin/hilla/EndpointController.java +++ b/packages/java/endpoint/src/main/java/com/vaadin/hilla/EndpointController.java @@ -17,7 +17,6 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import java.net.URL; import java.util.TreeMap; import com.fasterxml.jackson.core.JsonProcessingException; @@ -114,7 +113,7 @@ public EndpointController(ApplicationContext context, * Initializes the controller by registering all endpoints found in the * OpenApi definition or, as a fallback, in the Spring context. */ - public void registerEndpoints(URL openApiResource) { + public void registerEndpoints() { // Spring returns bean names in lower camel case, while Hilla names // endpoints in upper camel case, so a case-insensitive map is used to // ease searching @@ -123,28 +122,12 @@ public void registerEndpoints(URL openApiResource) { endpointBeans.putAll(context.getBeansWithAnnotation(Endpoint.class)); endpointBeans .putAll(context.getBeansWithAnnotation(BrowserCallable.class)); + endpointBeans.forEach((name, endpointBean) -> endpointRegistry + .registerEndpoint(endpointBean)); - if (endpointRegistry.isEmpty() && !endpointBeans.isEmpty()) { - LOGGER.debug("No endpoints found in openapi.json:" - + " registering all endpoints found using the Spring context"); - - endpointBeans.forEach((name, endpointBean) -> endpointRegistry - .registerEndpoint(endpointBean)); - } - - if (!endpointRegistry.isEmpty()) { - HillaStats.reportHasEndpoint(); - } - - // make sure that signalsHandler endpoint is always registered, but not - // counted as a regular endpoint in stats: - if (endpointRegistry.get(SIGNALS_HANDLER_BEAN_NAME) == null) { - var signalHandlerBean = endpointBeans - .get(SIGNALS_HANDLER_BEAN_NAME); - if (signalHandlerBean != null) { - endpointRegistry.registerEndpoint(signalHandlerBean); - } - } + endpointBeans.keySet().stream() + .filter(name -> !name.equals(SIGNALS_HANDLER_BEAN_NAME)) + .findAny().ifPresent(name -> HillaStats.reportHasEndpoint()); // Temporary Hack VaadinService vaadinService = VaadinService.getCurrent(); diff --git a/packages/java/endpoint/src/main/java/com/vaadin/hilla/startup/EndpointRegistryInitializer.java b/packages/java/endpoint/src/main/java/com/vaadin/hilla/startup/EndpointRegistryInitializer.java index 05a77ca3ec..f1db03929e 100644 --- a/packages/java/endpoint/src/main/java/com/vaadin/hilla/startup/EndpointRegistryInitializer.java +++ b/packages/java/endpoint/src/main/java/com/vaadin/hilla/startup/EndpointRegistryInitializer.java @@ -1,26 +1,13 @@ package com.vaadin.hilla.startup; -import com.vaadin.flow.function.DeploymentConfiguration; import com.vaadin.flow.server.ServiceInitEvent; import com.vaadin.flow.server.VaadinServiceInitListener; import com.vaadin.hilla.EndpointController; -import com.vaadin.hilla.engine.EngineConfiguration; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; -import java.net.MalformedURLException; -import java.net.URL; - @Component public class EndpointRegistryInitializer implements VaadinServiceInitListener { - private static final Logger LOGGER = LoggerFactory - .getLogger(EndpointRegistryInitializer.class); - - private static final String OPEN_API_PROD_RESOURCE_PATH = '/' - + EngineConfiguration.OPEN_API_PATH; - private final EndpointController endpointController; public EndpointRegistryInitializer(EndpointController endpointController) { @@ -29,28 +16,6 @@ public EndpointRegistryInitializer(EndpointController endpointController) { @Override public void serviceInit(ServiceInitEvent event) { - var deploymentConfig = event.getSource().getDeploymentConfiguration(); - var openApiResource = getOpenApiAsResource(deploymentConfig); - endpointController.registerEndpoints(openApiResource); - } - - private URL getOpenApiAsResource(DeploymentConfiguration deploymentConfig) { - if (deploymentConfig.isProductionMode()) { - return getClass().getResource(OPEN_API_PROD_RESOURCE_PATH); - } - var openApiPathInDevMode = deploymentConfig.getProjectFolder().toPath() - .resolve(deploymentConfig.getBuildFolder()) - .resolve(EngineConfiguration.OPEN_API_PATH); - try { - return openApiPathInDevMode.toFile().exists() - ? openApiPathInDevMode.toUri().toURL() - : null; - } catch (MalformedURLException e) { - LOGGER.debug(String.format( - "%s Mode: Path %s to resource %s seems to be malformed/could not be parsed. ", - deploymentConfig.getMode(), openApiPathInDevMode.toUri(), - EngineConfiguration.OPEN_API_PATH), e); - return null; - } + endpointController.registerEndpoints(); } } diff --git a/packages/java/endpoint/src/test/java/com/vaadin/hilla/EndpointControllerTest.java b/packages/java/endpoint/src/test/java/com/vaadin/hilla/EndpointControllerTest.java index 71c504a01a..3af6311432 100644 --- a/packages/java/endpoint/src/test/java/com/vaadin/hilla/EndpointControllerTest.java +++ b/packages/java/endpoint/src/test/java/com/vaadin/hilla/EndpointControllerTest.java @@ -827,7 +827,7 @@ endpointObjectMapper, mock(ExplicitNullableTypeChecker.class), mock(ServletContext.class), registry); new EndpointController(contextMock, registry, invoker, null) - .registerEndpoints(getDefaultOpenApiResourcePathInDevMode()); + .registerEndpoints(); verify(contextMock, never()).getBean(ObjectMapper.class); verify(contextMock, times(1)) @@ -1172,8 +1172,7 @@ private EndpointRegistry registerEndpoints(String openApiFilename) { "libraryEndpoint", new LibraryEndpoint())).when(context) .getBeansWithAnnotation(Endpoint.class); var controller = createVaadinControllerWithApplicationContext(context); - controller.registerEndpoints(getClass() - .getResource("/com/vaadin/hilla/packages/" + openApiFilename)); + controller.registerEndpoints(); return controller.endpointRegistry; } @@ -1314,8 +1313,7 @@ private EndpointController createVaadinController(T endpoint, EndpointController connectController = Mockito .spy(new EndpointController(mockApplicationContext, registry, invoker, csrfChecker)); - connectController - .registerEndpoints(getDefaultOpenApiResourcePathInDevMode()); + connectController.registerEndpoints(); return connectController; } @@ -1343,8 +1341,7 @@ private EndpointController createVaadinControllerWithApplicationContext( EndpointController hillaController = controllerMockBuilder .withObjectMapperFactory(new JacksonObjectMapperFactory.Json()) .withApplicationContext(applicationContext).build(); - hillaController - .registerEndpoints(getDefaultOpenApiResourcePathInDevMode()); + hillaController.registerEndpoints(); return hillaController; } diff --git a/packages/java/endpoint/src/test/java/com/vaadin/hilla/rest/EndpointWithRestControllerTest.java b/packages/java/endpoint/src/test/java/com/vaadin/hilla/rest/EndpointWithRestControllerTest.java index abbfde97ec..4809b9319a 100644 --- a/packages/java/endpoint/src/test/java/com/vaadin/hilla/rest/EndpointWithRestControllerTest.java +++ b/packages/java/endpoint/src/test/java/com/vaadin/hilla/rest/EndpointWithRestControllerTest.java @@ -82,7 +82,7 @@ public void setUp() throws IOException { EndpointControllerMockBuilder controllerMockBuilder = new EndpointControllerMockBuilder(); EndpointController controller = controllerMockBuilder .withApplicationContext(applicationContext).build(); - controller.registerEndpoints(getDefaultOpenApiResourcePathInDevMode()); + controller.registerEndpoints(); mockMvcForEndpoint = MockMvcBuilders.standaloneSetup(controller) .build(); } diff --git a/packages/java/endpoint/src/test/java/com/vaadin/hilla/typeconversion/BaseTypeConversionTest.java b/packages/java/endpoint/src/test/java/com/vaadin/hilla/typeconversion/BaseTypeConversionTest.java index 2daacf5de4..48df88072c 100644 --- a/packages/java/endpoint/src/test/java/com/vaadin/hilla/typeconversion/BaseTypeConversionTest.java +++ b/packages/java/endpoint/src/test/java/com/vaadin/hilla/typeconversion/BaseTypeConversionTest.java @@ -17,7 +17,6 @@ import com.vaadin.hilla.EndpointController; import com.vaadin.hilla.EndpointControllerMockBuilder; -import com.vaadin.hilla.engine.EngineConfiguration; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; @@ -69,10 +68,7 @@ public void setUp() throws IOException { EndpointControllerMockBuilder controllerMockBuilder = new EndpointControllerMockBuilder(); EndpointController controller = controllerMockBuilder .withApplicationContext(applicationContext).build(); - var openApiResource = projectFolder.getRoot().toPath() - .resolve(appConfig.getBuildFolder()) - .resolve(EngineConfiguration.OPEN_API_PATH); - controller.registerEndpoints(openApiResource.toUri().toURL()); + controller.registerEndpoints(); mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); } From e6c437275f26f0be439d10225c1f032e23dea28c Mon Sep 17 00:00:00 2001 From: Luciano Vernaschi Date: Thu, 9 Jan 2025 11:22:00 +0100 Subject: [PATCH 2/3] Remove obsolete endpoints --- .../main/java/com/vaadin/hilla/EndpointController.java | 10 ++++++++-- .../main/java/com/vaadin/hilla/EndpointRegistry.java | 3 ++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/java/endpoint/src/main/java/com/vaadin/hilla/EndpointController.java b/packages/java/endpoint/src/main/java/com/vaadin/hilla/EndpointController.java index 3c1802b309..517b833a3a 100644 --- a/packages/java/endpoint/src/main/java/com/vaadin/hilla/EndpointController.java +++ b/packages/java/endpoint/src/main/java/com/vaadin/hilla/EndpointController.java @@ -18,6 +18,7 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.util.TreeMap; +import java.util.stream.Collectors; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -122,8 +123,13 @@ public void registerEndpoints() { endpointBeans.putAll(context.getBeansWithAnnotation(Endpoint.class)); endpointBeans .putAll(context.getBeansWithAnnotation(BrowserCallable.class)); - endpointBeans.forEach((name, endpointBean) -> endpointRegistry - .registerEndpoint(endpointBean)); + + var currentEndpointNames = endpointBeans.values().stream() + .map(endpointRegistry::registerEndpoint) + .collect(Collectors.toSet()); + // remove obsolete endpoints + endpointRegistry.getEndpoints().keySet() + .retainAll(currentEndpointNames); endpointBeans.keySet().stream() .filter(name -> !name.equals(SIGNALS_HANDLER_BEAN_NAME)) diff --git a/packages/java/endpoint/src/main/java/com/vaadin/hilla/EndpointRegistry.java b/packages/java/endpoint/src/main/java/com/vaadin/hilla/EndpointRegistry.java index 9102d96b62..f401586763 100644 --- a/packages/java/endpoint/src/main/java/com/vaadin/hilla/EndpointRegistry.java +++ b/packages/java/endpoint/src/main/java/com/vaadin/hilla/EndpointRegistry.java @@ -102,7 +102,7 @@ private static String getEndpointNameForClass(Class beanType) { .orElse(beanType.getSimpleName()); } - void registerEndpoint(Object endpointBean) { + String registerEndpoint(Object endpointBean) { // Check the bean type instead of the implementation type in // case of e.g. proxies Class beanType = ClassUtils.getUserClass(endpointBean.getClass()); @@ -133,6 +133,7 @@ void registerEndpoint(Object endpointBean) { new VaadinEndpointData(endpointBean, endpointPublicMethods)); LOGGER.debug("Registered endpoint '{}' with class '{}'", endpointName, beanType); + return endpointName; } /** From e5517827a050c7668075eca4e984982294d05ab9 Mon Sep 17 00:00:00 2001 From: Luciano Vernaschi Date: Thu, 9 Jan 2025 17:34:58 +0100 Subject: [PATCH 3/3] Change when internal endpoint name is made lowercase --- .../src/main/java/com/vaadin/hilla/EndpointRegistry.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/java/endpoint/src/main/java/com/vaadin/hilla/EndpointRegistry.java b/packages/java/endpoint/src/main/java/com/vaadin/hilla/EndpointRegistry.java index f401586763..2b4e02774d 100644 --- a/packages/java/endpoint/src/main/java/com/vaadin/hilla/EndpointRegistry.java +++ b/packages/java/endpoint/src/main/java/com/vaadin/hilla/EndpointRegistry.java @@ -99,7 +99,7 @@ private static String getEndpointNameForClass(Class beanType) { // BrowserCallable has no value so this works return Optional.ofNullable(beanType.getAnnotation(Endpoint.class)) .map(Endpoint::value).filter(value -> !value.isEmpty()) - .orElse(beanType.getSimpleName()); + .orElse(beanType.getSimpleName()).toLowerCase(Locale.ENGLISH); } String registerEndpoint(Object endpointBean) { @@ -129,7 +129,7 @@ String registerEndpoint(Object endpointBean) { Method[] endpointPublicMethods = beanType.getMethods(); AccessibleObject.setAccessible(endpointPublicMethods, true); - vaadinEndpoints.put(endpointName.toLowerCase(Locale.ENGLISH), + vaadinEndpoints.put(endpointName, new VaadinEndpointData(endpointBean, endpointPublicMethods)); LOGGER.debug("Registered endpoint '{}' with class '{}'", endpointName, beanType);