java - 如何将 SimpleTimeLimiter (Guava) 与 Vaadin 一起使用

标签 java java-8 vaadin guava vaadin8

我正在尝试调用 vaadin 框架中运行时间较长的一些代码,这些代码将使用推送更新屏幕,但是如果该过程花费的时间太长,我希望能够取消它。

考虑到这一点,我尝试使用 Guava 的 SimpleTimeLimiter 类,但无论我做什么,我似乎都无法阻止 Vaadin 进程停止。我尝试将 SimpleTimeLimiter 放在 UI.getCurrent().access() 方法内部和外部,但它们都只是继续执行该过程,即使 SimpleTimeLimiter 抛出 TimeoutException。但是,如果我在普通线程中使用相同的代码,它似乎可以工作......

public static void limitExecutionTime(Consumer<UI> lambda)
{
    UI currentUI = UI.getCurrent();

    UI.getCurrent().access(() ->
    {
        try
        {
            SimpleTimeLimiter.create(Executors.newSingleThreadExecutor()).callWithTimeout(new Callable<Void>()
            {
                @Override
                public Void call()
                {
                    // This is needed to deal how Vaadin 8 handles UI's
                    UI.setCurrent(currentUI);
                    lambda.accept();
                    return null;
                }
            }, 1, TimeUnit.SECONDS);

        } catch (TimeoutException | InterruptedException | ExecutionException e) {
            NotificationUtils.showError("Execution took beyond the maximum allowed time.");
            currentUI.push();
        }
    });
}

在上面的代码中,如果该方法花费的时间超过 1 秒,它将抛出 TimeoutException 并显示通知窗口。然而它将继续执行 lambda。

因此,我尝试做相反的事情,并将 UI.getCurrent().access() 放入 public Void call() 方法中,但这有完全相同的结果...

最佳答案

您应该在后台任务准备好使用一些数据更新它之后调用 UI.access。您使用 access 方法在用户正在查看的页面上进行更改。

后台任务执行

在您的示例中,您缺少将任务取消消息传递给 call 方法的方法。为了准备从外部事件取消任务(例如单击取消按钮),您需要在任务内部考虑到这一点。以下示例展示了如何使用 Future.cancel 提供取消方法。

private void onCancelClick(Button.ClickEvent clickEvent) {
    // This method is called from Vaadin UI thread. We will signal
    // background task thread to stop.
    futureResult.cancel(true);
}

在实际任务中,可以通过以下方式处理

private void simulateLongAndSlowCalculation() {
    while (moreWorkTodo) {
        if (Thread.currentThread().isInterrupted()) {
            return;
        }
        try {
            doSomeBlockingCallThatCanBeInterrupted();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return;
        }
    }
}

启动任务和 UI.access

启动任务时, View 应该创建任务并将其提交给执行器服务。

private void onButtonClick(Button.ClickEvent clickEvent) {
    // This runTask creates important link to current UI and the background task.
    // "this" object in these onTask methods is the UI object that we want
    // to update. We need to have someway to pass UI object to background
    // thread. UI.getCurrent() could be a parameter that is passed to the
    // task as well.
    Future<String> futureResult = taskService.runTask(
            this::onTaskDone,
            this::onTaskCancel,
            this::onTaskProgress);
    progressDialog = new ProgressDialog(futureResult);
    progressDialog.show();
}

现在只有当我们想要更新 UI 时才需要 UI.access 方法。在此示例中,可能会在以下情况下发生

  1. 任务成功完成
  2. 任务进度已更新
  3. 任务被取消

请注意,以下所有方法 this 均引用启动任务的 UI 对象。因此,我们正在使用结果更新正确的 UI,而不是其他用户的 UI。

您不需要在代码中调用 UI.setCurrent。

private void onTaskProgress(double progress) {
    logger.info("onTaskProgress: {}", progress);
    access(() -> progressDialog.setProgress(progress));
}

private void onTaskCancel() {
    logger.info("onTaskCancel");
    access(() -> {
        progressDialog.close();
        setResult("Cancelled");
    });
}

private void onTaskDone(String result) {
    logger.info("onTaskDone");
    access(() -> {
        progressDialog.close();
        setResult(result);
    });
}

示例项目

我将另一个项目推送到 github,该项目展示了如何通过取消按钮取消后台任务:

https://github.com/m1kah/vaadin-background-task

编辑:添加了有关后台任务和 UI.access 的部分。更新了示例项目链接到另一个示例。

关于java - 如何将 SimpleTimeLimiter (Guava) 与 Vaadin 一起使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47492364/

相关文章:

java - RxJava2 : Need help to convert java code into rx

java - Java 中的拖放 - 绘制的不仅仅是边框

java - 多线程之间的对象共享

java - 如何使用 groupBy 创建一个值为 BigDecimal 字段平均值的 map ?

exception - Vaadin 7 获取屏幕宽度

javascript - 如何制作宽度为 1px 的 Vaadin 网格列?

java - 为什么 I/o 异常必须用 Buffered Reader 来处理,而 Scanner 则不强制处理?

java - Xml 到 JSON 单值数组处理

java - 如何使用流获取数组列表的特定值

java - Vaadin 8 Grid 不适用于太多条目