I am trying to have a auto refresh feature for the grid which basically, updates the grid with latest data from the server every 'n' seconds.
I was able to implement the PollListner whenever the user enables Auto-Refresh.
UI ui= TestUI.getCurrent();
Boolean value = isRefreshChkBox.getValue();
PollListener listener = e -> {
explorer.reloadUI();
};
if (value) {
String refreshRateValue = refreshRateTxtField.getValue();
int refreshRate = Integer.valueOf(refreshRateValue);
int millis = (int) TimeUnit.SECONDS.toMillis(refreshRate);
absUI.setPollInterval(millis);
absUI.addPollListener(listener);
} else {
absUI.setPollInterval(-1);
absUI.removePollListener(listener);
}
With the above code, I add PollListener everytime autorefresh is enabled and I remove it on disable.
I found similar question here VAADIN 7: What is the simplest way to refresh a Vaadin View in 5 minute intervals?
But what I want to understand, is there a better approach to achieve a simple usecase AutoRefresh UI?? where should PollListener be implemented?? I thought of creating PollListener once for the view and just update the PollInterval everytime user changes the refresh rate.
Any pointers on which approach is better or is there completely new concept in Vaadin to achieve this?
TIA
See the correct Answer by Leif Åstrand. I will add a bit of discussion, and a complete example app using both Polling and Push.
Vaadin 8 has two ways to automatically update the display of information without the user making a gesture: Polling & Push.
In Vaadin 8’s Polling feature, you set a polling interval of milliseconds on your UI
subclass. The default value of -1
disables Polling.
myUI.setPollInterval( ( int ) TimeUnit.MINUTES.toMillis( 1 ) ); // Specify milliseconds for polling interval.
When enabled, the Vaadin JavaScript library installed in the user’s web browser checks in with the Vaadin server. Being a PollNotifier
, the UI checking-in causes an event to be fired on the server-side.
If you define a class that implements the PollListener
interface, your instance will have its poll
method invoked.
Upon registering your PollListener
. get back a Registration
object. That object provides a remove
method to unregister your listener, if need be.
You have your choice of defining your PollListener
using lambda syntax, an anonymous inner class, or a separately-defined class.
Registration registration = this.addPollListener( new UIEvents.PollListener() {
@Override
public void poll ( UIEvents.PollEvent pollEvent ) {
System.out.println( "TRACE - PollListener::poll running. " + Instant.now() );
…
}
} );
Or, lambda syntax:
Registration registration = this.addPollListener( ( UIEvents.PollListener ) pollEvent -> {
System.out.println( "TRACE - PollListener::poll running. " + Instant.now() );
…
} );
During this invocation, your code can register a Runnable
to be invoked at a convenient time with your UI
subclass.
That Runnable
does the work of updating widgets contained in your UI
subclass. Remember to never access or modify widgets from a background thread. You may get away with it, or you may cause terrible things to happen. Be safe: Always call UI::access
to pass a Runnable
that accesses the widgets. That Runnable
will be run on the main user-interface thread of your web app, the thread in charge of your UI
subclass instance.
getUI().access( new Runnable() {
@Override
public void run ( ) {
subscriber.refresh( new ArrayList <>( statusList ) ); // Copy the list in case the `Grid` modifies it, such as sorting.
}
} );
The upside of using the Polling feature is that the programming you must do is simpler than with Push
(discussed below). Polling is likely a better route to take when learning about automated non-user-generated updates.
One simple aspect is that each instance of your UI
subclass is in charge of its own polling, choosing if and when to do polling and controlling how often to poll. Each UI
subclass instance calls its own setPollInterval
method. More polling may be nice for the user, but the chattiness increases network traffic, thereby making your network admin cranky. So you can tune the frequency by UI
subclass instance. Remember that not only does each user have their own UI
subclass instance, but also, Vaadin 8 is capable of multi-window/tab apps. One web app in each web browser can have multiple windows/tabs open, each running their own instance of the same or different UI
subclasses.
One downside aesthetically is that polling breaks the request-response elegance of the HTTP design. While this is a pet-peeve of mine, that ship has sailed long ago, so I'll not waste bytes here ranting about using a document-delivery system as an interactive client-server app architecture.
A more practical downside is unnecessary traffic on the network. If you are able to use Push via WebSocket or Webpush, then an open connection is maintained between client and server with very little traffic all the while until the server generates an event to be communicated to the client. But be aware that WebSocket is easily defeated by firewalls & proxies, and Webpush may not be implemented/supported, in which case the Push implementation in Vaadin (the Atmosphere Framework library by async-io.org) may fall back to polling techniques.
Another downside is inefficiency of each client doing its own repeated polling and each triggering a separate execution on the server-side such as the search for fresh data in the database. If you have many clients all consuming the same set of immutable objects, then Push can be more efficient doing a single search for fresh data and delivering the same bunch of data objects to all the clients.
The combination of Vaadin with Atmosphere (linked above) vastly simplifies using Push technology in your web app. Nevertheless, it is a bit more complicated with more moving parts than seen with the Polling feature.
Firstly, enable Push with the @Push
annotation on your UI
subclass.
Then schedule the firing of an event every minute using a ScheduledExecutorService
. Set up that executor with a ServletContextListener
. See example code below for all this.
Push can be quite efficient in terms of network traffic able to use WebSocket technology or Webpush, as mentioned above.
Unfortunately WebSocket can be defeated by firewalls & proxies. And Webpush is new and may not be widely supported. In this case, Vaadin/Atmosphere may fall-back to using a polling approach.
Another downside is that the coding is a bit more complex. A programmer new to this work may take a while to grasp the various moving pieces.
ScheduledExecutorService
to handle the threading and firing schedule. ServletContextListener
as discussed below. Be aware that some of the push approaches, especially WebSocket, involve maintaining an open network connection. So this consumes resources such as port numbers on your server machine.
I built a complete working example app using Vaadin 8.6beta1. This app supports both Polling and Push. Not sure if you would ever mix both in a real web app, but perhaps.
Access the main files on my Google Drive. Add to a project created via the Maven archetype vaadin-archetype-application
provided by Vaadin Ltd.
Caveat: This example was cobbled together part-time over days. So it may or may not be production-ready code, and may or may not show proper technique. But hopefully it will help to guide a newbie.
Caveat: I am not an expert in this arena. So take all my discussion above and my example code here with a grain-of-salt. Do your own research and study.
This app allows you to enable and disable each approach via radio buttons. You can also force an immediate refresh by clicking the Refresh manually now button.
The green-shading indicates changed values since the last refresh.
You can run multiple windows. Watch them update together or separately or not all, depending on your radio button settings.
The main idea of this example app is to simulate a database maintaining a current status of some ten pieces of equipment/processes/people/whatever. Each status in identified by a number 1-10. Each has a status with a domain of ten values, 1-9. And each status records the moment it was last updated.
These ten status records are displayed as rows in a Vaadin Grid widget.
All this data is recorded in a relational database, the H2 Database Engine. As a demo, we’ve no need for persistence, so the database is in-memory. A background thread randomly updates the status rows in the database.
MyDbService.java
This database-service code establishes our in-memory H2 database, defining the table for our Status, and populating ten rows. This class also can randomly update the value of some of the rows. And you can ask to retrieve a List
of Status
objects representing the currently stored values.
Status.java
Each status record is represented in Java by the Status
class, a simple POJO.
Vaadin is based on Java Servlet technology. Your Vaadin app is one big Servlet implementation. As a servlet, it responds to incoming requests by the users’ web browsers.
Before that first incoming request, we need to do some set-up work. For one thing, we need to establish and populate that database with our ten status records.
The Servlet specification requires all web containers to support the ServletContextListener
interface. If you write a class implementing that interface, and declare it to the web container, then it will be invoked before the first request and after the last request.
In our example, we use that hook to establish the database. We also set up a background thread that randomly changes our stored status records to simulate either users’ updates or fresh data from a feed.
Here is our example ServletContextListener
.
The easiest way to declare its presence to our web container is by the @WebListener
annotation but you can choose other routes as needed in your deployment scenario.
@WebListener
public class MyServletContextListener implements ServletContextListener {
…
MyUI.java
The entry point into this Vaadin web app is our subclass of UI
, MyUI.java
. It has two jobs: (a) Get our user-interface content on-screen, and (b) Register itself as a PollListener
to react to polling updates.
DataDisplayLayout.java
Here is our user-interface content. This is the centerpiece of this example app. It displays the Vaadin Grid whose display is to be updated with fresh data.
DataDisplayLayoutRefreshManager.java
This manager oversees the pub-sub (Publish-Subscribe) model of signing up instances of our DataDisplayLayout
that want to be updated via Push.
A collection of weak references are used here to track the subscribers. So the subscribing DataDisplayLayout
instance can gracefully notify of their desire to no longer be updated, or the instance can simply go out-of-scope to eventually be dropped as a subscriber.
The Polling approach does not need this manager, as each instance of our UI
subclass (MyUI
) is individually polling the server.
mytheme.scss
The green coloring of the cell in the Vaadin Grid denoting a fresh value is set via CSS. In Vaadin 8, we do this by editing the mytheme.scss
file found buried in your project’s webapp
folder.
Here we define the style name fresh_row
.
@import "../valo/valo.scss";
@mixin mytheme {
@include valo;
// Insert your own theme rules here
.v-grid-row.fresh_row > td:nth-child(2) {
background-color: honeydew;
}
}
We must assign that style name to our Vaadin Grid rows by implementing a style generator.
this.grid.setStyleGenerator( ( StyleGenerator ) o -> {
Status s = ( Status ) o;
if ( s.getUpdated().isAfter( this.whenRowLastUpdated ) ) {
return "fresh_row";
} else {
return null;
}
} );
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With