The good news is that what you want, to have a widget in your Vaadin user-interface later updated by work done in the background on the server without blocking the UI's responsiveness to the user, can be done. It can be done very well with Vaadin and its Java-based backend.
The bad news is that if you are new to concurrency and threading, you have a learning curve to climb.
Asynchronous
The technical term for wanting your app to do something in the background and check back later without blocking is: asynchronous update.
We can accomplish this in Java using threads. Spawn a thread to run your SQL service code. When that code finishes the database work, that code posts a request by calling UI::access(Runnable runnable)
to have the original user-interface (UI) thread update the Label
widget.
Push technology
As discussed in the Answer by Lund updating of the Label
widget requires Push Technology to update the browser from a server-side generated event. Fortunately, Vaadin 8 and later has excellent support for Push and makes instituting Push in your app extraordinarily easy.
Tip: Push in general, and WebSocket especially, has evolved greatly in recent years. Using the latest generation of Servlet container will improve your experience. For example, if using Tomcat I recommend using the latest version of Tomcat 8.5 or 9.
Threads
Java has excellent support for threading. Much of the necessary work is handled for you by the Executor framework built into Java.
If you are new to threading, you have some serious learning ahead of you. Start by studying the Oracle Tutorial on concurrency. Eventually you'll need to read and re-read a few times the excellent book Java Concurrency in Practice by Brian Goetz et al.
ServletContextListener
You will likely want to set-up and tear-down your thread-juggling executor service as your Vaadin app launches and exits. The way to do that is to write a class separate from your Vaadin servlet class. This class must implement the ServletContextListener
. You can easily do that by implementing the two required methods, and annotating the class with @WebListener
.
Be sure to tear-down the executor service. Otherwise the background threads it manages may survive the shutdown of your Vaadin app and possibly even the shutdown of your web container (Tomcat, Jetty, etc.), continuing to run indefinitely.
Never access widget from background thread
A key idea in this work: Never ever access any Vaadin UI widget directly from any background. Do not call any methods on the UI widgets, nor access any values, from any widget in code running in a background thread. The UI widgets are not thread-safe (making a user-interface technology thread-safe is terribly difficult). You might get away with such a background call, or terrible things may happen at runtime.
Java EE
If you happen to be using a full-blown Jakarta EE (formerly known as Java EE) server rather than either a web container (such as Tomcat or Jetty) or a Web Profile server (such as TomEE), then the work discussed above with the executor service and ServletContextListener
is done for you. Use the features defined in Java EE 7 and later: JSR 236: Concurrency Utilities for JavaTM EE
Spring
Your Question is tagged with Spring. Spring may have features to help with this work. I don’t know, as I am not a Spring user. Perhaps Spring TaskExecutor?
Search Stack Overflow
If you search Stack Overflow you will find that all these topics have been addressed.
I have already posted two full example apps demonstrating Vaadin with Push:
- A contrived minimalist example, just to give you a taste of what is involved. That particular approach should never be used in real work, as noted in that Answer.
- For real work, see a more complicated example app posted in this Answer.
Complete example
Start with the Vaadin 8.4.3 app generated by the Maven archetype, vaadin-archetype-application
provided by the Vaadin Ltd. company.
package com.basilbourque.example;
import javax.servlet.annotation.WebServlet;
import com.vaadin.annotations.Theme;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.ui.Button;
import com.vaadin.ui.Label;
import com.vaadin.ui.TextField;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
/**
* This UI is the application entry point. A UI may either represent a browser window
* (or tab) or some part of an HTML page where a Vaadin application is embedded.
* <p>
* The UI is initialized using {@link #init(VaadinRequest)}. This method is intended to be
* overridden to add component to the user interface and initialize non-component functionality.
*/
@Theme ( "mytheme" )
public class MyUI extends UI {
@Override
protected void init ( VaadinRequest vaadinRequest ) {
final VerticalLayout layout = new VerticalLayout();
final TextField name = new TextField();
name.setCaption( "Type your name here:" );
Button button = new Button( "Click Me" );
button.addClickListener( e -> {
layout.addComponent( new Label( "Thanks " + name.getValue() + ", it works!" ) );
} );
layout.addComponents( name , button );
setContent( layout );
}
@WebServlet ( urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true )
@VaadinServletConfiguration ( ui = MyUI.class, productionMode = false )
public static class MyUIServlet extends VaadinServlet {
}
}
As discussed above, we need to assign your SQL-service work as a task to be done on a background thread. The Executor framework in Java 5 and later does all the heavy-lifting for such thread work. We need to establish an executor service backed by a thread pool to do all the updating of all the new labels being added on all the users’ web browser windows. The question is where do we setup, store, and teardown that executor service object?
We want the executor service to be available for the entire lifecycle of our web app. Before the first user request arrives to our freshly-launched web app, we want to setup the executor service. And when we are trying to shutdown our web app, we need to teardown that executor service, so that the threads in its backing thread pool are terminated. How do we tie into the lifecycle of our Vaadin web app?
Well, Vaadin is an implementation of Java Servlet, albeit a very large and sophisticated servlet. In Servlet terminology, your web app is known as a “context”. The Servlet specification requires that all Servlet containers (such as Tomcat, Jetty, etc.) notice any class marked as a listener to certain events. To take advantage of this we must add another class to our Vaadin app, a class implementing the ServletContextListener
interface.
If we annotate our new class as @WebListener
, the Servlet container will notice this class, and when launching our web app will instantiate our listener object, and then call its methods at the appropriate times. The contextInitialized
method is called after the servlet has been properly initialized but before any incoming web browser requests have been handled. The contextDestroyed
method is called after the last web browser request has been handled, after that last response has been sent back to the user.
So our class implementing the ServletContextListener
is the perfect place to setup and teardown our executor service with its backing thread pool.
One more problem: After setting up our executor service, where do we store a reference to be found and used later in our Vaadin servlet when the users are adding their Label
objects? One solution is to store the executor service reference as an “attribute” in the “context” (our web app). The Servlet spec requires that every Servlet container provide each context (web app) with a simple key-value collection where the key is a String
object and the value is an Object
object. We can invent some string to identify our executor service, and then our Vaadin servlet can later do a loop-up to retrieve the executor service.
Ironically enough, that discussion above is longer than the actual code!
package com.basilbourque.example;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@WebListener
// Annotate to instruct your web container to notice this class as a context listener and then automatically instantiate as context (your web app) lanuches.
public class MyServletContextListener implements ServletContextListener {
static final public String executorServiceNameForUpdatingLabelAfterSqlService = "ExecutorService for SQL service update of labels";
@Override
public void contextInitialized ( final ServletContextEvent sce ) {
// Initialize an executor service. Store a reference as a context attribute for use later in our webapp’s Vaadin servlet.
ExecutorService executorService = Executors.newFixedThreadPool( 7 ); // Choose an implementation and number of threads appropriate to demands of your app and capabilities of your deployment server.
sce.getServletContext().setAttribute( MyServletContextListener.executorServiceNameForUpdatingLabelAfterSqlService , executorService );
}
@Override
public void contextDestroyed ( final ServletContextEvent sce ) {
// Always shutdown your ExecutorService, otherwise the threads may survive shutdown of your web app and perhaps even your web container.
// The context addribute is stored as `Object`. Cast to `ExecutorService`.
ExecutorService executorService = ( ExecutorService ) sce.getServletContext().getAttribute( MyServletContextListener.executorServiceNameForUpdatingLabelAfterSqlService );
if ( null != executorService ) {
executorService.shutdown();
}
}
}
Now, back to our Vaadin app. Modify that file:
- Annotate the Servlet with
@Push
to engage Vaadin’s ability to have a server-side generated event update the user-interface widgets.
- Modify the creation of each
Label
.
- Change the initial text for the
Label
to include "Created: " with current date-time.
- Move the instantiation to its own line.
- Add behavior so that after instantiating a new
Label
, we retrieve our executor service from the context attribute collection, and submit to it a Runnable
that will eventually be run to do our SQL service. To simulate the work of that SQL service, we sleep the background thread random number of seconds under half a minute. Upon waking, that background thread asks our UI
object representing our web app’s content displayed in the web browser to schedule yet another Runnable
to eventually be run on its main user-interface thread. As discussed above, never directly access a UI widget from a background thread! Always politely ask the UI
object to schedule widget-related work on its own timetable in its own thread.
If you are new to threading and concurrency, this may be daunting. Study this code, and it some time to sink in. You could structure this in other manners, but I wanted to make it simple here for teaching purposes. Focus not on the structure/arrangement of the code but on the idea of:
- User clicks button, an event in main Vaadin UI thread.
- Code on the button submits to an executor service a task (a
Runnable
) to be run later in a background thread.
- That background thread, when eventually run, calls on your SQL service to get some work done. When done, we post a request (another
Runnable
) to the UI to do some widget-related work (our Label
text updating) on our behalf.
- When convenient to the UI, when it is not too busy handling other user-related events generated in the user-interface, the UI gets around to running our
Runnable
to actually modify the text of the Label
that was added a while ago.
Here is our modified Vaadin app.
package com.basilbourque.example;
import javax.servlet.ServletContext;
import javax.servlet.annotation.WebServlet;
import com.vaadin.annotations.Push;
import com.vaadin.annotations.Theme;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.ui.Button;
import com.vaadin.ui.Label;
import com.vaadin.ui.TextField;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
/**
* This UI is the application entry point. A UI may either represent a browser window
* (or tab) or some part of an HTML page where a Vaadin application is embedded.
* <p>
* The UI is initialized using {@link #init(VaadinRequest)}. This method is intended to be
* overridden to add component to the user interface and initialize non-component functionality.
*/
@Push // This annotation enables the Push Technology built into Vaadin 8.4.
@Theme ( "mytheme" )
public class MyUI extends UI {
@Override
protected void init ( VaadinRequest vaadinRequest ) {
final VerticalLayout layout = new VerticalLayout();
final TextField name = new TextField();
name.setCaption( "Type your name here:" );
Button button = new Button( "Click Me" );
button.addClickListener( ( Button.ClickEvent e ) -> {
Label label = new Label( "Thanks " + name.getValue() + ", it works!" + " " + ZonedDateTime.now( ZoneId.systemDefault() ) ); // Moved instantiation of `Label` to its own line so that we can get a reference to pass to the executor service.
layout.addComponent( label ); // Notes current date-time when this object was created.
//
ServletContext servletContext = VaadinServlet.getCurrent().getServletContext();
// The context attribute is stored as `Object`. Cast to `ExecutorService`.
ExecutorService executorService = ( ExecutorService ) servletContext.getAttribute( MyServletContextListener.executorServiceNameForUpdatingLabelAfterSqlService );
if ( null == executorService ) {
System.out.println( "ERROR - Failed to find executor serivce." );
} else {
executorService.submit( new Runnable() {
@Override
public void run () {
// Pretending to access our SQL service. To fake it, let's sleep this thread for a random number of seconds.
int seconds = ThreadLocalRandom.current().nextInt( 4 , 30 + 1 ); // Pass ( min , max + 1 )
try {
Thread.sleep( TimeUnit.SECONDS.toMillis( seconds ) );
} catch ( InterruptedException e ) {
e.printStackTrace();
}
// Upon waking, ask that our `Label` be updated.
ZonedDateTime zdt = ZonedDateTime.now( ZoneId.systemDefault() );
System.out.println( "Updating label at " + zdt );
access( new Runnable() { // Calling `UI::access( Runnable )`, asking that this Runnable be run on the main UI thread rather than on this background thread.
@Override
public void run () {
label.setValue( label.getValue() + " Updated: " + zdt );
}
} );
}
} );
}
} );
layout.addComponents( name , button );
setContent( layout );
}
@WebServlet ( urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true )
@VaadinServletConfiguration ( ui = MyUI.class, productionMode = false )
public static class MyUIServlet extends VaadinServlet {
}
}
When doing such asynchronous threaded work, the exact order of execution cannot be predicted. You do not know exactly when and in which order the background threads well be executing. You do not know when the UI
object will get to our request to update the Label
object’s text. Notice in this screenshot that while running this app, different Label
objects were updated at different times in an arbitrary order.
Related Questions: