Vaadin 8:每1分钟从服务器重新加载网格数据

SDS*_*SDS 3 java refresh polling vaadin vaadin8

我正在尝试为网格提供自动刷新功能,基本上每隔n秒用服务器的最新数据更新网格.

每当用户启用自动刷新时,我就能够实现PollListner.

                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);
                }
Run Code Online (Sandbox Code Playgroud)

使用上面的代码,我每次启用autorefresh时都会添加PollListener,并在禁用时将其删除.

我在这里发现了类似的问题我在这里找到了类似的问题VAADIN 7:以5分钟为间隔刷新Vaadin View的最简单方法是什么?

但是想知道是否有更好的方法来实现简单的用例AutoRefresh UI?应该在哪里实施PollListener?我想为视图创建一次PollListener,并且只要每次用户更改刷新率时更新PollInterval.

有关哪种方法更好的指针还是Vaadin有一个全新的概念来实现这一目标?

TIA

Bas*_*que 7

请参阅LeifÅstrand正确答案.我将添加一些讨论,以及使用Polling和Push的完整示例应用程序.

Vaadin 8有两种方法可以在用户不做手势的情况下自动更新信息显示:轮询和推送.

轮询

在Vaadin 8的轮询功能中,您可以在子类上设置一个毫秒的轮询间隔UI.-1禁用轮询的默认值.

myUI.setPollInterval( ( int ) TimeUnit.MINUTES.toMillis( 1 ) );  // Specify milliseconds for polling interval.
Run Code Online (Sandbox Code Playgroud)

启用后,用户Web浏览器中安装的Vaadin JavaScript库将检入Vaadin服务器.作为a PollNotifier,UI签入导致在服务器端触发事件.

如果定义实现PollListener接口的类,则实例将poll调用其方法.

注册后PollListener.找回一个Registration物体.该对象提供了remove一种取消注册侦听器的方法(如果需要).

您可以选择定义PollListener使用lambda语法,匿名内部类或单独定义的类.

Registration registration = this.addPollListener( new UIEvents.PollListener() {
    @Override
    public void poll ( UIEvents.PollEvent pollEvent ) {
        System.out.println( "TRACE - PollListener::poll running. " + Instant.now() );
        …
    }
} );
Run Code Online (Sandbox Code Playgroud)

或者,lambda语法:

Registration registration = this.addPollListener( ( UIEvents.PollListener ) pollEvent -> {
    System.out.println( "TRACE - PollListener::poll running. " + Instant.now() );
    …
} );
Run Code Online (Sandbox Code Playgroud)

在此调用期间,您的代码可以Runnable在您方便的时间使用您的UI子类注册a .

这样Runnable做可以更新UI子类中包含的小部件.切记永远不要从后台线程访问或修改小部件.你可能会侥幸逃脱,或者你可能会发生可怕的事情.安全:始终调用UI::access以通过Runnable访问小部件的访问权限.这Runnable将在您的Web应用程序的主用户界面线程上运行,该线程负责您的UI子类实例.

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.
                    }
                } );
Run Code Online (Sandbox Code Playgroud)

优点

使用轮询功能的好处是你必须做的编程比Push(下面讨论的)更简单.在了解自动非用户生成的更新时,轮询可能是更好的选择.

一个简单的方面是UI子类的每个实例都负责自己的轮询,选择是否以及何时进行轮询并控制轮询的频率.每个UI子类实例都调用自己的setPollInterval方法.更多的轮询可能对用户来说很好,但是这种混乱会增加网络流量,从而使您的网络管理员处于胡思乱想状态.因此,您可以通过UI子类实例调整频率.请记住,每个用户不仅拥有自己的UI子类实例,而且Vaadin 8还具有多窗口/选项卡应用程序.每个Web浏览器中的一个Web应用程序可以打开多个窗口/选项卡,每个窗口/选项卡都运行相同或不同UI子类的自己的实例.

缺点

美学上的一个缺点是轮询破坏了HTTP设计的请求 - 响应优雅.虽然这是我的一个宠儿,但很久以前这艘船已经航行了,所以我不会在这里浪费大量的时间来使用文档传送系统作为交互式客户端 - 服务器应用程序架构.

更实际的缺点是网络上不必要的流量.如果您能够使用Push via WebSocketWebpush,则在客户端和服务器之间保持开放连接,始终保持很少的流量,直到服务器生成要传送给客户端的事件.但请注意,WebSocket很容易被防火墙和代理所击败,而Webpush可能无法实现/支持,在这种情况下,Vaadin中的Push实现(async-io.orgAtmosphere Framework库)可能会回归到轮询技术.

另一个缺点是每个客户端执行自己的重复轮询的效率低下,并且每个客户端在服务器端触发单独的执行,例如在数据库中搜索新数据.如果您有许多客户端都使用同一组不可变对象,则Push可以更高效地执行单个搜索新数据并将相同数据对象集传递给所有客户端.

Vaadin与Atmosphere的结合(上面链接)极大地简化了在您的Web应用程序中使用Push技术.然而,与轮询功能相比,移动部件更复杂一些.

首先,@PushUI子类上启用带有注释的Push .

然后使用a计划每分钟触发事件ScheduledExecutorService.用a设置执行者ServletContextListener.请参阅下面的示例代码.

