Skip to content
Merged
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
131 changes: 131 additions & 0 deletions articles/flow/advanced/service-executor.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
---
title: Vaadin Executor
page-title: How to configure and use Vaadin built-in Executor
description: Configuring and using Vaadin Executor to run background tasks.
meta-description: Discover how to configure and use Vaadin Executor to run background tasks.
order: 177
---


= Vaadin Executor

The Vaadin Executor provides a centralized mechanism for managing asynchronous tasks in Vaadin applications.
It offers a configurable thread pool that can be used for executing background operations without blocking the UI thread.

This feature is particularly useful for performing time-consuming operations asynchronously, executing background tasks
that should not block the UI, managing concurrent operations efficiently and integrating with framework-specific task execution mechanisms,
like Spring [classname]`TaskExecutor` or CDI [classname]`ManagedExecutor`.

By default, Vaadin creates a thread pool executor with the following configuration:

* Core pool size: 8 threads
* Maximum pool size: Unbounded (Integer.MAX_VALUE)

Check warning on line 22 in articles/flow/advanced/service-executor.adoc

View workflow job for this annotation

GitHub Actions / lint

[vale] reported by reviewdog 🐶 [Vaadin.Spacing] 'r.M' should have one space. Raw Output: {"message": "[Vaadin.Spacing] 'r.M' should have one space.", "location": {"path": "articles/flow/advanced/service-executor.adoc", "range": {"start": {"line": 22, "column": 39}}}, "severity": "WARNING"}
* Keep-alive time: 60 seconds for idle threads
* Custom thread factory that creates daemon threads
* Core threads are allowed to time out when idle

This default configuration is suitable for most applications but can be customized as needed.

== Accessing and Using the Executor

You can access the executor service through the [classname]`VaadinService` instance:

[source,java]
----
VaadinService service = VaadinService.getCurrent();
Executor executor = service.getExecutor();

// Execute a task asynchronously
executor.execute(() -> {
// Your background task here
service.longRunningTask();
});

// Execute a task asynchronously using CompletableFeature
CompletableFuture.supplyAsync(service::longRunningTask, executor);
.thenAccept(this::computeResult);
----

You can use the executor service in your UI components to perform background operations and update the UI when complete.

[source,java]
----
Button button = new Button("Start Background Task");
button.addClickListener(event -> {
UI ui = UI.getCurrent();
VaadinService.getCurrent().getExecutor().execute(() -> {

// Perform time-consuming operation
service.longRunningTask();

// Update UI from background thread
ui.access(() -> {
Notification.show("Background task completed!");
});
});
});
----

Remember that code running in the executor is not automatically thread-safe:

* Use `UI.access()` to update the UI from background threads.
* Be careful with shared state and consider using thread-safe collections or synchronization.
* Avoid long-running tasks that might block the thread pool.

== Configuring a Custom Executor

In standard Java applications, you can customize the executor by registering a <<service-init-listener#,VaadinServiceInitListener>> and providing your own executor implementation:

.`CustomExecutorServiceInitListener.java`
[source,java]
----
package com.example;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import com.vaadin.flow.server.ServiceInitEvent;
import com.vaadin.flow.server.VaadinServiceInitListener;

public class CustomExecutorServiceInitListener implements VaadinServiceInitListener {
@Override
public void serviceInit(ServiceInitEvent event) {
ThreadPoolExecutor customExecutor = new ThreadPoolExecutor(
16, // Core pool size
32, // Maximum pool size
120, // Keep-alive time
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(),
r -> {
Thread thread = new Thread(r, "CustomVaadinExecutor-" + r.hashCode());
thread.setDaemon(true);
return thread;
});

// Allow core threads to time out
customExecutor.allowCoreThreadTimeOut(true);

// Set the custom executor
event.setExecutor(customExecutor);
}
}
----

Register your listener using Java's `ServiceLoader` mechanism by creating a file at
`META-INF/services/com.vaadin.flow.server.VaadinServiceInitListener`.
The file should contain the fully qualified name of your implementation class:
`com.example.CustomExecutorServiceInitListener`

When configuring a custom Thread Pool, consider the following best practices:

* *Core Pool Size*: Set based on the number of concurrent tasks your application typically handles. A good starting point is the number of CPU cores.
* *Maximum Pool Size*: Set to a reasonable upper limit to prevent resource exhaustion. Consider your server's memory and CPU constraints.
* *Queue Capacity*: Use a bounded queue to prevent memory issues when task submission rate exceeds execution rate.
* Use descriptive thread names to make debugging easier.
* Make threads daemon threads (`Thread.setDaemon(true)`) to prevent them from blocking JVM shutdown.
* Allow core threads to time out if your application has periods of inactivity

For framework integrations, consult the specific documentation pages for <<../integrations/spring/configuration#configure-custom-vaadin-executor,Spring>>, <<../integrations/cdi/service-beans#configure-custom-vaadin-executor,CDI>> and <<../integrations/quarkus#configure-custom-vaadin-executor,Quarkus>>.


