Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vaadin 8 : reload grid with data from server every 1min

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

like image 535
SDS Avatar asked Aug 10 '18 22:08

SDS


1 Answers

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.

Polling

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.
                    }
                } );

Pros

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.

Cons

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.

Push

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.

Pros

Push can be quite efficient in terms of network traffic able to use WebSocket technology or Webpush, as mentioned above.

Cons

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.

  • You need a background thread on the server-side to track the time, in our case firing every minute. The modern approach to that is using a ScheduledExecutorService to handle the threading and firing schedule.
  • To setup that executor service, you will need to implement a 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.

Example app

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.

enter image description here

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.

enter image description here

Database

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.

Lifecycle

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.

Context listener

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;
    }
} );
like image 62
Basil Bourque Avatar answered Oct 21 '22 15:10

Basil Bourque