Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8244588

Update callback in snapshot method

XMLWordPrintable

    • Icon: Enhancement Enhancement
    • Resolution: Unresolved
    • Icon: P4 P4
    • tbd
    • jfx14
    • javafx
    • None
    • generic
    • generic

      There are 2 identical snapshot methods in Node and Scene:

      1. snapshot​(SnapshotParameters params,
      WritableImage image)
      2. snapshot​(Callback<SnapshotResult,​Void> callback,
      SnapshotParameters params,
      WritableImage image)

      If, for any reason, the snapshot capture fails, the exception is thrown in the first method. This makes it easier to catch and recover, if required.

      While in the second snapshot method, the exception doesn't bubble up and there is no way to recover from the failure. The exception caused is caught in [Scene#addSnapshotRunnable|https://github.com/openjdk/jfx/blob/038556390ef30c912139fcb828ee4a41d8c5abc8/modules/javafx.graphics/src/main/java/javafx/scene/Scene.java#L1438] and callback is never called. This leaves the caller with no feedback and no scope for recovery measures.

      ** Reproducible Sample **

      import javafx.application.Application;
      import javafx.beans.value.ChangeListener;
      import javafx.beans.value.ObservableValue;
      import javafx.concurrent.Worker;
      import javafx.scene.Scene;
      import javafx.scene.web.WebView;
      import javafx.stage.Stage;

      public class SnapshotFailure extends Application {

          private WebView webView;

          @Override
          public void start(Stage stage) {
              webView = new WebView();
              Scene scene = new Scene(webView);
              stage.setScene(scene);
              webView.getEngine().getLoadWorker().stateProperty().addListener(stateListener);
              webView.getEngine().loadContent("<html><h1>test</h1></html>");
              stage.show();
          }

          private final ChangeListener<Worker.State> stateListener = new ChangeListener<>() {
              @Override
              public void changed(ObservableValue<? extends Worker.State> ov, Worker.State oldState, Worker.State newState) {
                  // Sized intentionally too large; triggers NPE
                  webView.setPrefSize(100000, 100000);
                  webView.setMinSize(100000, 100000);
                  webView.autosize();

                  if (newState == Worker.State.SUCCEEDED) {

                      // ATTEMPT #1: Catchable, but NOT pulse-safe
                      System.out.println("======= SNAPSHOT #1 CATCHABLE =======");
                      try {
                          System.out.println("Starting snapshot...");
                          webView.snapshot(null, null);
                          System.out.println("Snapshot succeeded!");
                      } catch (Throwable t) {
                          System.out.println("Snapshot failed");
                      }

                      // ATTEMPT #2: Pulse-safe but NOT catchable
                      System.out.println("======= SNAPSHOT #2 UN-CATCHABLE =======");
                      try {
                          System.out.println("Starting snapshot...");
                          webView.snapshot(snapshotResult -> {
                              try {
                                  if (snapshotResult != null) {
                                      System.out.println("Snapshot succeeded!");
                                  } else {
                                      System.out.println("Snapshot failed");
                                  }
                              } catch (Exception e) {
                                  System.out.println("Snapshot failed");
                              }
                              return null;
                          }, null, null);
                      } catch (Throwable t) {
                          System.out.println("Snapshot failed");
                      }
                  }
              }
          };
      }

      ** Solution **

      Catch the exception in the snapshot method and call the callback with null. This will trigger the if..else block of case 2 in the above sample and is helpful in providing some feedback to the caller.

      A better way to deal with this (would require a lot of change) would be to swap the Callback with a StatusListener which can be updated depending on the status of the snapshot.

            abagarwal Abhinay Agarwal
            abagarwal Abhinay Agarwal
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated: