From f04596e9284640ec84f8c288df950369d48ffce9 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Fri, 25 Aug 2023 13:25:51 +0200 Subject: [PATCH 1/7] 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) { + } + } } From 1d076f70d54a904f591046b05e4a507e47a25dad Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Thu, 31 Aug 2023 13:50:20 +0200 Subject: [PATCH 2/7] Reuse Vaadin's Lookup mechanism * renamed VaadinFilter to VaadinRequestInterceptor * Vaadin uses Lookup to init the VaadinService ** Added VaadinRequestInterceptors to ServiceInitEvent ** Added a VaadinRequestInterceptorServiceInitListener that will add interceptors to the init event ** The listener works with ServiceLocator based DI and Spring based DI (has both arg and non-arg constructor) ** That way we have 1 way of initializing the interceptors * Added the VaadinRequestInterceptorServiceInitListener to META-INF for automated locator discovery * Added the VaadinRequestInterceptorServiceInitListener as a bean for Spring Boot auto configuration * Extended the Instantiator mechanism to find all instances of a given class ** Implemented the method in Spring and non-Spring implementations --- .../vaadin/flow/di/DefaultInstantiator.java | 21 +++- .../java/com/vaadin/flow/di/Instantiator.java | 27 ++++- .../vaadin/flow/server/ServiceInitEvent.java | 29 +++++- ...ter.java => VaadinRequestInterceptor.java} | 38 ++++--- ...RequestInterceptorServiceInitListener.java | 63 ++++++++++++ .../com/vaadin/flow/server/VaadinService.java | 98 +++++++++++++++---- .../com/vaadin/flow/server/VaadinServlet.java | 41 ++++---- .../flow/server/VaadinServletService.java | 29 +++--- ...adin.flow.server.VaadinServiceInitListener | 1 + .../MockServletServiceSessionSetup.java | 6 ++ .../flow/server/VaadinServletServiceTest.java | 84 ++++++++++------ .../spring/SpringBootAutoConfiguration.java | 16 +-- .../flow/spring/SpringInstantiator.java | 5 + ...ngBootAutoConfigurationRootMappedTest.java | 55 ++--------- .../SpringVaadinServletServiceTest.java | 46 ++++++--- .../service/TestServletConfiguration.java | 37 +++++++ 16 files changed, 418 insertions(+), 178 deletions(-) rename flow-server/src/main/java/com/vaadin/flow/server/{VaadinFilter.java => VaadinRequestInterceptor.java} (63%) create mode 100644 flow-server/src/main/java/com/vaadin/flow/server/VaadinRequestInterceptorServiceInitListener.java create mode 100644 flow-server/src/main/resources/META-INF/services/com.vaadin.flow.server.VaadinServiceInitListener diff --git a/flow-server/src/main/java/com/vaadin/flow/di/DefaultInstantiator.java b/flow-server/src/main/java/com/vaadin/flow/di/DefaultInstantiator.java index 35c74d7c3bc..3033fa4b221 100644 --- a/flow-server/src/main/java/com/vaadin/flow/di/DefaultInstantiator.java +++ b/flow-server/src/main/java/com/vaadin/flow/di/DefaultInstantiator.java @@ -15,11 +15,6 @@ */ package com.vaadin.flow.di; -import java.util.ServiceLoader; -import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Stream; -import java.util.stream.StreamSupport; - import com.vaadin.flow.component.Component; import com.vaadin.flow.function.DeploymentConfiguration; import com.vaadin.flow.i18n.I18NProvider; @@ -29,6 +24,12 @@ import com.vaadin.flow.server.VaadinService; import com.vaadin.flow.server.VaadinServiceInitListener; +import java.util.Collection; +import java.util.ServiceLoader; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + /** * Default instantiator that is used if no other instantiator has been * registered. This implementation uses vanilla Java mechanisms such as @@ -63,6 +64,16 @@ public T getOrCreate(Class type) { return result == null ? create(type) : result; } + @Override + public Stream getAll(Class type) { + if (service == null) { + return Stream.empty(); + } + Lookup lookup = service.getContext().getAttribute(Lookup.class); + Collection result = lookup == null ? null : lookup.lookupAll(type); + return result == null ? Stream.empty() : result.stream(); + } + @Override public T createComponent(Class componentClass) { return create(componentClass); diff --git a/flow-server/src/main/java/com/vaadin/flow/di/Instantiator.java b/flow-server/src/main/java/com/vaadin/flow/di/Instantiator.java index 5b2bd9b651b..c19cbb57e13 100644 --- a/flow-server/src/main/java/com/vaadin/flow/di/Instantiator.java +++ b/flow-server/src/main/java/com/vaadin/flow/di/Instantiator.java @@ -15,10 +15,6 @@ */ package com.vaadin.flow.di; -import java.io.Serializable; -import java.util.ServiceLoader; -import java.util.stream.Stream; - import com.vaadin.flow.component.Component; import com.vaadin.flow.component.HasElement; import com.vaadin.flow.component.UI; @@ -31,6 +27,11 @@ import com.vaadin.flow.server.communication.IndexHtmlRequestListener; import com.vaadin.flow.server.communication.UidlWriter; +import java.io.Serializable; +import java.util.Collections; +import java.util.ServiceLoader; +import java.util.stream.Stream; + /** * Delegate for discovering, creating and managing instances of various types * used by Flow. Dependency injection frameworks can provide an implementation @@ -116,6 +117,24 @@ default Stream getDependencyFilters( */ T getOrCreate(Class type); + /** + * Provides all instances of any given type, this is an abstraction that + * allows to make use of DI-frameworks from add-ons. + *

