Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,34 @@

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Consumer;

import org.atmosphere.cpr.ApplicationConfig;
import org.jspecify.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.boot.webmvc.autoconfigure.WebMvcAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.util.ClassUtils;
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.bootstrap.VaadinAsyncInitFutureRegistry;
import com.vaadin.flow.spring.bootstrap.VaadinDefaultAsyncInitFutureRegistry;
import com.vaadin.flow.spring.springnative.ClientCallableAotProcessor;
import com.vaadin.flow.spring.springnative.VaadinBeanFactoryInitializationAotProcessor;

Expand All @@ -52,6 +62,9 @@
@EnableConfigurationProperties(VaadinConfigurationProperties.class)
public class SpringBootAutoConfiguration {

private static final Logger LOG = LoggerFactory
.getLogger(SpringBootAutoConfiguration.class);

@Autowired
private WebApplicationContext context;

Expand All @@ -65,14 +78,45 @@ static ClientCallableAotProcessor flowClientCallableFactoryInitializationAotProc
return new ClientCallableAotProcessor();
}

@Bean
@ConditionalOnProperty(name = "vaadin.bootstrap.async")
@ConditionalOnMissingBean
public VaadinAsyncInitFutureRegistry vaadinWaitForAsyncInitContainer() {
return new VaadinDefaultAsyncInitFutureRegistry();
}

