JavaFX WebEngine等待ajax完成

War*_*kst 5 ajax jquery javafx javafx-webengine

我正在JavaFX中开发一个依赖于WebView(以及WebEngine)的数据挖掘应用程序.挖掘分两步进行:首先,用户使用UI导航到WebView中的网站,以配置可以搜索有趣数据的位置.其次,使用定期运行的后台任务,WebEngine加载相同的文档并尝试从加载的文档中提取数据.

这适用于大多数情况,但最近我遇到了使用AJAX呈现内容的页面遇到的麻烦.要检查是否已WebEngine加载文档,我听loadWorkerstateProperty.如果状态转换为successcesfull,我知道文档已加载(与可能在document.ready()上运行的任何javascript或等效文件一起).这是因为如果我没有弄错的话,javascript会在JavaFX线程上执行(来源:https://blogs.oracle.com/javafx/entry/communicating_between_javascript_and_javafx).但是,如果启动了AJAX调用,则javascript执行完成,引擎让我知道文档已准备就绪,但显然不是因为优秀的AJAX调用内容可能仍然会发生变化.

有没有办法解决这个问题,注入一个钩子,以便在AJAX调用结束时通知我?我已经尝试安装一个默认的完整处理程序,$.ajaxSetup()但这很狡猾,因为如果ajax调用覆盖整个处理程序,则不会调用默认值.另外,我只能在首次加载文档后注入它(然后一些AJAX调用可能已经在运行).我已经使用upcall测试了这个注入,它适用于在命令上启动的AJAX调用(在注入默认处理程序之后),它们不提供自己的完整处理程序.

我正在寻找两件事:第一:挂钩到AJAX调用的完成处理程序的通用方法,其次:等待WebEngine完成所有AJAX调用并在事后通知我的方法.

Mig*_*olm 5

说明

我也有这个问题并通过提供我自己的实现来解决它sun.net.www.protocol.http.HttpURLConnection,我用它来处理任何AJAX请求.我的类,方便地调用AjaxHttpURLConnection,挂钩到getInputStream()函数,但不返回其原始输入流.相反,我给了一个PipedInputStream回到的实例WebEngine.然后,我读取来自原始输入流的所有数据,并将其传递给我的管道流.这样,我获得了2个好处:

  1. 我知道何时收到了最后一个字节,因此完全处理了AJAX请求.
  2. 我甚至可以抓取所有传入的数据并且已经使用它(如果我想).


首先,您必须告诉Java使用URLConnection实现而不是默认实现.为此,您必须提供自己的版本URLStreamHandlerFactory.你可以在这里找到很多关于SO(例如这一个)或通过Google的主题.要设置工厂实例,请在main方法的早期将以下内容放在某处.这就是我的样子.

import java.net.URLStreamHandler;
import java.net.URLStreamHandlerFactory;

public class MyApplication extends Application {

    // ...

    public static void main(String[] args) {
        URL.setURLStreamHandlerFactory(new URLStreamHandlerFactory() {
            public URLStreamHandler createURLStreamHandler(String protocol) {
                if ("http".equals(protocol)) {
                    return new MyUrlConnectionHandler();    
                }
                return null; // Let the default handlers deal with whatever comes here (e.g. https, jar, ...)
            }
        });
        launch(args);
    }
}
Run Code Online (Sandbox Code Playgroud)

其次,我们必须提出自己的方法Handler,告诉程序何时使用哪种类型的URLConnection.

import java.io.IOException;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;

import sun.net.www.protocol.http.Handler;
import sun.net.www.protocol.http.HttpURLConnection;

public class MyUrlConnectionHandler extends Handler {

    @Override
    protected URLConnection openConnection(URL url, Proxy proxy) throws IOException {

        if (url.toString().contains("ajax=1")) {
            return new AjaxHttpURLConnection(url, proxy, this);
        }

        // Return a default HttpURLConnection instance.
        return new HttpURLConnection(url, proxy);
    }
}
Run Code Online (Sandbox Code Playgroud)

最后但同样重要的是,来了AjaxHttpURLConnection.

import java.io.IOException;
import java.io.InputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.net.Proxy;
import java.net.URL;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.commons.io.IOUtils;

import sun.net.www.protocol.http.Handler;
import sun.net.www.protocol.http.HttpURLConnection;

public class AjaxHttpURLConnection extends HttpURLConnection {

    private PipedInputStream pipedIn;
    private ReentrantLock lock;

    protected AjaxHttpURLConnection(URL url, Proxy proxy, Handler handler) {
        super(url, proxy, handler);
        this.pipedIn = null;
        this.lock = new ReentrantLock(true);
    }

    @Override
    public InputStream getInputStream() throws IOException {

        lock.lock();
        try {

            // Do we have to set up our own input stream?
            if (pipedIn == null) {

                PipedOutputStream pipedOut = new PipedOutputStream();
                pipedIn = new PipedInputStream(pipedOut);

                InputStream in = super.getInputStream();
                /*
                 * Careful here! for some reason, the getInputStream method seems
                 * to be calling itself (no idea why). Therefore, if we haven't set
                 * pipedIn before calling super.getInputStream(), we will run into
                 * a loop or into EOFExceptions!
                 */

                // TODO: timeout?
                new Thread(new Runnable() {
                    public void run() {
                        try {

                            // Pass the original data on to the browser.
                            byte[] data = IOUtils.toByteArray(in);
                            pipedOut.write(data);
                            pipedOut.flush();
                            pipedOut.close();

                            // Do something with the data? Decompress it if it was
                            // gzipped, for example.

                            // Signal that the browser has finished.

                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }).start();
            }
        } finally {
            lock.unlock();
        }
        return pipedIn;
    }
}
Run Code Online (Sandbox Code Playgroud)


进一步的考虑

  • 如果您正在使用多个WebEngine对象,那么告诉哪个实际打开了URLConnection哪个以及哪个浏览器已完成加载可能会很棘手.
  • 您可能已经注意到我只使用http连接进行了保护.我没有测试我的方法可以转移到https等的程度(这里不是专家:O).
  • 正如您所看到的,我唯一能够知道何时实际使用my的方法AjaxHttpURLConnection是在相应的url包含时ajax=1.就我而言,这已经足够了.由于我对html和http不太好,但我不知道是否WebEngine可以以任何不同的方式发出AJAX请求(例如标题字段?).如果有疑问,您可以简单地总是返回我们修改的url连接的实例,但这当然意味着一些开销.
  • 如开头所述,如果您希望这样做,则可以在从输入流中检索数据后立即处理数据.您可以WebEngine以类似的方式获取您发送的请求数据.只需包装该getOutputStream()函数并放置另一个中间流以捕获正在发送的内容,然后将其传递给原始输出流.