2017-12-28 10 views
0

내 목표는 웹 서버에서 문서를로드 한 다음 특정 콘텐츠에 대한 DOM을 구문 분석하는 것입니다. DOM을 로딩하는 것이 제 문제입니다.WebEngine 용 작업자 스레드가 완료되지 않는 이유는 무엇입니까?

javafx.scene.web.WebEngine을 사용하려고합니다. 최종 DOM에 영향을 줄 수있는 자바 스크립트 실행을 포함하여 필요한 모든 기능을 수행 할 수있는 것처럼 보입니다.

문서를로드 할 때 RUNNING 상태에서 멈추었으며 WebEngine.getDocument()에서 DOM에 액세스하기 전에 필요하다고 생각되는 SUCCEEDED 상태에 도달하지 못하는 것처럼 보입니다.

이것은 URL 또는 리터럴 콘텐츠 (이 최소 예에서 사용 된)에서로드하는지 여부에 관계없이 발생합니다.

누구나 내가 잘못하거나 오해 한 것을 볼 수 있습니까?

미리 도움을 청하십시오.

import java.util.concurrent.ExecutionException; 
import org.w3c.dom.Document; 
import javafx.application.Platform; 
import javafx.concurrent.Task; 
import javafx.concurrent.Worker; 
import javafx.embed.swing.JFXPanel; 
import javafx.scene.web.WebEngine; 

public class WebEngineProblem { 
    private static Task<WebEngine> getEngineTask() { 
     Task<WebEngine> task = new Task<>() { 
      @Override 
      protected WebEngine call() throws Exception { 
       WebEngine webEngine = new WebEngine(); 
       final Worker<Void> loadWorker = webEngine.getLoadWorker(); 
       loadWorker.stateProperty().addListener((obs, oldValue, newValue) -> { 
        System.out.println("state:" + newValue); 
        if (newValue == State.SUCCEEDED) { 
         System.out.println("finished loading"); 
        }  
       }); 
       webEngine.loadContent("<!DOCTYPE html>\r\n" + "<html>\r\n" + "<head>\r\n" + "<meta charset=\"UTF-8\">\r\n" 
        + "<title>Content Title</title>\r\n" + "</head>\r\n" + "<body>\r\n" + "<p>Body</p>\r\n" + "</body>\r\n" 
        + "</html>\r\n"); 
       State priorState = State.CANCELLED; //should never be CANCELLED 
       double priorWork = Double.NaN; 
       while (loadWorker.isRunning()) { 
        final double workDone = loadWorker.getWorkDone(); 
        if (loadWorker.getState() != priorState || priorWork != workDone) { 
         priorState = loadWorker.stateProperty().getValue(); 
         priorWork = workDone; 
         System.out.println(priorState + " " + priorWork + "/" + loadWorker.getTotalWork()); 
        } 
        Thread.sleep(1000); 
       } 
       return webEngine; 
      } 
     }; 
     return task; 
    } 

    public static void main(String[] args) { 
     new JFXPanel(); // Initialise the JavaFx Platform 
     WebEngine engine = null; 
     Task<WebEngine> task = getEngineTask(); 
     try { 
      Platform.runLater(task); 
      Thread.sleep(1000); 
      engine = task.get(); // Never completes as always RUNNING 
     } 
     catch (InterruptedException | ExecutionException e) { 
      e.printStackTrace(); 
     } 
     // This code is never reached as the content never completes loading 
     // It would fail as it's not on the FX thread. 
     Document doc = engine.getDocument(); 
     String content = doc.getTextContent(); 
     System.out.println(content); 
    } 

} 
+1

작업이 백그라운드 스레드에서 실행되도록 설계되어 있습니다 : 당신은 FX 응용 프로그램 스레드에서이 작업을 실행하는

여기 예를 들어 내가 당신이하려고 생각하고있다. 'SUCCEEDED' 상태 변경은 FX 응용 프로그램 스레드에서도 발생하므로 작업이 완료 될 때까지 상태를 변경할 수 없습니다. while 루프는'loadWorker'가'RUNNING' 상태에서 벗어날 때까지는 사실상 이상한 형태의 교착 상태가 발생할 때까지 완료되지 않을 것입니다. –

+0