+ * How the object is created and whether it is being cached or not is up to + * the implementation. + * + * @param type + * the instance type to fetch + * @param + * the type of the instance to fetch + * + * @return all instances of the given type + */ + default Stream getAll(Class type) { + return (Stream) Collections.emptyList().stream(); + } + /** * Creates an instance of a navigation target or router layout. This method * is not called in cases when a component instance is reused when diff --git a/flow-server/src/main/java/com/vaadin/flow/server/ServiceInitEvent.java b/flow-server/src/main/java/com/vaadin/flow/server/ServiceInitEvent.java index 798522e0791..0b80908112d 100644 --- a/flow-server/src/main/java/com/vaadin/flow/server/ServiceInitEvent.java +++ b/flow-server/src/main/java/com/vaadin/flow/server/ServiceInitEvent.java @@ -15,14 +15,14 @@ */ package com.vaadin.flow.server; +import com.vaadin.flow.server.communication.IndexHtmlRequestListener; + import java.util.ArrayList; import java.util.EventObject; import java.util.List; import java.util.Objects; import java.util.stream.Stream; -import com.vaadin.flow.server.communication.IndexHtmlRequestListener; - /** * Event fired to {@link VaadinServiceInitListener} when a {@link VaadinService} * is being initialized. @@ -38,6 +38,7 @@ public class ServiceInitEvent extends EventObject { private List addedRequestHandlers = new ArrayList<>(); private List addedIndexHtmlRequestListeners = new ArrayList<>(); private List addedDependencyFilters = new ArrayList<>(); + private List addedVaadinRequestInterceptors = new ArrayList<>(); /** * Creates a new service init event for a given {@link VaadinService} and @@ -92,6 +93,20 @@ public void addDependencyFilter(DependencyFilter dependencyFilter) { addedDependencyFilters.add(dependencyFilter); } + /** + * Adds a new request interceptor that will be used by this service. + * + * @param vaadinRequestInterceptor + * the request interceptor to add, not null + */ + public void addVaadinRequestInterceptor( + VaadinRequestInterceptor vaadinRequestInterceptor) { + Objects.requireNonNull(vaadinRequestInterceptor, + "Request Interceptor cannot be null"); + + addedVaadinRequestInterceptors.add(vaadinRequestInterceptor); + } + /** * Gets a stream of all custom request handlers that have been added for the * service. @@ -122,6 +137,16 @@ public Stream getAddedDependencyFilters() { return addedDependencyFilters.stream(); } + /** + * Gets a stream of all Vaadin request interceptors that have been added for + * the service. + * + * @return the stream of added request interceptors + */ + public Stream getAddedVaadinRequestInterceptor() { + return addedVaadinRequestInterceptors.stream(); + } + @Override public VaadinService getSource() { return (VaadinService) super.getSource(); diff --git a/flow-server/src/main/java/com/vaadin/flow/server/VaadinFilter.java b/flow-server/src/main/java/com/vaadin/flow/server/VaadinRequestInterceptor.java similarity index 63% rename from flow-server/src/main/java/com/vaadin/flow/server/VaadinFilter.java rename to flow-server/src/main/java/com/vaadin/flow/server/VaadinRequestInterceptor.java index 20e891d5fd3..a21ea334541 100644 --- a/flow-server/src/main/java/com/vaadin/flow/server/VaadinFilter.java +++ b/flow-server/src/main/java/com/vaadin/flow/server/VaadinRequestInterceptor.java @@ -24,32 +24,44 @@ * @author Marcin Grzejszczak * @since 24.2 */ -public interface VaadinFilter extends Serializable { +public interface VaadinRequestInterceptor extends Serializable { /** * Called when request is about to be processed. - * @param request request - * @param response response + * + * @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 + * + * @param request + * request + * @param response + * response + * @param vaadinSession + * session + * @param t + * exception */ - void handleException(VaadinRequest request, - VaadinResponse response, VaadinSession vaadinSession, Exception t); + 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 + * + * @param request + * request + * @param response + * response + * @param session + * session */ void requestEnd(VaadinRequest request, VaadinResponse response, - VaadinSession session); + VaadinSession session); } diff --git a/flow-server/src/main/java/com/vaadin/flow/server/VaadinRequestInterceptorServiceInitListener.java b/flow-server/src/main/java/com/vaadin/flow/server/VaadinRequestInterceptorServiceInitListener.java new file mode 100644 index 00000000000..45cdb56138c --- /dev/null +++ b/flow-server/src/main/java/com/vaadin/flow/server/VaadinRequestInterceptorServiceInitListener.java @@ -0,0 +1,63 @@ +/* + * 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.util.Collections; + +/** + * Sets all {@link VaadinRequestInterceptor} on an {@link ServiceInitEvent}. + * + * For internal use only. May be renamed or removed in a future release. + * + * @see ServiceInitEvent + * @see VaadinService#init() + * @author Marcin Grzejszczak + * @since 24.2 + */ +public class VaadinRequestInterceptorServiceInitListener + implements VaadinServiceInitListener { + + private final Iterable interceptors; + + /** + * For DI based mechanisms like e.g. Spring. + * + * @param interceptors + * request interceptors + */ + public VaadinRequestInterceptorServiceInitListener( + Iterable interceptors) { + this.interceptors = interceptors; + } + + /** + * For {@link java.util.ServiceLoader} mechanisms. + * + * @see com.vaadin.flow.di.DefaultInstantiator + */ + public VaadinRequestInterceptorServiceInitListener() { + this.interceptors = VaadinService.getCurrent() != null + ? VaadinService.getCurrent().getInstantiator() + .getAll(VaadinRequestInterceptor.class).toList() + : Collections.emptyList(); + } + + @Override + public void serviceInit(ServiceInitEvent event) { + interceptors.forEach(event::addVaadinRequestInterceptor); + } + +} 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 97e37c3c0b5..d6e7f1a29cd 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 @@ -41,14 +41,33 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.*; +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.*; +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.concurrent.*; +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; @@ -83,8 +102,6 @@ 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)} @@ -152,6 +169,8 @@ public abstract class VaadinService implements Serializable { private VaadinContext vaadinContext; + private Iterable vaadinRequestInterceptors; + /** * Creates a new vaadin service based on a deployment configuration. * @@ -192,6 +211,11 @@ public void init() throws ServiceException { List handlers = createRequestHandlers(); + // If the user has already provided interceptors we will add them to the + // list + // and append ones from the ServiceInitEvent + List requestInterceptors = createVaadinRequestInterceptors(); + ServiceInitEvent event = new ServiceInitEvent(this); // allow service init listeners and DI to use thread local access to @@ -206,6 +230,14 @@ public void init() throws ServiceException { requestHandlers = Collections.unmodifiableCollection(handlers); + event.getAddedVaadinRequestInterceptor() + .forEach(requestInterceptors::add); + + Collections.reverse(requestInterceptors); + + vaadinRequestInterceptors = Collections + .unmodifiableCollection(requestInterceptors); + dependencyFilters = Collections.unmodifiableCollection(instantiator .getDependencyFilters(event.getAddedDependencyFilters()) .collect(Collectors.toList())); @@ -294,6 +326,22 @@ protected List createRequestHandlers() return handlers; } + /** + * Called during initialization to add the request handlers for the service. + * Note that the returned list will be reversed so the last interceptor will + * be called first. This enables overriding this method and using add on the + * returned list to add a custom request interceptors which overrides any + * predefined handler. + * + * @return The list of request handlers used by this service. + * @throws ServiceException + * if a problem occurs when creating the request interceptors + */ + protected List createVaadinRequestInterceptors() + throws ServiceException { + return new ArrayList<>(); + } + /** * Creates an instantiator to use with this service. *

@@ -1404,7 +1452,9 @@ 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)); + vaadinRequestInterceptors + .forEach(requestInterceptor -> requestInterceptor + .requestStart(request, response)); } /** @@ -1421,7 +1471,9 @@ public void requestStart(VaadinRequest request, VaadinResponse response) { */ public void requestEnd(VaadinRequest request, VaadinResponse response, VaadinSession session) { - vaadinFilters.forEach(vaadinFilter -> vaadinFilter.requestEnd(request, response, session)); + vaadinRequestInterceptors + .forEach(requestInterceptor -> requestInterceptor + .requestEnd(request, response, session)); if (session != null) { assert VaadinSession.getCurrent() == session; session.lock(); @@ -1449,6 +1501,19 @@ public Iterable getRequestHandlers() { return requestHandlers; } + /** + * Returns the request interceptors that are registered with this service. + * The iteration order of the returned collection is the same as the order + * in which the request handlers will be invoked when a request is handled. + * + * @return a collection of request interceptors in the order they are + * invoked + * @see #createVaadinRequestInterceptors() + */ + public Iterable getVaadinRequestInterceptors() { + return vaadinRequestInterceptors; + } + /** * Gets the filters which all resource dependencies are passed through * before being sent to the client for loading. @@ -1518,13 +1583,16 @@ private void handleExceptionDuringRequest(VaadinRequest request, } try { try { - vaadinFilters.forEach(vaadinFilter -> vaadinFilter.handleException(request, - response, vaadinSession, t)); + vaadinRequestInterceptors + .forEach(requestInterceptor -> requestInterceptor + .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); + "Failed to handle an exception using request interceptors", + ex); } if (vaadinSession != null) { vaadinSession.getErrorHandler().error(new ErrorEvent(t)); @@ -2356,14 +2424,6 @@ 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 @@ -2388,4 +2448,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 6de41dd7527..28a4636baee 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,6 +15,19 @@ */ 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; @@ -24,6 +37,7 @@ 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; @@ -31,13 +45,6 @@ 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. @@ -65,8 +72,6 @@ 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. @@ -584,9 +589,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 @@ -596,7 +601,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(); @@ -642,14 +647,6 @@ private VaadinServletContext initializeContext() { return vaadinServletContext; } - public List getVaadinFilters() { - return vaadinFilters; - } - - public void setVaadinFilters(List vaadinFilters) { - this.vaadinFilters = vaadinFilters; - } - /** * For internal use only. * @@ -659,4 +656,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 a4a8378b0bc..05ea8926f3d 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,19 +16,9 @@ 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; @@ -37,6 +27,18 @@ 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}. * @@ -64,9 +66,6 @@ public VaadinServletService(VaadinServlet servlet, DeploymentConfiguration deploymentConfiguration) { super(deploymentConfiguration); this.servlet = servlet; - if (servlet != null) { - setVaadinFilters(servlet.getVaadinFilters()); - } } /** @@ -158,7 +157,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() { @@ -307,4 +306,4 @@ protected VaadinContext constructVaadinContext() { protected void setDefaultClassLoader() { setClassLoader(getServlet().getServletContext().getClassLoader()); } -} \ No newline at end of file +} diff --git a/flow-server/src/main/resources/META-INF/services/com.vaadin.flow.server.VaadinServiceInitListener b/flow-server/src/main/resources/META-INF/services/com.vaadin.flow.server.VaadinServiceInitListener new file mode 100644 index 00000000000..1a53de365bc --- /dev/null +++ b/flow-server/src/main/resources/META-INF/services/com.vaadin.flow.server.VaadinServiceInitListener @@ -0,0 +1 @@ +com.vaadin.flow.server.VaadinRequestInterceptorServiceInitListener \ 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 cee88c67c0f..da04fe0830f 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 @@ -330,6 +330,8 @@ public MockServletServiceSessionSetup(boolean sessionAvailable) .getResource("/" + invocation.getArgument(0)); }); + configureLookup(lookup); + servlet.init(servletConfig); if (sessionAvailable) { @@ -366,6 +368,10 @@ public MockServletServiceSessionSetup(boolean sessionAvailable) Mockito.when(request.getServletPath()).thenReturn(""); } + protected void configureLookup(Lookup lookup) { + + } + public TestVaadinServletService getService() { return service; } 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 857114180fd..406039031d9 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,6 +1,7 @@ package com.vaadin.flow.server; import com.vaadin.flow.di.Instantiator; +import com.vaadin.flow.di.Lookup; import com.vaadin.flow.server.MockServletServiceSessionSetup.TestVaadinServletService; import com.vaadin.flow.theme.AbstractTheme; import jakarta.servlet.ServletContext; @@ -21,6 +22,8 @@ import java.util.Map; import java.util.concurrent.locks.ReentrantLock; +import static org.mockito.Mockito.when; + /** * Test class for testing es6 resolution by browser capability. This is valid * only for bower mode where we need to decide ourselves. @@ -124,10 +127,10 @@ public void init_classLoaderIsSetUsingServletContext() throws ServiceException { VaadinServlet servlet = Mockito.mock(VaadinServlet.class); ServletContext context = Mockito.mock(ServletContext.class); - Mockito.when(servlet.getServletContext()).thenReturn(context); + when(servlet.getServletContext()).thenReturn(context); ClassLoader loader = Mockito.mock(ClassLoader.class); - Mockito.when(context.getClassLoader()).thenReturn(loader); + when(context.getClassLoader()).thenReturn(loader); VaadinServletService service = new VaadinServletService(servlet, mocks.getDeploymentConfiguration()) { @@ -154,7 +157,7 @@ public void getPwaRegistry_servletInitialized_getsRegistry() { MockServletServiceSessionSetup.TestVaadinServlet vaadinServlet = Mockito .spy(mocks.getServlet()); // Restore original behavior of getServletContext - Mockito.when(vaadinServlet.getServletContext()).thenAnswer( + when(vaadinServlet.getServletContext()).thenAnswer( i -> vaadinServlet.getServletConfig().getServletContext()); VaadinServletService service = new VaadinServletService(vaadinServlet, mocks.getDeploymentConfiguration()); @@ -166,7 +169,7 @@ public void getPwaRegistry_servletNotInitialized_getsNull() { MockServletServiceSessionSetup.TestVaadinServlet vaadinServlet = Mockito .spy(mocks.getServlet()); // Restore original behavior of getServletContext - Mockito.when(vaadinServlet.getServletContext()).thenAnswer( + when(vaadinServlet.getServletContext()).thenAnswer( i -> vaadinServlet.getServletConfig().getServletContext()); VaadinServletService service = new VaadinServletService(vaadinServlet, mocks.getDeploymentConfiguration()); @@ -193,11 +196,9 @@ private HttpServletRequest createNonIncludeRequest(String base, throws Exception { HttpServletRequest request = createRequest(base, realContextPath, realServletPath, pathInfo); - Mockito.when( - request.getAttribute("jakarta.servlet.include.context_path")) + when(request.getAttribute("jakarta.servlet.include.context_path")) .thenReturn(null); - Mockito.when( - request.getAttribute("jakarta.servlet.include.servlet_path")) + when(request.getAttribute("jakarta.servlet.include.servlet_path")) .thenReturn(null); return request; @@ -225,18 +226,20 @@ private HttpServletRequest createRequest(String base, String contextPath, 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()) + attributes.put("requestStartTime", System.currentTimeMillis()); // for + // request + // end + when(request.isSecure()) .thenReturn(url.getProtocol().equalsIgnoreCase("https")); - Mockito.when(request.getServerName()).thenReturn(url.getHost()); - Mockito.when(request.getServerPort()).thenReturn(url.getPort()); - Mockito.when(request.getRequestURI()).thenReturn(url.getPath()); - Mockito.when(request.getContextPath()).thenReturn(contextPath); - Mockito.when(request.getPathInfo()).thenReturn(pathInfo); - Mockito.when(request.getServletPath()).thenReturn(servletPath); + when(request.getServerName()).thenReturn(url.getHost()); + when(request.getServerPort()).thenReturn(url.getPort()); + when(request.getRequestURI()).thenReturn(url.getPath()); + when(request.getContextPath()).thenReturn(contextPath); + when(request.getPathInfo()).thenReturn(pathInfo); + 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); + when(request.getSession()).thenReturn(session); + when(request.getSession(Mockito.anyBoolean())).thenReturn(session); stubSessionAttributes(session, attributes); stubAttributes(request, attributes); return request; @@ -244,7 +247,7 @@ private HttpServletRequest createRequest(String base, String contextPath, private static void stubSessionAttributes(HttpSession session, Map attributes) { - Mockito.when( + 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()); @@ -252,30 +255,45 @@ private static void stubSessionAttributes(HttpSession session, private static void stubAttributes(HttpServletRequest request, Map attributes) { - Mockito.when( + 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", "/")); + public void filtersAreCalledWhenHandlingARequest() throws Exception { + mocks = new MockServletServiceSessionSetup() { + @Override + protected void configureLookup(Lookup lookup) { + when(lookup.lookupAll(VaadinRequestInterceptor.class)) + .thenReturn(Collections + .singletonList(new MyRequestInterceptor())); + } + }; + service = mocks.getService(); + servlet = mocks.getServlet(); + + 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.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")); + 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 { @@ -288,13 +306,14 @@ public boolean handleRequest(VaadinSession session, } } - static class MyFilter implements VaadinFilter { + static class MyRequestInterceptor implements VaadinRequestInterceptor { @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! + // An exception thrown here will not be caught by other methods of + // the filter! } @Override @@ -306,7 +325,10 @@ public void handleException(VaadinRequest request, request.setAttribute("exception handled", "true"); return; } - throw new AssertionError("Invalid exception thrown. Wanted got <" + t.getClass() + ">", t); + throw new AssertionError( + "Invalid exception thrown. Wanted got <" + + t.getClass() + ">", + t); } @Override 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 dc76b988f7f..456c12f5383 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 @@ -16,7 +16,8 @@ package com.vaadin.flow.spring; import com.vaadin.flow.server.Constants; -import com.vaadin.flow.server.VaadinFilter; +import com.vaadin.flow.server.VaadinRequestInterceptor; +import com.vaadin.flow.server.VaadinRequestInterceptorServiceInitListener; import com.vaadin.flow.server.VaadinServlet; import com.vaadin.flow.spring.springnative.VaadinBeanFactoryInitializationAotProcessor; import jakarta.servlet.MultipartConfigElement; @@ -37,7 +38,6 @@ import org.springframework.web.socket.server.standard.ServerEndpointExporter; import java.util.HashMap; -import java.util.List; import java.util.Map; /** @@ -85,7 +85,6 @@ public ServletContextInitializer contextInitializer() { @Bean public ServletRegistrationBean servletRegistrationBean( ObjectProvider multipartConfig, - ObjectProvider> vaadinFilters, VaadinConfigurationProperties configurationProperties) { String mapping = configurationProperties.getUrlMapping(); Map initParameters = new HashMap<>(); @@ -103,10 +102,8 @@ public ServletRegistrationBean servletRegistrationBean( initParameters.put(ApplicationConfig.JSR356_MAPPING_PATH, pushUrl); - SpringServlet springServlet = new SpringServlet(context, rootMapping); - vaadinFilters.ifAvailable(springServlet::setVaadinFilters); ServletRegistrationBean registration = new ServletRegistrationBean<>( - springServlet, mapping); + new SpringServlet(context, rootMapping), mapping); registration.setInitParameters(initParameters); registration .setAsyncSupported(configurationProperties.isAsyncSupported()); @@ -122,6 +119,13 @@ public ServletRegistrationBean servletRegistrationBean( return registration; } + @Bean + public VaadinRequestInterceptorServiceInitListener vaadinRequestInterceptorServiceInitListener( + ObjectProvider interceptors) { + return new VaadinRequestInterceptorServiceInitListener( + interceptors.stream().toList()); + } + /** * Deploys JSR-356 websocket endpoints when Atmosphere is available. * diff --git a/vaadin-spring/src/main/java/com/vaadin/flow/spring/SpringInstantiator.java b/vaadin-spring/src/main/java/com/vaadin/flow/spring/SpringInstantiator.java index 68cab5520f5..d3c1fa5754d 100644 --- a/vaadin-spring/src/main/java/com/vaadin/flow/spring/SpringInstantiator.java +++ b/vaadin-spring/src/main/java/com/vaadin/flow/spring/SpringInstantiator.java @@ -126,4 +126,9 @@ public T getOrCreate(Class type) { return context.getAutowireCapableBeanFactory().createBean(type); } } + + @Override + public Stream getAll(Class type) { + return context.getBeansOfType(type).values().stream(); + } } 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 b9005fd5512..3eaebf401ad 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,23 +1,19 @@ package com.vaadin.flow.spring; -import com.vaadin.flow.server.*; -import jakarta.servlet.ServletException; +import java.util.Set; + 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 java.util.Set; +import com.vaadin.flow.server.Constants; @SpringBootTest(classes = SpringBootAutoConfiguration.class) -@ContextConfiguration(classes = SpringBootAutoConfigurationRootMappedTest.TestConfig.class) +// @ContextConfiguration(SpringBootAutoConfiguration.class) public class SpringBootAutoConfigurationRootMappedTest { // private SpringBootAutoConfiguration autoConfiguration; @@ -25,12 +21,11 @@ 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()); @@ -38,42 +33,4 @@ 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) { - } - } } diff --git a/vaadin-spring/src/test/java/com/vaadin/flow/spring/service/SpringVaadinServletServiceTest.java b/vaadin-spring/src/test/java/com/vaadin/flow/spring/service/SpringVaadinServletServiceTest.java index fc35939abeb..28ef046d2a5 100644 --- a/vaadin-spring/src/test/java/com/vaadin/flow/spring/service/SpringVaadinServletServiceTest.java +++ b/vaadin-spring/src/test/java/com/vaadin/flow/spring/service/SpringVaadinServletServiceTest.java @@ -15,13 +15,17 @@ */ package com.vaadin.flow.spring.service; +import com.vaadin.flow.component.UI; +import com.vaadin.flow.di.Instantiator; +import com.vaadin.flow.server.ServiceException; +import com.vaadin.flow.server.VaadinRequestInterceptor; +import com.vaadin.flow.server.VaadinService; +import com.vaadin.flow.server.VaadinServiceInitListener; +import com.vaadin.flow.spring.instantiator.SpringInstantiatorTest; import jakarta.servlet.ServletException; - -import java.util.Properties; -import java.util.stream.Stream; - import org.junit.Assert; import org.junit.Test; +import org.junit.jupiter.api.Assertions; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; @@ -29,26 +33,28 @@ import org.springframework.stereotype.Component; import org.springframework.test.context.junit4.SpringRunner; -import com.vaadin.flow.component.UI; -import com.vaadin.flow.di.Instantiator; -import com.vaadin.flow.server.ServiceException; -import com.vaadin.flow.server.VaadinService; -import com.vaadin.flow.server.VaadinServiceInitListener; -import com.vaadin.flow.spring.instantiator.SpringInstantiatorTest; +import java.util.List; +import java.util.Properties; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; @RunWith(SpringRunner.class) @Import(TestServletConfiguration.class) public class SpringVaadinServletServiceTest { @Autowired - private ApplicationContext context; + private ApplicationContext context;; @Component public static class TestInstantiator implements Instantiator { + @Autowired + private ApplicationContext context; + @Override public Stream getServiceInitListeners() { - return Stream.of(); + return context.getBeansOfType(VaadinServiceInitListener.class) + .values().stream(); } @Override @@ -89,4 +95,20 @@ public void uiInitListenerAsSpringBean_listenerIsAutoregisteredAsUIInitiLietnerI Assert.assertSame(ui, listener.events.get(0).getUI()); } + @Test + public void filtersAreRegisteredOnTheServlet() throws ServletException { + VaadinService service = SpringInstantiatorTest.getService(context, + new Properties()); + + List interceptors = StreamSupport + .stream(service.getVaadinRequestInterceptors().spliterator(), + false) + .toList(); + Assertions.assertEquals(1, interceptors.size(), + "There should be 1 filter"); + Assertions.assertInstanceOf( + TestServletConfiguration.MyRequestInterceptor.class, + interceptors.get(0), "MyFilter should be registered"); + } + } diff --git a/vaadin-spring/src/test/java/com/vaadin/flow/spring/service/TestServletConfiguration.java b/vaadin-spring/src/test/java/com/vaadin/flow/spring/service/TestServletConfiguration.java index 16b4ee65c3d..83b6a03e59b 100644 --- a/vaadin-spring/src/test/java/com/vaadin/flow/spring/service/TestServletConfiguration.java +++ b/vaadin-spring/src/test/java/com/vaadin/flow/spring/service/TestServletConfiguration.java @@ -15,7 +15,10 @@ */ package com.vaadin.flow.spring.service; +import com.vaadin.flow.server.*; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.SpringBootConfiguration; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @@ -24,4 +27,38 @@ @SpringBootConfiguration public class TestServletConfiguration { + @Configuration(proxyBeanMethods = false) + static class TestConfig { + @Bean + MyRequestInterceptor myFilter() { + return new MyRequestInterceptor(); + } + + @Bean + VaadinRequestInterceptorServiceInitListener vaadinRequestInterceptorServiceInitListener( + ObjectProvider interceptors) { + return new VaadinRequestInterceptorServiceInitListener( + interceptors); + } + } + + static class MyRequestInterceptor implements VaadinRequestInterceptor { + + @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) { + } + } } \ No newline at end of file From 6a807718b05966575f6faa69c72f3a960a5c2eb0 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Thu, 31 Aug 2023 13:56:01 +0200 Subject: [PATCH 3/7] Polish --- flow-server/src/main/java/com/vaadin/flow/di/Instantiator.java | 3 +-- .../flow/spring/service/SpringVaadinServletServiceTest.java | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/flow-server/src/main/java/com/vaadin/flow/di/Instantiator.java b/flow-server/src/main/java/com/vaadin/flow/di/Instantiator.java index c19cbb57e13..59f402c3605 100644 --- a/flow-server/src/main/java/com/vaadin/flow/di/Instantiator.java +++ b/flow-server/src/main/java/com/vaadin/flow/di/Instantiator.java @@ -28,7 +28,6 @@ import com.vaadin.flow.server.communication.UidlWriter; import java.io.Serializable; -import java.util.Collections; import java.util.ServiceLoader; import java.util.stream.Stream; @@ -132,7 +131,7 @@ default Stream getDependencyFilters( * @return all instances of the given type */ default Stream getAll(Class type) { - return (Stream) Collections.emptyList().stream(); + return Stream.empty(); } /** diff --git a/vaadin-spring/src/test/java/com/vaadin/flow/spring/service/SpringVaadinServletServiceTest.java b/vaadin-spring/src/test/java/com/vaadin/flow/spring/service/SpringVaadinServletServiceTest.java index 28ef046d2a5..33c353a3a23 100644 --- a/vaadin-spring/src/test/java/com/vaadin/flow/spring/service/SpringVaadinServletServiceTest.java +++ b/vaadin-spring/src/test/java/com/vaadin/flow/spring/service/SpringVaadinServletServiceTest.java @@ -96,7 +96,8 @@ public void uiInitListenerAsSpringBean_listenerIsAutoregisteredAsUIInitiLietnerI } @Test - public void filtersAreRegisteredOnTheServlet() throws ServletException { + public void requestInterceptorsAreRegisteredOnTheService() + throws ServletException { VaadinService service = SpringInstantiatorTest.getService(context, new Properties()); From e604e608fc93f0a82ff01978a92ebf6c5023a21c Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Tue, 5 Sep 2023 15:17:27 +0200 Subject: [PATCH 4/7] Apply suggestions from code review Co-authored-by: Mikhail Shabarov <61410877+mshabarov@users.noreply.github.com> --- .../services/com.vaadin.flow.server.VaadinServiceInitListener | 2 +- .../flow/spring/service/SpringVaadinServletServiceTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/flow-server/src/main/resources/META-INF/services/com.vaadin.flow.server.VaadinServiceInitListener b/flow-server/src/main/resources/META-INF/services/com.vaadin.flow.server.VaadinServiceInitListener index 1a53de365bc..11ec318f404 100644 --- a/flow-server/src/main/resources/META-INF/services/com.vaadin.flow.server.VaadinServiceInitListener +++ b/flow-server/src/main/resources/META-INF/services/com.vaadin.flow.server.VaadinServiceInitListener @@ -1 +1 @@ -com.vaadin.flow.server.VaadinRequestInterceptorServiceInitListener \ No newline at end of file +com.vaadin.flow.server.VaadinRequestInterceptorServiceInitListener diff --git a/vaadin-spring/src/test/java/com/vaadin/flow/spring/service/SpringVaadinServletServiceTest.java b/vaadin-spring/src/test/java/com/vaadin/flow/spring/service/SpringVaadinServletServiceTest.java index 33c353a3a23..a2b075887ea 100644 --- a/vaadin-spring/src/test/java/com/vaadin/flow/spring/service/SpringVaadinServletServiceTest.java +++ b/vaadin-spring/src/test/java/com/vaadin/flow/spring/service/SpringVaadinServletServiceTest.java @@ -43,7 +43,7 @@ public class SpringVaadinServletServiceTest { @Autowired - private ApplicationContext context;; + private ApplicationContext context; @Component public static class TestInstantiator implements Instantiator { From 15c727fe76485094a09f9a171a8d9e2838a14f4d Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Wed, 13 Sep 2023 15:59:48 +0200 Subject: [PATCH 5/7] VaadinCommandInterceptor --- .../com/vaadin/flow/server/FutureAccess.java | 25 +++- .../vaadin/flow/server/ServiceInitEvent.java | 25 ++++ .../flow/server/VaadinCommandInterceptor.java | 62 ++++++++ ...aadinInterceptorsServiceInitListener.java} | 30 ++-- .../com/vaadin/flow/server/VaadinService.java | 31 +++- ...adin.flow.server.VaadinServiceInitListener | 2 +- .../com/vaadin/flow/component/UITest.java | 134 ++++++++++-------- .../spring/SpringBootAutoConfiguration.java | 15 +- .../service/TestServletConfiguration.java | 37 ++++- 9 files changed, 276 insertions(+), 85 deletions(-) create mode 100644 flow-server/src/main/java/com/vaadin/flow/server/VaadinCommandInterceptor.java rename flow-server/src/main/java/com/vaadin/flow/server/{VaadinRequestInterceptorServiceInitListener.java => VaadinInterceptorsServiceInitListener.java} (54%) diff --git a/flow-server/src/main/java/com/vaadin/flow/server/FutureAccess.java b/flow-server/src/main/java/com/vaadin/flow/server/FutureAccess.java index 86d9bf47c6e..a03f72c943b 100644 --- a/flow-server/src/main/java/com/vaadin/flow/server/FutureAccess.java +++ b/flow-server/src/main/java/com/vaadin/flow/server/FutureAccess.java @@ -15,11 +15,14 @@ */ package com.vaadin.flow.server; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.FutureTask; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.FutureTask; + /** * Encapsulates a {@link Command} submitted using * {@link VaadinSession#access(Command)}. This class is used internally by the @@ -31,6 +34,8 @@ public class FutureAccess extends FutureTask { private final VaadinSession session; private final Command command; + private final Iterable interceptors; + private final Map context = new HashMap<>(); // TODO: ConcurrentHashMap? /** * Creates an instance for the given command. @@ -40,10 +45,17 @@ public class FutureAccess extends FutureTask { * @param command * the command to run when this task is purged from the queue */ - public FutureAccess(VaadinSession session, Command command) { + public FutureAccess(Iterable interceptors, VaadinSession session, Command command) { super(command::execute, null); this.session = session; this.command = command; + this.interceptors = interceptors; + } + + @Override + public void run() { + this.interceptors.forEach(interceptor -> interceptor.commandExecutionStart(context, command)); + super.run(); } @Override @@ -59,7 +71,9 @@ public Void get() throws InterruptedException, ExecutionException { * easier to detect potential problems. */ VaadinService.verifyNoOtherSessionLocked(session); - return super.get(); + Void unused = super.get(); + interceptors.forEach(interceptor -> interceptor.commandExecutionEnd(context, command)); + return unused; } /** @@ -70,6 +84,7 @@ public Void get() throws InterruptedException, ExecutionException { */ public void handleError(Exception exception) { try { + interceptors.forEach(interceptor -> interceptor.handleException(context, command, exception)); if (command instanceof ErrorHandlingCommand) { ErrorHandlingCommand errorHandlingCommand = (ErrorHandlingCommand) command; @@ -88,6 +103,8 @@ public void handleError(Exception exception) { } } catch (Exception e) { getLogger().error(e.getMessage(), e); + } finally { + interceptors.forEach(interceptor -> interceptor.commandExecutionEnd(context, command)); } } diff --git a/flow-server/src/main/java/com/vaadin/flow/server/ServiceInitEvent.java b/flow-server/src/main/java/com/vaadin/flow/server/ServiceInitEvent.java index 0b80908112d..8056349fcf3 100644 --- a/flow-server/src/main/java/com/vaadin/flow/server/ServiceInitEvent.java +++ b/flow-server/src/main/java/com/vaadin/flow/server/ServiceInitEvent.java @@ -39,6 +39,7 @@ public class ServiceInitEvent extends EventObject { private List addedIndexHtmlRequestListeners = new ArrayList<>(); private List addedDependencyFilters = new ArrayList<>(); private List addedVaadinRequestInterceptors = new ArrayList<>(); + private List addedVaadinCommandInterceptor = new ArrayList<>(); /** * Creates a new service init event for a given {@link VaadinService} and @@ -107,6 +108,20 @@ public void addVaadinRequestInterceptor( addedVaadinRequestInterceptors.add(vaadinRequestInterceptor); } + /** + * Adds a new command interceptor that will be used by this service. + * + * @param vaadinCommandInterceptor + * the interceptor to add, not null + */ + public void addVaadinCommandInterceptor( + VaadinCommandInterceptor vaadinCommandInterceptor) { + Objects.requireNonNull(vaadinCommandInterceptor, + "Command Interceptor cannot be null"); + + addedVaadinCommandInterceptor.add(vaadinCommandInterceptor); + } + /** * Gets a stream of all custom request handlers that have been added for the * service. @@ -147,6 +162,16 @@ public Stream getAddedVaadinRequestInterceptor() { return addedVaadinRequestInterceptors.stream(); } + /** + * Gets a stream of all Vaadin command interceptors that have been added for + * the service. + * + * @return the stream of added command interceptors + */ + public Stream getAddedVaadinCommandInterceptor() { + return addedVaadinCommandInterceptor.stream(); + } + @Override public VaadinService getSource() { return (VaadinService) super.getSource(); diff --git a/flow-server/src/main/java/com/vaadin/flow/server/VaadinCommandInterceptor.java b/flow-server/src/main/java/com/vaadin/flow/server/VaadinCommandInterceptor.java new file mode 100644 index 00000000000..db26ff51a47 --- /dev/null +++ b/flow-server/src/main/java/com/vaadin/flow/server/VaadinCommandInterceptor.java @@ -0,0 +1,62 @@ +/* + * 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; +import java.util.Map; + +/** + * Used to provide an around-like aspect option around command processing. + * + * @author Marcin Grzejszczak + * @since 24.2 + */ +public interface VaadinCommandInterceptor extends Serializable { + + /** + * Called when command is about to be started. + * + * @param context + * mutable map passed between methods of this interceptor + * @param command + * command + */ + void commandExecutionStart(Map context, Command command); + + /** + * Called when an exception occurred + * + * @param context + * mutable map passed between methods of this interceptor + * @param command + * command + * @param t + * exception + */ + void handleException(Map context, Command command, Exception t); + + /** + * Called at the end of processing a command. Will be called + * regardless of whether there was an exception or not. + * + * @param context + * mutable map passed between methods of this interceptor + * @param command + * command + */ + void commandExecutionEnd(Map context, Command command); +} diff --git a/flow-server/src/main/java/com/vaadin/flow/server/VaadinRequestInterceptorServiceInitListener.java b/flow-server/src/main/java/com/vaadin/flow/server/VaadinInterceptorsServiceInitListener.java similarity index 54% rename from flow-server/src/main/java/com/vaadin/flow/server/VaadinRequestInterceptorServiceInitListener.java rename to flow-server/src/main/java/com/vaadin/flow/server/VaadinInterceptorsServiceInitListener.java index 45cdb56138c..15a3461c3ab 100644 --- a/flow-server/src/main/java/com/vaadin/flow/server/VaadinRequestInterceptorServiceInitListener.java +++ b/flow-server/src/main/java/com/vaadin/flow/server/VaadinInterceptorsServiceInitListener.java @@ -18,7 +18,7 @@ import java.util.Collections; /** - * Sets all {@link VaadinRequestInterceptor} on an {@link ServiceInitEvent}. + * Sets all Vaadin interceptors on an {@link ServiceInitEvent}. * * For internal use only. May be renamed or removed in a future release. * @@ -27,20 +27,25 @@ * @author Marcin Grzejszczak * @since 24.2 */ -public class VaadinRequestInterceptorServiceInitListener +public class VaadinInterceptorsServiceInitListener implements VaadinServiceInitListener { - private final Iterable interceptors; + private final Iterable requestInterceptors; + private final Iterable commandInterceptors; /** * For DI based mechanisms like e.g. Spring. * - * @param interceptors + * @param requestInterceptors * request interceptors + * @param commandInterceptors + * command interceptors */ - public VaadinRequestInterceptorServiceInitListener( - Iterable interceptors) { - this.interceptors = interceptors; + public VaadinInterceptorsServiceInitListener( + Iterable requestInterceptors, + Iterable commandInterceptors) { + this.requestInterceptors = requestInterceptors; + this.commandInterceptors = commandInterceptors; } /** @@ -48,16 +53,21 @@ public VaadinRequestInterceptorServiceInitListener( * * @see com.vaadin.flow.di.DefaultInstantiator */ - public VaadinRequestInterceptorServiceInitListener() { - this.interceptors = VaadinService.getCurrent() != null + public VaadinInterceptorsServiceInitListener() { + this.requestInterceptors = VaadinService.getCurrent() != null ? VaadinService.getCurrent().getInstantiator() .getAll(VaadinRequestInterceptor.class).toList() : Collections.emptyList(); + this.commandInterceptors = VaadinService.getCurrent() != null + ? VaadinService.getCurrent().getInstantiator() + .getAll(VaadinCommandInterceptor.class).toList() + : Collections.emptyList(); } @Override public void serviceInit(ServiceInitEvent event) { - interceptors.forEach(event::addVaadinRequestInterceptor); + requestInterceptors.forEach(event::addVaadinRequestInterceptor); + commandInterceptors.forEach(event::addVaadinCommandInterceptor); } } 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 d6e7f1a29cd..f66335e3340 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 @@ -171,6 +171,8 @@ public abstract class VaadinService implements Serializable { private Iterable vaadinRequestInterceptors; + private Iterable vaadinCommandInterceptors; + /** * Creates a new vaadin service based on a deployment configuration. * @@ -215,6 +217,7 @@ public void init() throws ServiceException { // list // and append ones from the ServiceInitEvent List requestInterceptors = createVaadinRequestInterceptors(); + List commandInterceptors = createVaadinCommandInterceptor(); ServiceInitEvent event = new ServiceInitEvent(this); @@ -238,6 +241,14 @@ public void init() throws ServiceException { vaadinRequestInterceptors = Collections .unmodifiableCollection(requestInterceptors); + event.getAddedVaadinCommandInterceptor() + .forEach(commandInterceptors::add); + + Collections.reverse(commandInterceptors); + + vaadinCommandInterceptors = Collections + .unmodifiableCollection(commandInterceptors); + dependencyFilters = Collections.unmodifiableCollection(instantiator .getDependencyFilters(event.getAddedDependencyFilters()) .collect(Collectors.toList())); @@ -330,7 +341,7 @@ protected List createRequestHandlers() * Called during initialization to add the request handlers for the service. * Note that the returned list will be reversed so the last interceptor will * be called first. This enables overriding this method and using add on the - * returned list to add a custom request interceptors which overrides any + * returned list to add a custom interceptors which overrides any * predefined handler. * * @return The list of request handlers used by this service. @@ -342,6 +353,22 @@ protected List createVaadinRequestInterceptors() return new ArrayList<>(); } + /** + * Called during initialization to add the request handlers for the service. + * Note that the returned list will be reversed so the last interceptor will + * be called first. This enables overriding this method and using add on the + * returned list to add a custom interceptors which overrides any + * predefined handler. + * + * @return The list of request handlers used by this service. + * @throws ServiceException + * if a problem occurs when creating the request interceptors + */ + protected List createVaadinCommandInterceptor() + throws ServiceException { + return new ArrayList<>(); + } + /** * Creates an instantiator to use with this service. *

@@ -1994,7 +2021,7 @@ public static boolean isCsrfTokenValid(UI ui, String requestToken) { * @see VaadinSession#access(Command) */ public Future accessSession(VaadinSession session, Command command) { - FutureAccess future = new FutureAccess(session, command); + FutureAccess future = new FutureAccess(vaadinCommandInterceptors, session, command); session.getPendingAccessQueue().add(future); ensureAccessQueuePurged(session); diff --git a/flow-server/src/main/resources/META-INF/services/com.vaadin.flow.server.VaadinServiceInitListener b/flow-server/src/main/resources/META-INF/services/com.vaadin.flow.server.VaadinServiceInitListener index 11ec318f404..5903bfc7930 100644 --- a/flow-server/src/main/resources/META-INF/services/com.vaadin.flow.server.VaadinServiceInitListener +++ b/flow-server/src/main/resources/META-INF/services/com.vaadin.flow.server.VaadinServiceInitListener @@ -1 +1 @@ -com.vaadin.flow.server.VaadinRequestInterceptorServiceInitListener +com.vaadin.flow.server.VaadinInterceptorsServiceInitListener diff --git a/flow-server/src/test/java/com/vaadin/flow/component/UITest.java b/flow-server/src/test/java/com/vaadin/flow/component/UITest.java index cc9de2bd4b7..ef56160932e 100644 --- a/flow-server/src/test/java/com/vaadin/flow/component/UITest.java +++ b/flow-server/src/test/java/com/vaadin/flow/component/UITest.java @@ -16,31 +16,6 @@ package com.vaadin.flow.component; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; - -import org.hamcrest.CoreMatchers; -import org.hamcrest.MatcherAssert; -import org.junit.After; -import org.junit.Assert; -import org.junit.Ignore; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.ArgumentMatchers; -import org.mockito.Mockito; -import org.slf4j.Logger; - import com.vaadin.flow.component.internal.PendingJavaScriptInvocation; import com.vaadin.flow.component.page.History; import com.vaadin.flow.component.page.History.HistoryStateChangeEvent; @@ -54,43 +29,32 @@ import com.vaadin.flow.function.SerializableRunnable; import com.vaadin.flow.internal.CurrentInstance; import com.vaadin.flow.internal.StateNode; -import com.vaadin.flow.router.AfterNavigationEvent; -import com.vaadin.flow.router.AfterNavigationListener; -import com.vaadin.flow.router.BeforeEnterEvent; -import com.vaadin.flow.router.BeforeEnterListener; -import com.vaadin.flow.router.BeforeEvent; -import com.vaadin.flow.router.BeforeLeaveEvent; -import com.vaadin.flow.router.BeforeLeaveListener; -import com.vaadin.flow.router.HasUrlParameter; -import com.vaadin.flow.router.ListenerPriority; -import com.vaadin.flow.router.Location; -import com.vaadin.flow.router.NavigationTrigger; -import com.vaadin.flow.router.NotFoundException; -import com.vaadin.flow.router.QueryParameters; -import com.vaadin.flow.router.Route; -import com.vaadin.flow.router.RouteConfiguration; -import com.vaadin.flow.router.RouteNotFoundError; -import com.vaadin.flow.router.RouteParam; -import com.vaadin.flow.router.RouteParameters; -import com.vaadin.flow.router.RoutePrefix; -import com.vaadin.flow.router.Router; -import com.vaadin.flow.router.RouterLayout; +import com.vaadin.flow.router.*; import com.vaadin.flow.router.internal.AfterNavigationHandler; import com.vaadin.flow.router.internal.BeforeEnterHandler; import com.vaadin.flow.router.internal.BeforeLeaveHandler; -import com.vaadin.flow.server.InvalidRouteConfigurationException; -import com.vaadin.flow.server.MockVaadinContext; -import com.vaadin.flow.server.MockVaadinServletService; -import com.vaadin.flow.server.MockVaadinSession; -import com.vaadin.flow.server.VaadinContext; -import com.vaadin.flow.server.VaadinRequest; -import com.vaadin.flow.server.VaadinResponse; -import com.vaadin.flow.server.VaadinService; -import com.vaadin.flow.server.VaadinServletRequest; +import com.vaadin.flow.server.*; import com.vaadin.flow.server.frontend.MockLogger; import com.vaadin.flow.shared.Registration; import com.vaadin.tests.util.AlwaysLockedVaadinSession; import com.vaadin.tests.util.MockUI; +import org.hamcrest.CoreMatchers; +import org.hamcrest.MatcherAssert; +import org.junit.After; +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatchers; +import org.mockito.Mockito; +import org.slf4j.Logger; + +import java.io.IOException; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.Assert.*; public class UITest { @@ -173,6 +137,30 @@ public void visit(StateNode node, NodeVisitor visitor) { } } + static class MyInterceptor implements VaadinCommandInterceptor { + + Map map; + + @Override + public void commandExecutionStart(Map context, + Command command) { + map = context; + context.put("observation.started", true); + } + + @Override + public void handleException(Map context, + Command command, Exception t) { + context.put("observation.error", t); + } + + @Override + public void commandExecutionEnd(Map context, + Command command) { + context.put("observation.end", true); + } + } + @After public void tearDown() { CurrentInstance.clearAll(); @@ -209,7 +197,12 @@ private static MockUI createAccessableTestUI() { } private static void initUI(UI ui, String initialLocation, - ArgumentCaptor statusCodeCaptor) + ArgumentCaptor statusCodeCaptor) { + initUI(ui, initialLocation, statusCodeCaptor, null); + } + + private static void initUI(UI ui, String initialLocation, + ArgumentCaptor statusCodeCaptor, MyInterceptor myInterceptor) throws InvalidRouteConfigurationException { VaadinServletRequest request = Mockito.mock(VaadinServletRequest.class); VaadinResponse response = Mockito.mock(VaadinResponse.class); @@ -228,6 +221,15 @@ private static void initUI(UI ui, String initialLocation, public VaadinContext getContext() { return new MockVaadinContext(); } + + @Override + protected List createVaadinCommandInterceptor() + throws ServiceException { + if (myInterceptor != null) { + return Collections.singletonList(myInterceptor); + } + return super.createVaadinCommandInterceptor(); + } }; service.setCurrentInstances(request, response); @@ -608,6 +610,26 @@ public void unsetSession_accessErrorHandlerStillWorks() throws IOException { logOutputNoDebug.contains("UIDetachedException")); } + @Test + public void unsetSession_commandInterceptorGetsExecuted() throws IOException { + MyInterceptor myInterceptor = new MyInterceptor(); + UI ui = createTestUI(); + initUI(ui, "", null, myInterceptor); + + ui.getSession().access(() -> ui.getInternals().setSession(null)); + ui.access(() -> { + Assert.fail("We should never get here because the UI is detached"); + }); + + // Unlock to run pending access tasks + ui.getSession().unlock(); + + Map map = myInterceptor.map; + Assert.assertTrue("Listener must be called on command start", map.containsKey("observation.started")); + Assert.assertTrue("Listener must be called on command error", map.containsKey("observation.error")); + Assert.assertTrue("Listener must be called on command end", map.containsKey("observation.end")); + } + @Test public void beforeClientResponse_regularOrder() { UI ui = createTestUI(); 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 456c12f5383..7efdcf00783 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,10 +15,7 @@ */ package com.vaadin.flow.spring; -import com.vaadin.flow.server.Constants; -import com.vaadin.flow.server.VaadinRequestInterceptor; -import com.vaadin.flow.server.VaadinRequestInterceptorServiceInitListener; -import com.vaadin.flow.server.VaadinServlet; +import com.vaadin.flow.server.*; import com.vaadin.flow.spring.springnative.VaadinBeanFactoryInitializationAotProcessor; import jakarta.servlet.MultipartConfigElement; import org.atmosphere.cpr.ApplicationConfig; @@ -120,10 +117,12 @@ public ServletRegistrationBean servletRegistrationBean( } @Bean - public VaadinRequestInterceptorServiceInitListener vaadinRequestInterceptorServiceInitListener( - ObjectProvider interceptors) { - return new VaadinRequestInterceptorServiceInitListener( - interceptors.stream().toList()); + public VaadinInterceptorsServiceInitListener vaadinRequestInterceptorServiceInitListener( + ObjectProvider requestInterceptors, + ObjectProvider commandInterceptors) { + return new VaadinInterceptorsServiceInitListener( + requestInterceptors.stream().toList(), + commandInterceptors.stream().toList()); } /** diff --git a/vaadin-spring/src/test/java/com/vaadin/flow/spring/service/TestServletConfiguration.java b/vaadin-spring/src/test/java/com/vaadin/flow/spring/service/TestServletConfiguration.java index 83b6a03e59b..21515aa9df5 100644 --- a/vaadin-spring/src/test/java/com/vaadin/flow/spring/service/TestServletConfiguration.java +++ b/vaadin-spring/src/test/java/com/vaadin/flow/spring/service/TestServletConfiguration.java @@ -22,6 +22,8 @@ import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; +import java.util.Map; + @Configuration @ComponentScan @SpringBootConfiguration @@ -35,10 +37,16 @@ MyRequestInterceptor myFilter() { } @Bean - VaadinRequestInterceptorServiceInitListener vaadinRequestInterceptorServiceInitListener( - ObjectProvider interceptors) { - return new VaadinRequestInterceptorServiceInitListener( - interceptors); + MyVaadinComandInterceptor myVaadinComandInterceptor() { + return new MyVaadinComandInterceptor(); + } + + @Bean + VaadinInterceptorsServiceInitListener vaadinRequestInterceptorServiceInitListener( + ObjectProvider requestInterceptors, + ObjectProvider commandInterceptors) { + return new VaadinInterceptorsServiceInitListener( + requestInterceptors, commandInterceptors); } } @@ -61,4 +69,25 @@ public void requestEnd(VaadinRequest request, VaadinResponse response, VaadinSession session) { } } + + static class MyVaadinComandInterceptor implements VaadinCommandInterceptor { + + @Override + public void commandExecutionStart(Map context, + Command command) { + + } + + @Override + public void handleException(Map context, + Command command, Exception t) { + + } + + @Override + public void commandExecutionEnd(Map context, + Command command) { + + } + } } \ No newline at end of file From d3a682724ed81490e57ca36a2b882a29815b1ddb Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Thu, 28 Sep 2023 15:43:06 +0200 Subject: [PATCH 6/7] Removed unncecessary code --- .../vaadin/flow/di/DefaultInstantiator.java | 21 ++---- .../java/com/vaadin/flow/di/Instantiator.java | 26 +------ ...VaadinInterceptorsServiceInitListener.java | 73 ------------------- ...adin.flow.server.VaadinServiceInitListener | 1 - .../com/vaadin/flow/component/UITest.java | 47 +++++++++++- .../flow/server/VaadinServletServiceTest.java | 4 +- .../spring/SpringBootAutoConfiguration.java | 19 ++--- .../flow/spring/SpringInstantiator.java | 5 -- .../service/TestServletConfiguration.java | 12 +-- 9 files changed, 66 insertions(+), 142 deletions(-) delete mode 100644 flow-server/src/main/java/com/vaadin/flow/server/VaadinInterceptorsServiceInitListener.java delete mode 100644 flow-server/src/main/resources/META-INF/services/com.vaadin.flow.server.VaadinServiceInitListener diff --git a/flow-server/src/main/java/com/vaadin/flow/di/DefaultInstantiator.java b/flow-server/src/main/java/com/vaadin/flow/di/DefaultInstantiator.java index 3033fa4b221..35c74d7c3bc 100644 --- a/flow-server/src/main/java/com/vaadin/flow/di/DefaultInstantiator.java +++ b/flow-server/src/main/java/com/vaadin/flow/di/DefaultInstantiator.java @@ -15,6 +15,11 @@ */ package com.vaadin.flow.di; +import java.util.ServiceLoader; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + import com.vaadin.flow.component.Component; import com.vaadin.flow.function.DeploymentConfiguration; import com.vaadin.flow.i18n.I18NProvider; @@ -24,12 +29,6 @@ import com.vaadin.flow.server.VaadinService; import com.vaadin.flow.server.VaadinServiceInitListener; -import java.util.Collection; -import java.util.ServiceLoader; -import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Stream; -import java.util.stream.StreamSupport; - /** * Default instantiator that is used if no other instantiator has been * registered. This implementation uses vanilla Java mechanisms such as @@ -64,16 +63,6 @@ public T getOrCreate(Class type) { return result == null ? create(type) : result; } - @Override - public Stream getAll(Class type) { - if (service == null) { - return Stream.empty(); - } - Lookup lookup = service.getContext().getAttribute(Lookup.class); - Collection result = lookup == null ? null : lookup.lookupAll(type); - return result == null ? Stream.empty() : result.stream(); - } - @Override public T createComponent(Class componentClass) { return create(componentClass); diff --git a/flow-server/src/main/java/com/vaadin/flow/di/Instantiator.java b/flow-server/src/main/java/com/vaadin/flow/di/Instantiator.java index 59f402c3605..5b2bd9b651b 100644 --- a/flow-server/src/main/java/com/vaadin/flow/di/Instantiator.java +++ b/flow-server/src/main/java/com/vaadin/flow/di/Instantiator.java @@ -15,6 +15,10 @@ */ package com.vaadin.flow.di; +import java.io.Serializable; +import java.util.ServiceLoader; +import java.util.stream.Stream; + import com.vaadin.flow.component.Component; import com.vaadin.flow.component.HasElement; import com.vaadin.flow.component.UI; @@ -27,10 +31,6 @@ import com.vaadin.flow.server.communication.IndexHtmlRequestListener; import com.vaadin.flow.server.communication.UidlWriter; -import java.io.Serializable; -import java.util.ServiceLoader; -import java.util.stream.Stream; - /** * Delegate for discovering, creating and managing instances of various types * used by Flow. Dependency injection frameworks can provide an implementation @@ -116,24 +116,6 @@ default Stream getDependencyFilters( */ T getOrCreate(Class type); - /** - * Provides all instances of any given type, this is an abstraction that - * allows to make use of DI-frameworks from add-ons. - *

- * How the object is created and whether it is being cached or not is up to - * the implementation. - * - * @param type - * the instance type to fetch - * @param - * the type of the instance to fetch - * - * @return all instances of the given type - */ - default Stream getAll(Class type) { - return Stream.empty(); - } - /** * Creates an instance of a navigation target or router layout. This method * is not called in cases when a component instance is reused when diff --git a/flow-server/src/main/java/com/vaadin/flow/server/VaadinInterceptorsServiceInitListener.java b/flow-server/src/main/java/com/vaadin/flow/server/VaadinInterceptorsServiceInitListener.java deleted file mode 100644 index 15a3461c3ab..00000000000 --- a/flow-server/src/main/java/com/vaadin/flow/server/VaadinInterceptorsServiceInitListener.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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.util.Collections; - -/** - * Sets all Vaadin interceptors on an {@link ServiceInitEvent}. - * - * For internal use only. May be renamed or removed in a future release. - * - * @see ServiceInitEvent - * @see VaadinService#init() - * @author Marcin Grzejszczak - * @since 24.2 - */ -public class VaadinInterceptorsServiceInitListener - implements VaadinServiceInitListener { - - private final Iterable requestInterceptors; - private final Iterable commandInterceptors; - - /** - * For DI based mechanisms like e.g. Spring. - * - * @param requestInterceptors - * request interceptors - * @param commandInterceptors - * command interceptors - */ - public VaadinInterceptorsServiceInitListener( - Iterable requestInterceptors, - Iterable commandInterceptors) { - this.requestInterceptors = requestInterceptors; - this.commandInterceptors = commandInterceptors; - } - - /** - * For {@link java.util.ServiceLoader} mechanisms. - * - * @see com.vaadin.flow.di.DefaultInstantiator - */ - public VaadinInterceptorsServiceInitListener() { - this.requestInterceptors = VaadinService.getCurrent() != null - ? VaadinService.getCurrent().getInstantiator() - .getAll(VaadinRequestInterceptor.class).toList() - : Collections.emptyList(); - this.commandInterceptors = VaadinService.getCurrent() != null - ? VaadinService.getCurrent().getInstantiator() - .getAll(VaadinCommandInterceptor.class).toList() - : Collections.emptyList(); - } - - @Override - public void serviceInit(ServiceInitEvent event) { - requestInterceptors.forEach(event::addVaadinRequestInterceptor); - commandInterceptors.forEach(event::addVaadinCommandInterceptor); - } - -} diff --git a/flow-server/src/main/resources/META-INF/services/com.vaadin.flow.server.VaadinServiceInitListener b/flow-server/src/main/resources/META-INF/services/com.vaadin.flow.server.VaadinServiceInitListener deleted file mode 100644 index 5903bfc7930..00000000000 --- a/flow-server/src/main/resources/META-INF/services/com.vaadin.flow.server.VaadinServiceInitListener +++ /dev/null @@ -1 +0,0 @@ -com.vaadin.flow.server.VaadinInterceptorsServiceInitListener diff --git a/flow-server/src/test/java/com/vaadin/flow/component/UITest.java b/flow-server/src/test/java/com/vaadin/flow/component/UITest.java index ef56160932e..e7cb50c9356 100644 --- a/flow-server/src/test/java/com/vaadin/flow/component/UITest.java +++ b/flow-server/src/test/java/com/vaadin/flow/component/UITest.java @@ -29,11 +29,42 @@ import com.vaadin.flow.function.SerializableRunnable; import com.vaadin.flow.internal.CurrentInstance; import com.vaadin.flow.internal.StateNode; -import com.vaadin.flow.router.*; +import com.vaadin.flow.router.AfterNavigationEvent; +import com.vaadin.flow.router.AfterNavigationListener; +import com.vaadin.flow.router.BeforeEnterEvent; +import com.vaadin.flow.router.BeforeEnterListener; +import com.vaadin.flow.router.BeforeEvent; +import com.vaadin.flow.router.BeforeLeaveEvent; +import com.vaadin.flow.router.BeforeLeaveListener; +import com.vaadin.flow.router.HasUrlParameter; +import com.vaadin.flow.router.ListenerPriority; +import com.vaadin.flow.router.Location; +import com.vaadin.flow.router.NavigationTrigger; +import com.vaadin.flow.router.NotFoundException; +import com.vaadin.flow.router.QueryParameters; +import com.vaadin.flow.router.Route; +import com.vaadin.flow.router.RouteConfiguration; +import com.vaadin.flow.router.RouteNotFoundError; +import com.vaadin.flow.router.RouteParam; +import com.vaadin.flow.router.RouteParameters; +import com.vaadin.flow.router.RoutePrefix; +import com.vaadin.flow.router.Router; +import com.vaadin.flow.router.RouterLayout; import com.vaadin.flow.router.internal.AfterNavigationHandler; import com.vaadin.flow.router.internal.BeforeEnterHandler; import com.vaadin.flow.router.internal.BeforeLeaveHandler; -import com.vaadin.flow.server.*; +import com.vaadin.flow.server.Command; +import com.vaadin.flow.server.InvalidRouteConfigurationException; +import com.vaadin.flow.server.MockVaadinContext; +import com.vaadin.flow.server.MockVaadinServletService; +import com.vaadin.flow.server.MockVaadinSession; +import com.vaadin.flow.server.ServiceException; +import com.vaadin.flow.server.VaadinCommandInterceptor; +import com.vaadin.flow.server.VaadinContext; +import com.vaadin.flow.server.VaadinRequest; +import com.vaadin.flow.server.VaadinResponse; +import com.vaadin.flow.server.VaadinService; +import com.vaadin.flow.server.VaadinServletRequest; import com.vaadin.flow.server.frontend.MockLogger; import com.vaadin.flow.shared.Registration; import com.vaadin.tests.util.AlwaysLockedVaadinSession; @@ -50,11 +81,19 @@ import org.slf4j.Logger; import java.io.IOException; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; public class UITest { 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 83f349fa452..bb2d1e022a8 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 @@ -226,8 +226,8 @@ private HttpServletRequest createRequest(String base, String contextPath, Map attributes = new HashMap<>(); attributes.put("null.lock", new ReentrantLock()); // for session attributes.put("requestStartTime", System.currentTimeMillis()); // for - // request - // end + // request + // end when(request.isSecure()) .thenReturn(url.getProtocol().equalsIgnoreCase("https")); when(request.getServerName()).thenReturn(url.getHost()); 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 7efdcf00783..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,7 +15,9 @@ */ package com.vaadin.flow.spring; -import com.vaadin.flow.server.*; +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; @@ -35,6 +37,7 @@ import org.springframework.web.socket.server.standard.ServerEndpointExporter; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -82,6 +85,7 @@ public ServletContextInitializer contextInitializer() { @Bean public ServletRegistrationBean servletRegistrationBean( ObjectProvider multipartConfig, + ObjectProvider> vaadinFilters, VaadinConfigurationProperties configurationProperties) { String mapping = configurationProperties.getUrlMapping(); Map initParameters = new HashMap<>(); @@ -99,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()); @@ -116,15 +122,6 @@ public ServletRegistrationBean servletRegistrationBean( return registration; } - @Bean - public VaadinInterceptorsServiceInitListener vaadinRequestInterceptorServiceInitListener( - ObjectProvider requestInterceptors, - ObjectProvider commandInterceptors) { - return new VaadinInterceptorsServiceInitListener( - requestInterceptors.stream().toList(), - commandInterceptors.stream().toList()); - } - /** * Deploys JSR-356 websocket endpoints when Atmosphere is available. * diff --git a/vaadin-spring/src/main/java/com/vaadin/flow/spring/SpringInstantiator.java b/vaadin-spring/src/main/java/com/vaadin/flow/spring/SpringInstantiator.java index d3c1fa5754d..68cab5520f5 100644 --- a/vaadin-spring/src/main/java/com/vaadin/flow/spring/SpringInstantiator.java +++ b/vaadin-spring/src/main/java/com/vaadin/flow/spring/SpringInstantiator.java @@ -126,9 +126,4 @@ public T getOrCreate(Class type) { return context.getAutowireCapableBeanFactory().createBean(type); } } - - @Override - public Stream getAll(Class type) { - return context.getBeansOfType(type).values().stream(); - } } diff --git a/vaadin-spring/src/test/java/com/vaadin/flow/spring/service/TestServletConfiguration.java b/vaadin-spring/src/test/java/com/vaadin/flow/spring/service/TestServletConfiguration.java index ffb0d6c7610..5c3da9424c4 100644 --- a/vaadin-spring/src/test/java/com/vaadin/flow/spring/service/TestServletConfiguration.java +++ b/vaadin-spring/src/test/java/com/vaadin/flow/spring/service/TestServletConfiguration.java @@ -15,7 +15,10 @@ */ package com.vaadin.flow.spring.service; -import com.vaadin.flow.server.*; +import com.vaadin.flow.server.Command; +import com.vaadin.flow.server.VaadinRequest; +import com.vaadin.flow.server.VaadinResponse; +import com.vaadin.flow.server.VaadinSession; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.SpringBootConfiguration; import org.springframework.context.annotation.Bean; @@ -41,13 +44,6 @@ MyVaadinComandInterceptor myVaadinComandInterceptor() { return new MyVaadinComandInterceptor(); } - @Bean - VaadinInterceptorsServiceInitListener vaadinRequestInterceptorServiceInitListener( - ObjectProvider requestInterceptors, - ObjectProvider commandInterceptors) { - return new VaadinInterceptorsServiceInitListener( - requestInterceptors, commandInterceptors); - } } static class MyRequestInterceptor implements VaadinRequestInterceptor { From 3ace8e85a00aea5068310672a8896d92e2f8b444 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Thu, 28 Sep 2023 15:45:36 +0200 Subject: [PATCH 7/7] PolisH --- .../com/vaadin/flow/server/FutureAccess.java | 18 ++++++++++----- .../flow/server/VaadinCommandInterceptor.java | 7 +++--- .../com/vaadin/flow/server/VaadinService.java | 7 +++--- .../com/vaadin/flow/component/UITest.java | 15 ++++++++---- .../spring/SpringBootAutoConfiguration.java | 23 ++++++++----------- 5 files changed, 40 insertions(+), 30 deletions(-) diff --git a/flow-server/src/main/java/com/vaadin/flow/server/FutureAccess.java b/flow-server/src/main/java/com/vaadin/flow/server/FutureAccess.java index a03f72c943b..ebf30f02c6c 100644 --- a/flow-server/src/main/java/com/vaadin/flow/server/FutureAccess.java +++ b/flow-server/src/main/java/com/vaadin/flow/server/FutureAccess.java @@ -35,7 +35,8 @@ public class FutureAccess extends FutureTask { private final VaadinSession session; private final Command command; private final Iterable interceptors; - private final Map context = new HashMap<>(); // TODO: ConcurrentHashMap? + private final Map context = new HashMap<>(); // TODO: + // ConcurrentHashMap? /** * Creates an instance for the given command. @@ -45,7 +46,8 @@ public class FutureAccess extends FutureTask { * @param command * the command to run when this task is purged from the queue */ - public FutureAccess(Iterable interceptors, VaadinSession session, Command command) { + public FutureAccess(Iterable interceptors, + VaadinSession session, Command command) { super(command::execute, null); this.session = session; this.command = command; @@ -54,7 +56,8 @@ public FutureAccess(Iterable interceptors, VaadinSessi @Override public void run() { - this.interceptors.forEach(interceptor -> interceptor.commandExecutionStart(context, command)); + this.interceptors.forEach(interceptor -> interceptor + .commandExecutionStart(context, command)); super.run(); } @@ -72,7 +75,8 @@ public Void get() throws InterruptedException, ExecutionException { */ VaadinService.verifyNoOtherSessionLocked(session); Void unused = super.get(); - interceptors.forEach(interceptor -> interceptor.commandExecutionEnd(context, command)); + interceptors.forEach(interceptor -> interceptor + .commandExecutionEnd(context, command)); return unused; } @@ -84,7 +88,8 @@ public Void get() throws InterruptedException, ExecutionException { */ public void handleError(Exception exception) { try { - interceptors.forEach(interceptor -> interceptor.handleException(context, command, exception)); + interceptors.forEach(interceptor -> interceptor + .handleException(context, command, exception)); if (command instanceof ErrorHandlingCommand) { ErrorHandlingCommand errorHandlingCommand = (ErrorHandlingCommand) command; @@ -104,7 +109,8 @@ public void handleError(Exception exception) { } catch (Exception e) { getLogger().error(e.getMessage(), e); } finally { - interceptors.forEach(interceptor -> interceptor.commandExecutionEnd(context, command)); + interceptors.forEach(interceptor -> interceptor + .commandExecutionEnd(context, command)); } } diff --git a/flow-server/src/main/java/com/vaadin/flow/server/VaadinCommandInterceptor.java b/flow-server/src/main/java/com/vaadin/flow/server/VaadinCommandInterceptor.java index db26ff51a47..6f178a6ef8f 100644 --- a/flow-server/src/main/java/com/vaadin/flow/server/VaadinCommandInterceptor.java +++ b/flow-server/src/main/java/com/vaadin/flow/server/VaadinCommandInterceptor.java @@ -47,11 +47,12 @@ public interface VaadinCommandInterceptor extends Serializable { * @param t * exception */ - void handleException(Map context, Command command, Exception t); + void handleException(Map context, Command command, + Exception t); /** - * Called at the end of processing a command. Will be called - * regardless of whether there was an exception or not. + * Called at the end of processing a command. Will be called regardless of + * whether there was an exception or not. * * @param context * mutable map passed between methods of this interceptor 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 16a0201b16f..5fa332861c6 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 @@ -367,8 +367,8 @@ protected List createVaadinRequestInterceptors() * Called during initialization to add the request handlers for the service. * Note that the returned list will be reversed so the last interceptor will * be called first. This enables overriding this method and using add on the - * returned list to add a custom interceptors which overrides any - * predefined handler. + * returned list to add a custom interceptors which overrides any predefined + * handler. * * @return The list of request handlers used by this service. * @throws ServiceException @@ -2031,7 +2031,8 @@ public static boolean isCsrfTokenValid(UI ui, String requestToken) { * @see VaadinSession#access(Command) */ public Future accessSession(VaadinSession session, Command command) { - FutureAccess future = new FutureAccess(vaadinCommandInterceptors, session, command); + FutureAccess future = new FutureAccess(vaadinCommandInterceptors, + session, command); session.getPendingAccessQueue().add(future); ensureAccessQueuePurged(session); diff --git a/flow-server/src/test/java/com/vaadin/flow/component/UITest.java b/flow-server/src/test/java/com/vaadin/flow/component/UITest.java index e7cb50c9356..2160593bd42 100644 --- a/flow-server/src/test/java/com/vaadin/flow/component/UITest.java +++ b/flow-server/src/test/java/com/vaadin/flow/component/UITest.java @@ -241,7 +241,8 @@ private static void initUI(UI ui, String initialLocation, } private static void initUI(UI ui, String initialLocation, - ArgumentCaptor statusCodeCaptor, MyInterceptor myInterceptor) + ArgumentCaptor statusCodeCaptor, + MyInterceptor myInterceptor) throws InvalidRouteConfigurationException { VaadinServletRequest request = Mockito.mock(VaadinServletRequest.class); VaadinResponse response = Mockito.mock(VaadinResponse.class); @@ -650,7 +651,8 @@ public void unsetSession_accessErrorHandlerStillWorks() throws IOException { } @Test - public void unsetSession_commandInterceptorGetsExecuted() throws IOException { + public void unsetSession_commandInterceptorGetsExecuted() + throws IOException { MyInterceptor myInterceptor = new MyInterceptor(); UI ui = createTestUI(); initUI(ui, "", null, myInterceptor); @@ -664,9 +666,12 @@ public void unsetSession_commandInterceptorGetsExecuted() throws IOException { ui.getSession().unlock(); Map map = myInterceptor.map; - Assert.assertTrue("Listener must be called on command start", map.containsKey("observation.started")); - Assert.assertTrue("Listener must be called on command error", map.containsKey("observation.error")); - Assert.assertTrue("Listener must be called on command end", map.containsKey("observation.end")); + Assert.assertTrue("Listener must be called on command start", + map.containsKey("observation.started")); + Assert.assertTrue("Listener must be called on command error", + map.containsKey("observation.error")); + Assert.assertTrue("Listener must be called on command end", + map.containsKey("observation.end")); } @Test 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 dc76b988f7f..2abe78e7765 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,11 +15,9 @@ */ package com.vaadin.flow.spring; -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 java.util.HashMap; +import java.util.Map; + import org.atmosphere.cpr.ApplicationConfig; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; @@ -36,9 +34,11 @@ import org.springframework.web.context.WebApplicationContext; import org.springframework.web.socket.server.standard.ServerEndpointExporter; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import com.vaadin.flow.server.Constants; +import com.vaadin.flow.server.VaadinServlet; +import com.vaadin.flow.spring.springnative.VaadinBeanFactoryInitializationAotProcessor; + +import jakarta.servlet.MultipartConfigElement; /** * Spring boot auto-configuration class for Flow. @@ -85,7 +85,6 @@ public ServletContextInitializer contextInitializer() { @Bean public ServletRegistrationBean servletRegistrationBean( ObjectProvider multipartConfig, - ObjectProvider> vaadinFilters, VaadinConfigurationProperties configurationProperties) { String mapping = configurationProperties.getUrlMapping(); Map initParameters = new HashMap<>(); @@ -103,10 +102,8 @@ public ServletRegistrationBean servletRegistrationBean( initParameters.put(ApplicationConfig.JSR356_MAPPING_PATH, pushUrl); - SpringServlet springServlet = new SpringServlet(context, rootMapping); - vaadinFilters.ifAvailable(springServlet::setVaadinFilters); ServletRegistrationBean registration = new ServletRegistrationBean<>( - springServlet, mapping); + new SpringServlet(context, rootMapping), mapping); registration.setInitParameters(initParameters); registration .setAsyncSupported(configurationProperties.isAsyncSupported()); @@ -132,4 +129,4 @@ public ServerEndpointExporter websocketEndpointDeployer() { return new VaadinWebsocketEndpointExporter(); } -} +} \ No newline at end of file