[discussion-id]`2EF7B593-E426-479E-89D7-E62667D316C2`
45 changes: 45 additions & 0 deletions articles/flow/integrations/cdi/service-beans.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
- [interfacename]`InstantiatorFactory`
- [interfacename]`SystemMessagesProvider`
- [interfacename]`ErrorHandler`
- [interfacename]`Executor`

To ensure the beans are recognized, they should be qualified by the `@VaadinServiceEnabled` annotation. This is required because it marks a bean which is used as an `I18NProvider` by the service. If there are any other `I18NProvider` beans, the one that's also used by the service is used.

Expand Down Expand Up @@ -55,4 +56,48 @@
For example, there's no guarantee that an active Vaadin session or UI context exists when the add-on looks for any of these beans. It's safe, though, to use standard CDI `@Dependent` and `@ApplicationScoped` contexts.


== Configure Custom Vaadin Executor

When a Vaadin application starts in a CDI environment, the [classname]`CdiVaadinServletService` overrides the `createDefaultExecutor()` method to use CDI capabilities for selecting an appropriate [interfacename]`Executor` for executing Vaadin's internal tasks. The selection process follows this order:

1. Look for a custom [interfacename]`Executor` bean qualified with the [annotationname]`@VaadinServiceEnabled` annotation
2. If no custom executor is found, try to use the container's [interfacename]`ManagedExecutorService` (available in Jakarta EE environments)
3. If neither is available, fall back to Vaadin's <<{articles}/flow/advanced/service-executor#, default executor>> implementation

To avoid ambiguity, the CDI extension enforces these rules:

* There can be at most one [interfacename]`Executor` bean annotated with [annotationname]`@VaadinServiceEnabled`
* If multiple [interfacename]`Executor` beans with [annotationname]`@VaadinServiceEnabled` annotation are found, the application deployment will fail with an appropriate error message

Check warning on line 70 in articles/flow/integrations/cdi/service-beans.adoc

View workflow job for this annotation

GitHub Actions / lint

[vale] reported by reviewdog 🐶 [Vaadin.Will] Avoid using 'will'. Raw Output: {"message": "[Vaadin.Will] Avoid using 'will'.", "location": {"path": "articles/flow/integrations/cdi/service-beans.adoc", "range": {"start": {"line": 70, "column": 141}}}, "severity": "WARNING"}


You can provide your own task executor to customize how Vaadin executes server-side operations. This can be useful for implementing custom threading models, using specific thread pools, or integrating with other execution frameworks.
To provide a custom executor, define a CDI bean that implements the [interfacename]`Executor` interface and annotate it with [annotationname]`@VaadinServiceEnabled`:

[source,java]
----
@ApplicationScoped
public class ExecutorConfiguration {

@Produces
@Singleton
@VaadinServiceEnabled
public Executor vaadinTaskExecutor() {
ThreadPoolExecutor customExecutor = new ThreadPoolExecutor(
16, // Core pool size
32, // Maximum pool size
120, // Keep-alive time
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(),
r -> {
Thread thread = new Thread(r, "CustomVaadinExecutor-" + r.hashCode());
thread.setDaemon(true);
return thread;
});
customExecutor.allowCoreThreadTimeOut(true);
return customExecutor;
}
}
----


[discussion-id]`A55D3416-D5B7-40B9-8C4A-1454E97C92F1`
44 changes: 44 additions & 0 deletions articles/flow/integrations/quarkus.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,50 @@
Quarkus attempts to remove by default all unused beans during the build. However, it can't detect the programmatic lookup performed by the [classname]`QuarkusInstantiator` class in Vaadin. Therefore, when implementing beans that are not referenced directly by the application code, you might need to add the [annotationname]`@Unremovable` annotation to the class. +
[since:com.vaadin:[email protected]]##[annotationname]`@VaadinServiceEnabled` annotated beans are marked as unremovable by default.##

== Configure Custom Vaadin Executor

When a Vaadin application starts in a Quarkus environment, the [classname]`QuarkusVaadinServletService` overrides the `createDefaultExecutor()` method to use CDI capabilities for selecting an appropriate [interfacename]`Executor` for executing Vaadin's internal tasks. The selection process follows this order:

1. Look for a custom [interfacename]`Executor` bean qualified with the [annotationname]`@VaadinServiceEnabled` annotation
2. If no custom executor is found, try to use the container's [interfacename]`ManagedExecutor`
3. If neither is available, fall back to Vaadin's <<{articles}/flow/advanced/service-executor#, default executor>> implementation

To avoid ambiguity, the Quarkus extension enforces these rules:

* There can be at most one [interfacename]`Executor` bean annotated with [annotationname]`@VaadinServiceEnabled`
* If multiple [interfacename]`Executor` beans with [annotationname]`@VaadinServiceEnabled` annotation are found, the application deployment will fail with an appropriate error message

Check warning on line 143 in articles/flow/integrations/quarkus.adoc

View workflow job for this annotation

GitHub Actions / lint

[vale] reported by reviewdog 🐶 [Vaadin.Will] Avoid using 'will'. Raw Output: {"message": "[Vaadin.Will] Avoid using 'will'.", "location": {"path": "articles/flow/integrations/quarkus.adoc", "range": {"start": {"line": 143, "column": 141}}}, "severity": "WARNING"}