/**
* Creates a {@link ServletContextInitializer} instance.
*
* @return a custom ServletContextInitializer instance
*/
@Bean
public ServletContextInitializer contextInitializer() {
return new VaadinServletContextInitializer(context);
@ConditionalOnMissingBean
public VaadinServletContextInitializer contextInitializer(
Map<String, AsyncTaskExecutor> taskExecutors,
Optional<VaadinAsyncInitFutureRegistry> optVaadinAsyncInitFutureRegistry) {
Consumer<Runnable> contextInitializedExecutor = optVaadinAsyncInitFutureRegistry
.<Consumer<Runnable>> map(reg -> {
final AsyncTaskExecutor asyncTaskExecutor = determineBootstrapExecutor(
taskExecutors);
if (asyncTaskExecutor == null) {
return null;
}
LOG.debug("Will use async bootstrap using {}",
asyncTaskExecutor);
return asyncTaskExecutor::submit;
}).orElseGet(() -> Runnable::run);
return new VaadinServletContextInitializer(context,
contextInitializedExecutor);
}

// NOTE: Based on JpaBaseConfiguration
protected @Nullable AsyncTaskExecutor determineBootstrapExecutor(
Map<String, AsyncTaskExecutor> taskExecutors) {
return taskExecutors.size() == 1
? taskExecutors.values().iterator().next()
: taskExecutors.get(
TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME);
}

/**
Expand All @@ -90,12 +134,13 @@ public ServletContextInitializer contextInitializer() {
@ConditionalOnMissingBean(value = SpringServlet.class, parameterizedContainer = ServletRegistrationBean.class)
public ServletRegistrationBean<SpringServlet> servletRegistrationBean(
ObjectProvider<MultipartConfigElement> multipartConfig,
VaadinConfigurationProperties configurationProperties) {
VaadinConfigurationProperties configurationProperties,
Optional<VaadinAsyncInitFutureRegistry> optVaadinAsyncInitFutureRegistry) {
boolean rootMapping = RootMappedCondition
.isRootMapping(configurationProperties.getUrlMapping());
return configureServletRegistrationBean(multipartConfig,
configurationProperties,
new SpringServlet(context, rootMapping));
configurationProperties, new SpringServlet(context, rootMapping,
optVaadinAsyncInitFutureRegistry));
}

public static ServletRegistrationBean<SpringServlet> configureServletRegistrationBean(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package com.vaadin.flow.spring;

import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
Expand All @@ -23,6 +24,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.Properties;
import java.util.stream.Collectors;

Expand All @@ -36,6 +38,7 @@
import com.vaadin.flow.server.VaadinServlet;
import com.vaadin.flow.server.VaadinServletService;
import com.vaadin.flow.shared.util.SharedUtil;
import com.vaadin.flow.spring.bootstrap.VaadinAsyncInitFutureRegistry;

/**
* Spring application context aware Vaadin servlet implementation.
Expand Down Expand Up @@ -75,6 +78,7 @@ public class SpringServlet extends VaadinServlet {

private final ApplicationContext context;
private final boolean rootMapping;
private final Optional<VaadinAsyncInitFutureRegistry> optVaadinAsyncInitFutureRegistry;

/**
* Creates a new Vaadin servlet instance with the application
Expand All @@ -91,7 +95,6 @@ public class SpringServlet extends VaadinServlet {
* mapping which doesn't overlap with any endpoint mapping. In this case use
* {@code false} for the {@code forwardingEnforced} parameter.
*
*
* @param context
* the Spring application context
* @param rootMapping
Expand All @@ -100,8 +103,45 @@ public class SpringServlet extends VaadinServlet {
* @since 11.0
*/
public SpringServlet(ApplicationContext context, boolean rootMapping) {
this(context, rootMapping, Optional.empty());
}

/**
* Creates a new Vaadin servlet instance with the application
* {@code context} provided.
* <p>
* Use {@code true} as a value for {@code forwardingEnforced} parameter if
* your servlet is mapped to the root ({@code "/*"}). In the case of root
* mapping a {@link RootMappedCondition} is checked and
* {@link VaadinServletConfiguration} is applied conditionally. This
* configuration provide a {@link ServletForwardingController} so that other
* Spring endpoints may co-exist with Vaadin application (it's required
* since root mapping handles any request to the context). This is not
* needed if you are using non-root mapping since are you free to use the
* mapping which doesn't overlap with any endpoint mapping. In this case use
* {@code false} for the {@code forwardingEnforced} parameter.
*
* @param context
* the Spring application context
* @param rootMapping
* the incoming HttpServletRequest is wrapped in
* ForwardingRequestWrapper if {@code true}
* @param optVaadinAsyncInitFutureRegistry
* Optional {@link VaadinAsyncInitFutureRegistry}
* @since 11.0
*/
public SpringServlet(ApplicationContext context, boolean rootMapping,
Optional<VaadinAsyncInitFutureRegistry> optVaadinAsyncInitFutureRegistry) {
this.context = context;
this.rootMapping = rootMapping;
this.optVaadinAsyncInitFutureRegistry = optVaadinAsyncInitFutureRegistry;
}

@Override
public void init(ServletConfig servletConfig) throws ServletException {
optVaadinAsyncInitFutureRegistry.ifPresent(
VaadinAsyncInitFutureRegistry::waitForAllUntilFinishedAndClose);
super.init(servletConfig);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ protected Logger getLogger() {
*/
@Bean
public RootExcludeHandler vaadinRootMapping(Environment environment,
Controller vaadinForwardingController,
@Qualifier("vaadinForwardingController") Controller vaadinForwardingController,
@Autowired(required = false) @Qualifier("resourceHandlerMapping") HandlerMapping resourceHandlerMapping) {
return new RootExcludeHandler(getExcludedUrls(environment),
vaadinForwardingController, resourceHandlerMapping);
Expand All @@ -203,7 +203,7 @@ public RootExcludeHandler vaadinRootMapping(Environment environment,
*
* @return a forwarding controller
*/
@Bean
@Bean(name = "vaadinForwardingController")
public Controller vaadinForwardingController() {
ServletForwardingController controller = new ServletForwardingController();
controller.setServletName(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;

Expand Down Expand Up @@ -113,6 +114,7 @@ public class VaadinServletContextInitializer

private static boolean devModeCachingEnabled;
private ApplicationContext appContext;
private Consumer<Runnable> contextInitializedExecutor;
private ResourceLoader customLoader;
private MetadataReaderFactory metadataReaderFactory;

Expand Down Expand Up @@ -233,17 +235,26 @@ void failFastContextInitialized(ServletContextEvent event)
private static class CompositeServletContextListener
implements ServletContextListener, Serializable {
private final List<FailFastServletContextListener> listeners = new ArrayList<>();
private final Consumer<Runnable> contextInitializedExecutor;

public CompositeServletContextListener(
Consumer<Runnable> contextInitializedExecutor) {
this.contextInitializedExecutor = contextInitializedExecutor;
}

@Override
public void contextInitialized(ServletContextEvent event) {
long start = System.nanoTime();
contextInitializedExecutor.accept(() -> {
long start = System.nanoTime();

listeners.forEach(listener -> listener.contextInitialized(event));
listeners.forEach(
listener -> listener.contextInitialized(event));

long ms = (System.nanoTime() - start) / 1000000;
getLogger().debug(
"Total time for Vaadin Servlet Context Init took {} ms",
ms);
long ms = (System.nanoTime() - start) / 1000000;
getLogger().debug(
"Total time for Vaadin Servlet Context Init took {} ms",
ms);
});
}

@Override
Expand Down Expand Up @@ -728,7 +739,22 @@ public void failFastContextInitialized(ServletContextEvent event) {
* the application context
*/
public VaadinServletContextInitializer(ApplicationContext context) {
this(context, Runnable::run);
}

/**
* Creates a new {@link ServletContextInitializer} instance with application
* {@code context} provided.
*
* @param context
* the application context
* @param contextInitializedExecutor
* execution function for context initialized
*/
public VaadinServletContextInitializer(ApplicationContext context,
Consumer<Runnable> contextInitializedExecutor) {
appContext = context;
this.contextInitializedExecutor = contextInitializedExecutor;

String neverScanProperty = appContext.getEnvironment()
.getProperty("vaadin.blocked-packages");
Expand Down Expand Up @@ -777,9 +803,7 @@ public VaadinServletContextInitializer(ApplicationContext context) {
}

@Override
public void onStartup(ServletContext servletContext)
throws ServletException {

public void onStartup(ServletContext servletContext) {
VaadinServletContext vaadinContext = new VaadinServletContext(
servletContext);
servletContext.addListener(createCompositeListener(vaadinContext));
Expand Down Expand Up @@ -823,7 +847,8 @@ private void initializeDevModeClassCache() {

private CompositeServletContextListener createCompositeListener(
VaadinServletContext context) {
CompositeServletContextListener compositeListener = new CompositeServletContextListener();
CompositeServletContextListener compositeListener = new CompositeServletContextListener(
this.contextInitializedExecutor);

compositeListener.addListener(new LookupInitializerListener());

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright 2000-2026 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.spring.bootstrap;

import java.util.concurrent.CompletableFuture;

/**
* A registry to register initialization futures.
*/
public interface VaadinAsyncInitFutureRegistry {
void register(final CompletableFuture<Void> cf);

void waitForAllUntilFinishedAndClose();
}
Loading