Skip to content
Advertisement

Vaadin: Cannot get progress dialog to receive/react when the task completes

I’ve written a small Spring Boot/Vaadin application that displays a simple UI to take user input and make a call to another service that takes some time to run. When the task is submitted, I’m displaying a progress dialog that shows a progress bar, a message informing the user what is going on and a close button to allow them to close the dialog when the job completes. I’m using a ListenableFuture to be notified when the task is done.

I can get the dialog to appear with status of “executing” and the progress bar doing its thing, but when the task is done (I have debug statements going to the console to let me know), it’s not triggering the logic to update the status message and enable the close button. I can’t figure out what I’m doing wrong.

Here’s the code:

MainView1.java

@Route("rescheduleWorkOrders1")
@CssImport("./styles/shared-styles.css")
public class MainView1 extends VerticalLayout {
  ...
  private final BackendService service;

  public MainView1(BackendService service) {
    this.service = service;
    configureView();
    addSubmitButton();
    bindFields();
  }

  private void addSubmitButton() {
    Button submit = new Button("Submit", this::submit);
    add(submit);
  }

  private void submit(ClickEvent<?> event) {
    UserData data = binder.getBean();
    ListenableFuture<ResponseEntity<String>> future = service.executeTask(data);
    ProgressDialog dialog = new ProgressDialog(future);
    dialog.open();
  }

  private void configureView() {
    ...
  }

  private void bindFields() {
    ...
  }    
}

ProgressDialog.java

public class ProgressDialog extends Dialog {
  private final ListenableFuture<ResponseEntity<String>> future;

  private ProgressBar                                    bar;
  private Paragraph                                      message;
  private Button                                         close;

  public ProgressDialog(ListenableFuture<ResponseEntity<String>> future) {
    super();
    this.future = future;
    configureView();

    this.future.addCallback(new ListenableFutureCallback<>() {
      @Override
      public void onSuccess(ResponseEntity<String> result) {
        message.setText("Task complete. Status: " + result.getStatusCode());
        bar.setVisible(false);
        close.setEnabled(true);
      }

      @Override
      public void onFailure(Throwable ex) {
        message.setText(ex.getMessage());
        bar.setVisible(false);
        close.setEnabled(true);
      }
    });
  }

  private void configureView() {
    bar = new ProgressBar();
    bar.setIndeterminate(true);
    bar.setVisible(true);

    message = new Paragraph("Executing task ...");

    close = new Button("Close", this::close);
    close.setEnabled(false);

    add(bar, message, close);
  }

  private void close(ClickEvent<?> event) {
    this.close();
  }
}

BackendService.java

@Service
public class BackendService {

  @Async
  public ListenableFuture<ResponseEntity<String>> executeTask(UserData data) {
    ...
    RestTemplate template = new RestTemplate();
    ResponseEntity<String> response = template.postForEntity(uri, entity, String.class);

    System.out.println(response);
    return AsyncResult.forValue(response);
  }

}

Note: I do have @EnableAsync specified in a @Configuration annotated class.

Advertisement

Answer

When dealing with asynchronous code in Vaadin you need to:

  • Use UI#access when updating the UI outside an active request. This acquires a lock on the UI, to prevent it being updated by two threads simultaneously.
  • Enable server push by adding the @Push annotation to your main layout or view. This allows the server to push updates to the client even if no request is active.

Without the former, you can get ConcurrentModificationExceptions in the best case, and very subtle bugs in the worst.

Without the latter, the changes will be applied (i.e. dialog closed), but the changes will only be sent to the client the next time the client sends a request. I believe this is your main issue.

More information can be found in the documentation.

User contributions licensed under: CC BY-SA
1 People found this is helpful
Advertisement