You can provide your own task executor to customize how Vaadin executes server-side operations. This can be useful for implementing custom threading models, using specific thread pools, or integrating with other execution frameworks.
To provide a custom executor, define a CDI bean that implements the [interfacename]`Executor` interface and annotate it with [annotationname]`@VaadinServiceEnabled`:

[source,java]
----
@ApplicationScoped
public class ExecutorConfiguration {

@Produces
@Singleton
@VaadinServiceEnabled
public Executor vaadinTaskExecutor() {
ThreadPoolExecutor customExecutor = new ThreadPoolExecutor(
16, // Core pool size
32, // Maximum pool size
120, // Keep-alive time
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(),
r -> {
Thread thread = new Thread(r, "CustomVaadinExecutor-" + r.hashCode());
thread.setDaemon(true);
return thread;
});
customExecutor.allowCoreThreadTimeOut(true);
return customExecutor;
}
}
----



[[quarkus.vaadin.addons]]
== Vaadin Add-Ons in Quarkus
Expand Down
68 changes: 68 additions & 0 deletions articles/flow/integrations/spring/configuration.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
page-title: Spring Configuration for Vaadin Applications
description: Configuring Spring properties in a Vaadin application.
meta-description: Configure Spring applications with Vaadin for seamless integration.
layout: tabbed-page
order: 60
---

Expand Down Expand Up @@ -215,6 +216,73 @@
}
----

== Configure Custom Vaadin Executor

When running a Vaadin application in a Spring environment, the [classname]`SpringVaadinServletService` automatically selects an appropriate executor for asynchronous operations using the following process:

. Look for [interfacename]`TaskExecutor` beans defined in the Spring application context
. If no [interfacename]`TaskExecutor` beans are found, fall back to Vaadin's <<{articles}/flow/advanced/service-executor#, default executor>>
. If [interfacename]`TaskExecutor` beans are found, apply specific selection rules to choose the most appropriate one

When multiple [interfacename]`TaskExecutor` beans exist in your application, Vaadin follows these rules to select the appropriate one:

. If any bean is annotated with [annotationname]`@VaadinTaskExecutor` or named `VaadinTaskExecutor`, it will be chosen

Check warning on line 229 in articles/flow/integrations/spring/configuration.adoc

View workflow job for this annotation

GitHub Actions / lint

[vale] reported by reviewdog 🐶 [Vaadin.Will] Avoid using 'will'. Raw Output: {"message": "[Vaadin.Will] Avoid using 'will'.", "location": {"path": "articles/flow/integrations/spring/configuration.adoc", "range": {"start": {"line": 229, "column": 105}}}, "severity": "WARNING"}
. If multiple beans remain after this filtering, regular executors are preferred over schedulers
. If Spring's default `applicationTaskExecutor` is among multiple candidates, it is discarded to prefer application-defined beans
. If multiple candidates still remain, Vaadin will throw an [classname]`IllegalStateException` with suggestions to resolve the conflict

Check warning on line 232 in articles/flow/integrations/spring/configuration.adoc

View workflow job for this annotation

GitHub Actions / lint

[vale] reported by reviewdog 🐶 [Vaadin.Will] Avoid using 'will'. Raw Output: {"message": "[Vaadin.Will] Avoid using 'will'.", "location": {"path": "articles/flow/integrations/spring/configuration.adoc", "range": {"start": {"line": 232, "column": 47}}}, "severity": "WARNING"}

There are several ways to customize the executor used by Vaadin in your Spring application.
If you define a single [interfacename]`TaskExecutor` bean in your Spring application context, Vaadin will automatically use it.

Check warning on line 235 in articles/flow/integrations/spring/configuration.adoc

View workflow job for this annotation

GitHub Actions / lint

[vale] reported by reviewdog 🐶 [Vaadin.Will] Avoid using 'will'. Raw Output: {"message": "[Vaadin.Will] Avoid using 'will'.", "location": {"path": "articles/flow/integrations/spring/configuration.adoc", "range": {"start": {"line": 235, "column": 102}}}, "severity": "WARNING"}
If you have multiple [interfacename]`TaskExecutor` beans in your application, you can specifically designate one for Vaadin using the `@VaadinTaskExecutor` annotation or you can name your bean `VaadinTaskExecutor`.

[.example]
--

[source,java]
----
<source-info group="Single TaskExecutor Bean"></source-info>
@Bean
public TaskExecutor vaadinExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(50);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("vaadin-custom-executor-");
return executor;
}
----

[source,java]
----
<source-info group="Annotated TaskExecutor Bean"></source-info>
@Bean
@VaadinTaskExecutor
public TaskExecutor vaadinSpecificExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(50);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("vaadin-annotated-executor-");
return executor;
}
----

[source,java]
----
<source-info group="Named TaskExecutor Bean"></source-info>
@Bean("VaadinTaskExecutor")
public TaskExecutor customVaadinExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(50);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("vaadin-named-executor-");
return executor;
}
----

--


== Configure Spring MVC Applications

Expand Down