From 7baf255265ce3215a3fdde095a6ea0eced460438 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Fri, 25 Aug 2023 13:25:51 +0200 Subject: [PATCH] Adds a VaadinFilter abstraction VaadinFilter simulates an around aspect around processing of a request related to gh-17436 --- .../com/vaadin/flow/server/VaadinFilter.java | 55 ++++++++++ .../com/vaadin/flow/server/VaadinService.java | 86 +++++++-------- .../com/vaadin/flow/server/VaadinServlet.java | 41 +++---- .../flow/server/VaadinServletService.java | 29 ++--- .../MockServletServiceSessionSetup.java | 49 +++++---- .../flow/server/VaadinServletServiceTest.java | 103 ++++++++++++++++-- .../spring/SpringBootAutoConfiguration.java | 21 ++-- ...ngBootAutoConfigurationRootMappedTest.java | 55 +++++++++- 8 files changed, 314 insertions(+), 125 deletions(-) create mode 100644 flow-server/src/main/java/com/vaadin/flow/server/VaadinFilter.java diff --git a/flow-server/src/main/java/com/vaadin/flow/server/VaadinFilter.java b/flow-server/src/main/java/com/vaadin/flow/server/VaadinFilter.java new file mode 100644 index 00000000000..20e891d5fd3 --- /dev/null +++ b/flow-server/src/main/java/com/vaadin/flow/server/VaadinFilter.java @@ -0,0 +1,55 @@ +/* + * Copyright 2000-2023 Vaadin Ltd. + * + * 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 + * + * http://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 com.vaadin.flow.server; + +import java.io.Serializable; + +/** + * Used to provide an around-like aspect option around request processing. + * + * @author Marcin Grzejszczak + * @since 24.2 + */ +public interface VaadinFilter extends Serializable { + + /** + * Called when request is about to be processed. + * @param request request + * @param response response + */ + void requestStart(VaadinRequest request, VaadinResponse response); + + /** + * Called when an exception occurred + * @param request request + * @param response response + * @param vaadinSession session + * @param t exception + */ + void handleException(VaadinRequest request, + VaadinResponse response, VaadinSession vaadinSession, Exception t); + + /** + * Called in the finally block of processing a request. Will be called + * regardless of whether there was an exception or not. + * @param request request + * @param response response + * @param session session + */ + void requestEnd(VaadinRequest request, VaadinResponse response, + VaadinSession session); +} diff --git a/flow-server/src/main/java/com/vaadin/flow/server/VaadinService.java b/flow-server/src/main/java/com/vaadin/flow/server/VaadinService.java index 98a9b134217..97e37c3c0b5 100644 --- a/flow-server/src/main/java/com/vaadin/flow/server/VaadinService.java +++ b/flow-server/src/main/java/com/vaadin/flow/server/VaadinService.java @@ -16,41 +16,6 @@ package com.vaadin.flow.server; -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; -import java.io.Serializable; -import java.lang.reflect.Constructor; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Optional; -import java.util.ServiceLoader; -import java.util.Set; -import java.util.concurrent.CancellationException; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; -import java.util.stream.Collectors; -import java.util.stream.StreamSupport; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import com.vaadin.flow.component.UI; import com.vaadin.flow.di.DefaultInstantiator; import com.vaadin.flow.di.Instantiator; @@ -64,26 +29,30 @@ import com.vaadin.flow.router.RouteData; import com.vaadin.flow.router.Router; import com.vaadin.flow.server.HandlerHelper.RequestType; -import com.vaadin.flow.server.communication.AtmospherePushConnection; -import com.vaadin.flow.server.communication.HeartbeatHandler; -import com.vaadin.flow.server.communication.IndexHtmlRequestListener; -import com.vaadin.flow.server.communication.IndexHtmlResponse; -import com.vaadin.flow.server.communication.JavaScriptBootstrapHandler; -import com.vaadin.flow.server.communication.PwaHandler; -import com.vaadin.flow.server.communication.SessionRequestHandler; -import com.vaadin.flow.server.communication.StreamRequestHandler; -import com.vaadin.flow.server.communication.UidlRequestHandler; -import com.vaadin.flow.server.communication.WebComponentBootstrapHandler; -import com.vaadin.flow.server.communication.WebComponentProvider; +import com.vaadin.flow.server.communication.*; import com.vaadin.flow.shared.ApplicationConstants; import com.vaadin.flow.shared.JsonConstants; import com.vaadin.flow.shared.Registration; import com.vaadin.flow.shared.communication.PushMode; - import elemental.json.Json; import elemental.json.JsonException; import elemental.json.JsonObject; import elemental.json.impl.JsonUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.*; +import java.lang.reflect.Constructor; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.util.*; +import java.util.Map.Entry; +import java.util.concurrent.*; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; import static java.nio.charset.StandardCharsets.UTF_8; @@ -114,6 +83,8 @@ public abstract class VaadinService implements Serializable { + PushMode.class.getSimpleName() + "." + PushMode.DISABLED.name() + "." + SEPARATOR; + private List vaadinFilters = new ArrayList<>(); + /** * Attribute name for telling * {@link VaadinSession#valueUnbound(jakarta.servlet.http.HttpSessionBindingEvent)} @@ -1433,6 +1404,7 @@ public void requestStart(VaadinRequest request, VaadinResponse response) { } setCurrentInstances(request, response); request.setAttribute(REQUEST_START_TIME_ATTRIBUTE, System.nanoTime()); + vaadinFilters.forEach(vaadinFilter -> vaadinFilter.requestStart(request, response)); } /** @@ -1449,6 +1421,7 @@ public void requestStart(VaadinRequest request, VaadinResponse response) { */ public void requestEnd(VaadinRequest request, VaadinResponse response, VaadinSession session) { + vaadinFilters.forEach(vaadinFilter -> vaadinFilter.requestEnd(request, response, session)); if (session != null) { assert VaadinSession.getCurrent() == session; session.lock(); @@ -1544,6 +1517,15 @@ private void handleExceptionDuringRequest(VaadinRequest request, vaadinSession.lock(); } try { + try { + vaadinFilters.forEach(vaadinFilter -> vaadinFilter.handleException(request, + response, vaadinSession, t)); + } catch (Exception ex) { + // An exception occurred while handling an exception. Log + // it and continue handling only the original error. + getLogger().warn( + "Failed to handle an exception using filters", ex); + } if (vaadinSession != null) { vaadinSession.getErrorHandler().error(new ErrorEvent(t)); } @@ -2374,6 +2356,14 @@ public static String getCsrfTokenAttributeName() { + ApplicationConstants.CSRF_TOKEN; } + public List getVaadinFilters() { + return vaadinFilters; + } + + public void setVaadinFilters(List vaadinFilters) { + this.vaadinFilters = vaadinFilters; + } + private void doSetClassLoader() { final String classLoaderName = getDeploymentConfiguration() == null ? null @@ -2398,4 +2388,4 @@ private void doSetClassLoader() { setDefaultClassLoader(); } } -} +} \ No newline at end of file diff --git a/flow-server/src/main/java/com/vaadin/flow/server/VaadinServlet.java b/flow-server/src/main/java/com/vaadin/flow/server/VaadinServlet.java index 28a4636baee..6de41dd7527 100644 --- a/flow-server/src/main/java/com/vaadin/flow/server/VaadinServlet.java +++ b/flow-server/src/main/java/com/vaadin/flow/server/VaadinServlet.java @@ -15,19 +15,6 @@ */ package com.vaadin.flow.server; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Properties; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import com.vaadin.flow.component.UI; import com.vaadin.flow.di.Lookup; import com.vaadin.flow.function.DeploymentConfiguration; @@ -37,7 +24,6 @@ import com.vaadin.flow.server.HandlerHelper.RequestType; import com.vaadin.flow.server.startup.ApplicationConfiguration; import com.vaadin.flow.shared.JsonConstants; - import jakarta.servlet.ServletConfig; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; @@ -45,6 +31,13 @@ import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.*; /** * The main servlet, which handles all incoming requests to the application. @@ -72,6 +65,8 @@ public class VaadinServlet extends HttpServlet { private static List whenFrontendMappingAvailable = new ArrayList<>(); + private List vaadinFilters = new ArrayList<>(); + /** * Called by the servlet container to indicate to a servlet that the servlet * is being placed into service. @@ -589,9 +584,9 @@ static URL getApplicationUrl(HttpServletRequest request) final URL reqURL = new URL((request.isSecure() ? "https://" : "http://") + request.getServerName() + ((request.isSecure() && request.getServerPort() == 443) - || (!request.isSecure() - && request.getServerPort() == 80) ? "" - : ":" + request.getServerPort()) + || (!request.isSecure() + && request.getServerPort() == 80) ? "" + : ":" + request.getServerPort()) + request.getRequestURI()); String servletPath; if (request @@ -601,7 +596,7 @@ static URL getApplicationUrl(HttpServletRequest request) .getAttribute("jakarta.servlet.include.context_path") .toString() + request.getAttribute( - "jakarta.servlet.include.servlet_path"); + "jakarta.servlet.include.servlet_path"); } else { servletPath = request.getContextPath() + request.getServletPath(); @@ -647,6 +642,14 @@ private VaadinServletContext initializeContext() { return vaadinServletContext; } + public List getVaadinFilters() { + return vaadinFilters; + } + + public void setVaadinFilters(List vaadinFilters) { + this.vaadinFilters = vaadinFilters; + } + /** * For internal use only. * @@ -656,4 +659,4 @@ public static String getFrontendMapping() { return frontendMapping; } -} +} \ No newline at end of file diff --git a/flow-server/src/main/java/com/vaadin/flow/server/VaadinServletService.java b/flow-server/src/main/java/com/vaadin/flow/server/VaadinServletService.java index 05ea8926f3d..a4a8378b0bc 100644 --- a/flow-server/src/main/java/com/vaadin/flow/server/VaadinServletService.java +++ b/flow-server/src/main/java/com/vaadin/flow/server/VaadinServletService.java @@ -16,9 +16,19 @@ package com.vaadin.flow.server; +import com.vaadin.flow.function.DeploymentConfiguration; +import com.vaadin.flow.internal.DevModeHandler; +import com.vaadin.flow.internal.DevModeHandlerManager; +import com.vaadin.flow.server.communication.FaviconHandler; +import com.vaadin.flow.server.communication.IndexHtmlRequestHandler; +import com.vaadin.flow.server.communication.PushRequestHandler; +import com.vaadin.flow.server.startup.ApplicationRouteRegistry; +import com.vaadin.flow.shared.ApplicationConstants; import jakarta.servlet.GenericServlet; import jakarta.servlet.ServletContext; import jakarta.servlet.http.HttpServletRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.InputStream; import java.net.MalformedURLException; @@ -27,18 +37,6 @@ import java.util.Objects; import java.util.Optional; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.vaadin.flow.function.DeploymentConfiguration; -import com.vaadin.flow.internal.DevModeHandler; -import com.vaadin.flow.internal.DevModeHandlerManager; -import com.vaadin.flow.server.communication.FaviconHandler; -import com.vaadin.flow.server.communication.IndexHtmlRequestHandler; -import com.vaadin.flow.server.communication.PushRequestHandler; -import com.vaadin.flow.server.startup.ApplicationRouteRegistry; -import com.vaadin.flow.shared.ApplicationConstants; - /** * A service implementation connected to a {@link VaadinServlet}. * @@ -66,6 +64,9 @@ public VaadinServletService(VaadinServlet servlet, DeploymentConfiguration deploymentConfiguration) { super(deploymentConfiguration); this.servlet = servlet; + if (servlet != null) { + setVaadinFilters(servlet.getVaadinFilters()); + } } /** @@ -157,7 +158,7 @@ private boolean isOtherRequest(VaadinRequest request) { return type == null || ApplicationConstants.REQUEST_TYPE_INIT.equals(type) || ApplicationConstants.REQUEST_TYPE_WEBCOMPONENT_RESYNC - .equals(type); + .equals(type); } public static HttpServletRequest getCurrentServletRequest() { @@ -306,4 +307,4 @@ protected VaadinContext constructVaadinContext() { protected void setDefaultClassLoader() { setClassLoader(getServlet().getServletContext().getClassLoader()); } -} +} \ No newline at end of file diff --git a/flow-server/src/test/java/com/vaadin/flow/server/MockServletServiceSessionSetup.java b/flow-server/src/test/java/com/vaadin/flow/server/MockServletServiceSessionSetup.java index afdb60b0d24..cee88c67c0f 100644 --- a/flow-server/src/test/java/com/vaadin/flow/server/MockServletServiceSessionSetup.java +++ b/flow-server/src/test/java/com/vaadin/flow/server/MockServletServiceSessionSetup.java @@ -1,5 +1,19 @@ package com.vaadin.flow.server; +import com.vaadin.flow.di.Instantiator; +import com.vaadin.flow.di.Lookup; +import com.vaadin.flow.di.ResourceProvider; +import com.vaadin.flow.function.DeploymentConfiguration; +import com.vaadin.flow.internal.CurrentInstance; +import com.vaadin.flow.internal.ResponseWriterTest.CapturingServletOutputStream; +import com.vaadin.flow.router.QueryParameters; +import com.vaadin.flow.router.Router; +import com.vaadin.flow.router.TestRouteRegistry; +import com.vaadin.flow.server.AppShellRegistry.AppShellRegistryWrapper; +import com.vaadin.flow.server.communication.IndexHtmlRequestListener; +import com.vaadin.flow.server.communication.IndexHtmlResponse; +import com.vaadin.flow.shared.ApplicationConstants; +import com.vaadin.tests.util.MockDeploymentConfiguration; import jakarta.servlet.ServletConfig; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; @@ -7,6 +21,9 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -23,25 +40,6 @@ import java.util.concurrent.LinkedBlockingDeque; import java.util.function.Supplier; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; - -import com.vaadin.flow.di.Instantiator; -import com.vaadin.flow.di.Lookup; -import com.vaadin.flow.di.ResourceProvider; -import com.vaadin.flow.function.DeploymentConfiguration; -import com.vaadin.flow.internal.CurrentInstance; -import com.vaadin.flow.internal.ResponseWriterTest.CapturingServletOutputStream; -import com.vaadin.flow.router.QueryParameters; -import com.vaadin.flow.router.Router; -import com.vaadin.flow.router.TestRouteRegistry; -import com.vaadin.flow.server.AppShellRegistry.AppShellRegistryWrapper; -import com.vaadin.flow.server.communication.IndexHtmlRequestListener; -import com.vaadin.flow.server.communication.IndexHtmlResponse; -import com.vaadin.flow.shared.ApplicationConstants; -import com.vaadin.tests.util.MockDeploymentConfiguration; - public class MockServletServiceSessionSetup { public class TestVaadinServletService extends VaadinServletService { @@ -51,6 +49,7 @@ public class TestVaadinServletService extends VaadinServletService { private Router router; private List indexHtmlRequestListeners = new ArrayList<>(); private VaadinContext context; + private List handlers; public TestVaadinServletService(TestVaadinServlet testVaadinServlet, DeploymentConfiguration deploymentConfiguration) { @@ -74,6 +73,18 @@ public void setRouteRegistry(TestRouteRegistry routeRegistry) { this.routeRegistry = routeRegistry; } + @Override + protected List createRequestHandlers() + throws ServiceException { + List requestHandlers = super.createRequestHandlers(); + handlers = requestHandlers; + return requestHandlers; + } + + public List getRequestHandlers() { + return handlers; + } + @Override protected RouteRegistry getRouteRegistry() { if (routeRegistry != null) { diff --git a/flow-server/src/test/java/com/vaadin/flow/server/VaadinServletServiceTest.java b/flow-server/src/test/java/com/vaadin/flow/server/VaadinServletServiceTest.java index eaf6ae4e72c..857114180fd 100644 --- a/flow-server/src/test/java/com/vaadin/flow/server/VaadinServletServiceTest.java +++ b/flow-server/src/test/java/com/vaadin/flow/server/VaadinServletServiceTest.java @@ -1,22 +1,25 @@ package com.vaadin.flow.server; +import com.vaadin.flow.di.Instantiator; +import com.vaadin.flow.server.MockServletServiceSessionSetup.TestVaadinServletService; +import com.vaadin.flow.theme.AbstractTheme; import jakarta.servlet.ServletContext; import jakarta.servlet.http.HttpServletRequest; - -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Collections; -import java.util.List; - +import jakarta.servlet.http.HttpSession; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; -import com.vaadin.flow.di.Instantiator; -import com.vaadin.flow.server.MockServletServiceSessionSetup.TestVaadinServletService; -import com.vaadin.flow.theme.AbstractTheme; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.locks.ReentrantLock; /** * Test class for testing es6 resolution by browser capability. This is valid @@ -220,6 +223,9 @@ private HttpServletRequest createRequest(String base, String contextPath, String servletPath, String pathInfo) throws MalformedURLException { URL url = new URL(base + contextPath + pathInfo); HttpServletRequest request = Mockito.mock(HttpServletRequest.class); + Map attributes = new HashMap<>(); + attributes.put("null.lock", new ReentrantLock()); // for session + attributes.put("requestStartTime", System.currentTimeMillis()); // for request end Mockito.when(request.isSecure()) .thenReturn(url.getProtocol().equalsIgnoreCase("https")); Mockito.when(request.getServerName()).thenReturn(url.getHost()); @@ -228,8 +234,85 @@ private HttpServletRequest createRequest(String base, String contextPath, Mockito.when(request.getContextPath()).thenReturn(contextPath); Mockito.when(request.getPathInfo()).thenReturn(pathInfo); Mockito.when(request.getServletPath()).thenReturn(servletPath); - + HttpSession session = Mockito.mock(HttpSession.class); + Mockito.when(request.getSession()).thenReturn(session); + Mockito.when(request.getSession(Mockito.anyBoolean())).thenReturn(session); + stubSessionAttributes(session, attributes); + stubAttributes(request, attributes); return request; } + private static void stubSessionAttributes(HttpSession session, + Map attributes) { + Mockito.when( + session.getAttribute(Mockito.anyString())).thenAnswer(invocation -> attributes.get(invocation.getArgument(0))); + Mockito.doAnswer(invocation -> attributes.put(invocation.getArgument(0), invocation.getArgument(1))).when( + session).setAttribute(Mockito.anyString(), Mockito.anyString()); + } + + private static void stubAttributes(HttpServletRequest request, + Map attributes) { + Mockito.when( + request.getAttribute(Mockito.anyString())).thenAnswer(invocation -> attributes.get(invocation.getArgument(0))); + Mockito.doAnswer(invocation -> attributes.put(invocation.getArgument(0), invocation.getArgument(1))).when( + request).setAttribute(Mockito.anyString(), Mockito.anyString()); + } + + @Test + public void filtersAreCalledWhenHandlingARequest() + throws MalformedURLException { + VaadinRequest request = servlet.createVaadinRequest(createRequest("http://dummy.host:8080/", "/contextpath", "/servlet", "/")); + VaadinResponse response = Mockito.mock(VaadinResponse.class); + service.setVaadinFilters(Collections.singletonList(new MyFilter())); + service.getRequestHandlers().clear(); + service.getRequestHandlers().add(new ExceptionThrowingRequestHandler()); + + try { + service.handleRequest(request, response); + } catch (ServiceException ex) { + Assert.assertTrue("The exception was the one coming from RequestHandler", ex.getMessage().contains("BOOM!")); + } + + Assert.assertEquals("Filter was called on request start", "true", request.getAttribute("started")); + Assert.assertEquals("Filter was called on exception handling", "true", request.getAttribute("exception handled")); + Assert.assertEquals("Filter was called in the finally block", "true", request.getAttribute("ended")); + } + + static class ExceptionThrowingRequestHandler implements RequestHandler { + + @Override + public boolean handleRequest(VaadinSession session, + VaadinRequest request, VaadinResponse response) + throws IOException { + throw new IllegalStateException("BOOM!"); + } + } + + static class MyFilter implements VaadinFilter { + + @Override + public void requestStart(VaadinRequest request, + VaadinResponse response) { + request.setAttribute("started", "true"); + // An exception thrown here will not be caught by other methods of the filter! + } + + @Override + public void handleException(VaadinRequest request, + VaadinResponse response, VaadinSession vaadinSession, + Exception t) { + if (t instanceof IllegalStateException ex) { + Assert.assertEquals("BOOM!", ex.getMessage()); + request.setAttribute("exception handled", "true"); + return; + } + throw new AssertionError("Invalid exception thrown. Wanted got <" + t.getClass() + ">", t); + } + + @Override + public void requestEnd(VaadinRequest request, VaadinResponse response, + VaadinSession session) { + request.setAttribute("ended", "true"); + } + } } diff --git a/vaadin-spring/src/main/java/com/vaadin/flow/spring/SpringBootAutoConfiguration.java b/vaadin-spring/src/main/java/com/vaadin/flow/spring/SpringBootAutoConfiguration.java index 1c6322f2854..dc76b988f7f 100644 --- a/vaadin-spring/src/main/java/com/vaadin/flow/spring/SpringBootAutoConfiguration.java +++ b/vaadin-spring/src/main/java/com/vaadin/flow/spring/SpringBootAutoConfiguration.java @@ -15,9 +15,11 @@ */ package com.vaadin.flow.spring; -import java.util.HashMap; -import java.util.Map; - +import com.vaadin.flow.server.Constants; +import com.vaadin.flow.server.VaadinFilter; +import com.vaadin.flow.server.VaadinServlet; +import com.vaadin.flow.spring.springnative.VaadinBeanFactoryInitializationAotProcessor; +import jakarta.servlet.MultipartConfigElement; import org.atmosphere.cpr.ApplicationConfig; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; @@ -34,11 +36,9 @@ import org.springframework.web.context.WebApplicationContext; import org.springframework.web.socket.server.standard.ServerEndpointExporter; -import com.vaadin.flow.server.Constants; -import com.vaadin.flow.server.VaadinServlet; -import com.vaadin.flow.spring.springnative.VaadinBeanFactoryInitializationAotProcessor; - -import jakarta.servlet.MultipartConfigElement; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** * Spring boot auto-configuration class for Flow. @@ -85,6 +85,7 @@ public ServletContextInitializer contextInitializer() { @Bean public ServletRegistrationBean servletRegistrationBean( ObjectProvider multipartConfig, + ObjectProvider> vaadinFilters, VaadinConfigurationProperties configurationProperties) { String mapping = configurationProperties.getUrlMapping(); Map initParameters = new HashMap<>(); @@ -102,8 +103,10 @@ public ServletRegistrationBean servletRegistrationBean( initParameters.put(ApplicationConfig.JSR356_MAPPING_PATH, pushUrl); + SpringServlet springServlet = new SpringServlet(context, rootMapping); + vaadinFilters.ifAvailable(springServlet::setVaadinFilters); ServletRegistrationBean registration = new ServletRegistrationBean<>( - new SpringServlet(context, rootMapping), mapping); + springServlet, mapping); registration.setInitParameters(initParameters); registration .setAsyncSupported(configurationProperties.isAsyncSupported()); diff --git a/vaadin-spring/src/test/java/com/vaadin/flow/spring/SpringBootAutoConfigurationRootMappedTest.java b/vaadin-spring/src/test/java/com/vaadin/flow/spring/SpringBootAutoConfigurationRootMappedTest.java index 3eaebf401ad..b9005fd5512 100644 --- a/vaadin-spring/src/test/java/com/vaadin/flow/spring/SpringBootAutoConfigurationRootMappedTest.java +++ b/vaadin-spring/src/test/java/com/vaadin/flow/spring/SpringBootAutoConfigurationRootMappedTest.java @@ -1,19 +1,23 @@ package com.vaadin.flow.spring; -import java.util.Set; - +import com.vaadin.flow.server.*; +import jakarta.servlet.ServletException; import org.atmosphere.cpr.ApplicationConfig; import org.junit.Assert; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.TestConfiguration; import org.springframework.boot.web.servlet.ServletRegistrationBean; +import org.springframework.context.annotation.Bean; import org.springframework.core.env.Environment; +import org.springframework.test.context.ContextConfiguration; -import com.vaadin.flow.server.Constants; +import java.util.Set; @SpringBootTest(classes = SpringBootAutoConfiguration.class) -// @ContextConfiguration(SpringBootAutoConfiguration.class) +@ContextConfiguration(classes = SpringBootAutoConfigurationRootMappedTest.TestConfig.class) public class SpringBootAutoConfigurationRootMappedTest { // private SpringBootAutoConfiguration autoConfiguration; @@ -21,11 +25,12 @@ public class SpringBootAutoConfigurationRootMappedTest { private ServletRegistrationBean servletRegistrationBean; @Autowired private Environment environment; + @Autowired + private MyFilter myFilter; @Test public void urlMappingPassedToAtmosphere() { - Assert.assertTrue(RootMappedCondition - .isRootMapping(RootMappedCondition.getUrlMapping(environment))); + Assert.assertTrue(RootMappedCondition.isRootMapping(RootMappedCondition.getUrlMapping(environment))); Assert.assertEquals( Set.of(VaadinServletConfiguration.VAADIN_SERVLET_MAPPING), servletRegistrationBean.getUrlMappings()); @@ -33,4 +38,42 @@ public void urlMappingPassedToAtmosphere() { servletRegistrationBean.getInitParameters() .get(ApplicationConfig.JSR356_MAPPING_PATH)); } + + @Test + public void filtersAreRegisteredOnTheServlet() throws ServletException { + SpringServlet servlet = servletRegistrationBean.getServlet(); + + Assertions.assertEquals(1, servlet.getVaadinFilters().size(), + "There should be 1 filter"); + Assertions.assertInstanceOf(MyFilter.class, servlet.getVaadinFilters().get(0), + "MyFilter should be registered"); + } + + @TestConfiguration(proxyBeanMethods = false) + static class TestConfig { + @Bean + MyFilter myFilter() { + return new MyFilter(); + } + } + + static class MyFilter implements VaadinFilter { + + @Override + public void requestStart(VaadinRequest request, + VaadinResponse response) { + + } + + @Override + public void handleException(VaadinRequest request, + VaadinResponse response, VaadinSession vaadinSession, + Exception t) { + } + + @Override + public void requestEnd(VaadinRequest request, VaadinResponse response, + VaadinSession session) { + } + } }