优点

如上所述,就能够使用WebSocket技术或Webpush的网络流量而言,推送可以非常有效.

缺点

不幸的是,WebSocket可以被防火墙和代理打败.Webpush是新的,可能无法得到广泛支持.在这种情况下,Vaadin/Atmosphere可能会退回到使用轮询方法.

另一个缺点是编码有点复杂.对这项工作不熟悉的程序员可能需要一段时间才能掌握各种动作.

  • 您需要在服务器端使用后台线程来跟踪时间,在我们的情况下每分钟都会触发一次.现代的方法是使用a ScheduledExecutorService来处理线程和射击时间表.
  • 要设置执行程序服务,您需要实现ServletContextListener如下所述的操作.

请注意,某些推送方法(尤其是WebSocket)涉及维护开放式网络连接.因此,这会消耗服务器计算机上的端口号等资源.

示例应用

我使用Vaadin 8.6beta1构建了一个完整的工作示例应用程序.此应用程序支持两种轮询和推送.不确定你是否会在真正的网络应用程序中混合使用,但也许.

访问我的Google云端硬盘上的主要文件.添加到vaadin-archetype-application由Vaadin Ltd.提供的Maven原型创建的项目中.

警告:这个例子在几天内拼凑在一起兼职.因此,它可能是也可能不是生产就绪代码,可能会也可能不会显示正确的技术.但希望这将有助于引导新手.

警告:我不是这个领域的专家.所以我上面的所有讨论和我的示例代码在这里用一粒盐.做自己的研究和学习.

此应用程序允许您通过单选按钮启用和禁用每种方法.您还可以通过单击立即手动刷新按钮强制立即刷新.

在此输入图像描述

绿色阴影表示自上次刷新以来更改的值.

您可以运行多个窗口.根据您的单选按钮设置,观看它们一起更新或单独更新或全部更新.

在此输入图像描述

数据库

这个示例应用程序的主要思想是模拟一个数据库,维护大约十件设备/进程/人/任何东西的当前状态.每个状态由数字1-10标识.每个状态都具有十个值的域,1-9.每个状态记录上次更新的时刻.

这十个状态记录在Vaadin Grid小部件中显示为行.

所有这些数据都记录在关系数据库H2数据库引擎中.作为演示,我们不需要持久性,因此数据库在内存中.后台线程随机更新数据库中的状态行.

MyDbService.java

这个数据库服务代码建立了我们的内存H2数据库,定义了Status的表,并填充了十行.该类还可以随机更新某些行的值.您可以要求检索表示当前存储值ListStatus对象.

Status.java

每个状态记录都由Java表示Status,一个简单的POJO.

生命周期

Vaadin基于Java Servlet技术.你的Vaadin应用程序是一个很大的Servlet实现.作为servlet,它响应用户的Web浏览器的传入请求.

在第一个传入请求之前,我们需要做一些设置工作.首先,我们需要使用我们的十个状态记录来建立和填充该数据库.

Servlet规范要求所有Web容器都支持该ServletContextListener接口.如果您编写实现该接口的类,并将其声明为Web容器,则将在第一个请求之前和最后一个请求之后调用它.

在我们的示例中,我们使用该钩子来建立数据库.我们还设置了一个后台线程,随机更改我们存储的状态记录,以模拟用户的更新或来自Feed的新数据.

上下文监听器

这是我们的例子ServletContextListener.

向Web容器声明其存在的最简单方法是通过@WebListener注释,但您可以根据需要在部署方案中选择其他路由.

@WebListener
public class MyServletContextListener implements ServletContextListener {
…
Run Code Online (Sandbox Code Playgroud)

MyUI.java

入口点进入这个Vaadin Web应用程序是我们的子类UI,MyUI.java.它有两个工作:(a)在屏幕上获取我们的用户界面内容,以及(b)将自身注册为PollListener对轮询更新做出反应.

DataDisplayLayout.java

这是我们的用户界面内容.这是此示例应用程序的核心.它显示Vaadin网格,其显示将使用新数据进行更新.

DataDisplayLayoutRefreshManager.java

该经理监督pub-sub(发布 - 订阅)模型,即注册我们DataDisplayLayout希望通过Push更新的实例.

这里使用一组弱引用来跟踪订户.因此,订阅DataDisplayLayout实例可以优雅地通知他们不再需要更新的愿望,或者实例可以简单地超出范围以最终作为订阅者被丢弃.

轮询方法不需要此管理器,因为我们的UI子类(MyUI)的每个实例都单独轮询服务器.

mytheme.scss

通过CSS设置Vaadin网格中表示新值的单元格的绿色.在Vaadin 8中,我们通过编辑mytheme.scss隐藏在项目webapp文件夹中的文件来完成此操作.

这里我们定义样式名称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;
  }
}
Run Code Online (Sandbox Code Playgroud)

我们必须通过实现样式生成器将该样式名称分配给我们的Vaadin Grid行.

this.grid.setStyleGenerator( ( StyleGenerator ) o -> {
    Status s = ( Status ) o;
    if ( s.getUpdated().isAfter( this.whenRowLastUpdated ) ) {
        return "fresh_row";
    } else {
        return null;
    }
} );
Run Code Online (Sandbox Code Playgroud)