확실히,'Platform.runLater() '호출은 작업이 FXApplication 쓰레드에서 실행되도록하는 방법이지만, 작업자 쓰레드는 별개이고 별개입니까? webEngine.loadContent()가 즉시 반환되므로로드가 별도의 작업자 스레드에서 발생해야합니다. "백그라운드 스레드에서 항상 로딩이 발생합니다 백그라운드 작업을 예약 한 후 로딩을 시작하는 메서드가 즉시 반환됩니다 진행률을 추적하거나 작업을 취소하려면 getLoadWorker() 메서드에서 사용할 수있는 Worker 인스턴스를 사용하십시오." https://docs.oracle.com/javase/8/javafx/api/javafx/scene/web/WebEngine.html – Dragonthoughts

+1

작업자 스레드는 분리되어 있지만 별개이지만 작업자의 stateProperty() FX 응용 프로그램 스레드에서 발생합니다. (기본적으로이 속성은 모두 단일 스레드입니다.) 따라서 작업자 스레드의 구현에서 어딘가에 'Platform.runLater (...)'상태를 업데이트합니다. FX 응용 프로그램 스레드를 차단 한 경우 실제로는 호출 할 수 없습니다. (기본적으로 "헤드리스"모드에서 실행 중이더라도 FX 응용 프로그램 스레드를 차단해서는 안됩니다.) –

답변

1

그 노동자는 백그라운드 스레드에서 실행중인 경우에도 불구하고, FX 응용 프로그램 스레드에서 발생하는 Workerstate 특성에 대한 변경. (JavaFX 속성은 본질적으로 단일 스레드입니다.) 웹 엔진의 내용을로드하는 스레드 구현의 어딘가에서 작업자 상태를 변경하는 Platform.runLater(...) 호출이 있습니다.

작업자 상태가 변경 될 때까지 작업이 중단되고 작업이 FX 응용 프로그램 스레드에서 실행되기 때문에 본질적으로 FX 응용 프로그램 스레드에 교착 상태가 발생합니다. 작업자 상태의 변경은 발생할 수 없습니다 작업이 완료 될 때까지 (동일한 스레드에서 실행 중이므로) 상태가 변경 될 때까지 작업을 완료 할 수 없습니다 (작업을 프로그래밍 한대로).

기본적으로 FX 응용 프로그램 스레드를 차단하는 것은 오류입니다. 대신 원하는 조건 (웹 엔진이 생성되고 스레드로드가 완료 됨)이 될 때까지 다른 스레드를 차단 한 다음 수행 할 다음 작업을 수행하십시오 (Platform.runLater(...) 다시 실행해야 할 경우 다시 실행해야 함). FX 응용 프로그램 스레드).

import java.util.concurrent.CountDownLatch; 
import java.util.concurrent.ExecutionException; 
import java.util.concurrent.FutureTask; 

import org.w3c.dom.Document; 

import javafx.application.Platform; 
import javafx.concurrent.Worker; 
import javafx.concurrent.Worker.State; 
import javafx.embed.swing.JFXPanel; 
import javafx.scene.web.WebEngine; 

public class WebEngineProblem { 

    public static void main(String[] args) throws InterruptedException, ExecutionException { 
     new JFXPanel(); // Initialise the JavaFx Platform 

     CountDownLatch loaded = new CountDownLatch(1); 

     FutureTask<WebEngine> createEngineTask = new FutureTask<WebEngine>(() -> { 
      WebEngine webEngine = new WebEngine(); 
      final Worker<Void> loadWorker = webEngine.getLoadWorker(); 
      loadWorker.stateProperty().addListener((obs, oldValue, newValue) -> { 
       System.out.println("state:" + newValue); 
       if (newValue == State.SUCCEEDED) { 
        System.out.println("finished loading"); 
        loaded.countDown(); 
       }  
      }); 
      webEngine.loadContent("<!DOCTYPE html>\r\n" + "<html>\r\n" + "<head>\r\n" + "<meta charset=\"UTF-8\">\r\n" 
       + "<title>Content Title</title>\r\n" + "</head>\r\n" + "<body>\r\n" + "<p>Body</p>\r\n" + "</body>\r\n" 
       + "</html>\r\n"); 
      return webEngine ; 
     }); 

     Platform.runLater(createEngineTask); 
     WebEngine engine = createEngineTask.get(); 
     loaded.await(); 

     Platform.runLater(() -> { 
      Document doc = engine.getDocument(); 
      String content = doc.getDocumentElement().getTextContent(); 
      System.out.println(content); 
     }); 
    } 

} 
+0

고마워, 내 진짜 문제를 해결하는 훌륭한 기반처럼 보입니다. :) – Dragonthoughts