JavaFX的WebEngine等待阿贾克斯完成 [英] JavaFX WebEngine wait for ajax to complete

查看:292
本文介绍了JavaFX的WebEngine等待阿贾克斯完成的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发JavaFX中数据挖掘应用程序,它依赖于web视图(因此也WebEngine)。开采发生在2个步骤:首先,用户使用用户界面导航到网站的web视图来配置,其中感兴趣的数据可以被搜索。其次,使用定期运行后台任务,一个WebEngine加载同一文件,并试图从加载的文档中提取数据。

I'm developing a data mining application in JavaFX which relies on the WebView (and thus also the WebEngine). The mining happens in 2 steps: first the user uses the UI to navigate to a website in the WebView to configure where interesting data can be searched. Second, using a background task that periodically runs, a WebEngine loads the same document and tries to extract the data from the loaded document.

这完全适用于大多数情况下,但最近我遇到了与使用AJAX来呈现内容的网页一些麻烦。要检查是否WebEngine已加载的文件,我听 loadWorker stateProperty 。如果状态转换为succesfull,我知道该文件被加载(与可能的的document.ready()或同等学历运行任何JavaScript在一起)。这是因为JavaScript是JavaFX的线程上执行,如果我没有记错(来源:的https://blogs.oracle.com/javafx/entry/communicating_between_javascript_and_javafx).但是,如果一个AJAX调用开始,JavaScript的执行完毕,发动机让我知道该文件已经准备好,虽然这显然是不一样的内容可能会有所变动,由于出色的AJAX调用。

This works perfectly for most cases but recently I've ran into some trouble with pages that use AJAX to render content. To check if the WebEngine has loaded the document, I listen to the loadWorker's stateProperty. If the state transitions to succesfull, I know the document is loaded (together with any javascript that might run on document.ready() or equivalent). This because javascript is executed on the JavaFX thread if I'm not mistaken (source: https://blogs.oracle.com/javafx/entry/communicating_between_javascript_and_javafx). However, if an AJAX call is started, the javascript execution finishes and the engine lets me know the document is ready though it is obviously not as the contents might still change due to the outstanding AJAX call.

有没有解决这个办法,来让我通知时,Ajax调用完成后注入钩?我试着安装在 $默认完整的处理程序。ajaxSetup()但是这是相当不可靠的,因为如果一个Ajax调用覆盖了完整的处理程序,默认不会被调用。另外,我也只能这样注入后,第一次加载该文件(到那时一些AJAX调用可能已经在运行)。我测试过这种注射上行调用,它工作正常不提供自己完整的处理程序,它是在命令启动(注默认处理程序后)AJAX调用。

Is there any way around this, to inject a hook so I am notified when AJAX calls are finished? I've tried installing a default complete handler in $.ajaxSetup() but that is quite dodgy because if an ajax call overrides the complete handler, the default won't be called. Plus, I can only inject this after the document is first loaded (and by then some AJAX calls may already be running). I've tested this injection with an upcall and it works fine for AJAX calls that are launched on command (after the injection of the default handler) that don't supply their own complete handler.

我在寻找两件事情:第一:一个通用的方法挂钩到的AJAX调用,二来完成处理:一种方法来等待WebEngine完成所有的AJAX调用事后通知我。

I'm looking for two things: firstly: a generic way to hook into the completion handler of AJAX calls, and secondly: a way to wait for the WebEngine to finish all AJAX calls and notify me afterwards.

推荐答案

我也有这个问题,通过提供自己的执行 sun.net.www.protocol.http.HttpURLConnection 的,我用它来处理任何AJAX解决了这个问题要求。我的课,通称为 AjaxHttpURLConnection ,挂钩到的getInputStream()的功能,但不会返回其原来的输入流。相反,我举一个实例的PipedInputStream WebEngine 。然后我读了所有从原来的输入流中的数据并将它传递给我的管道流。 这样一来,我获得2好处:

Explanation

I've also had this problem and solved it by providing my own implementation of sun.net.www.protocol.http.HttpURLConnection which I use to process any AJAX requests. My class, conveniently called AjaxHttpURLConnection, hooks into the getInputStream() function, but does not return its original input stream. Instead, I give an instance of PipedInputStream back to the WebEngine. I then read all the data coming from the original input stream and pass it on to my piped stream. This way, I gain 2 benefits:

  1. 我知道,当最后一个字节已收到,因此AJAX请求已处理完毕。
  2. 我甚至可以抓住所有的输入数据,并已经与它的工作(如果我想)。


首先,您将拥有,而不是默认的告诉Java使用的URLConnection实施。要做到这一点,你必须给它提供你自己版本的 URLStreamHandlerFactory 的。你可以找到许多线程在这里SO(如这个),或通过这个谷歌主题。为了设置您的工厂实例,将按照你的地方早期主要方法。这就是我的模样。

First, you will have to tell Java to use your URLConnection implementation instead of the default one. To do so, you must provide it with your own version of the URLStreamHandlerFactory. You can find many threads here on SO (e.g. this one) or via Google on this topic. In order to set your factory instance, put the following somewhere early in your main method. This is what mine looks like.

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);
    }
}

其次,我们要拿出我们自己的处理程序的通知程序时要使用哪种类型的的URLConnection 。

Second, we have to come up with our own Handler that tells the programme when to use which type of 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);
    }
}

最后但并非最不重要的,来这里的 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;
    }
}



  • 如果您正在使用多个 WebEngine 的对象,它可能会非常棘手知道哪一个真正拉开了的URLConnection 和因此,该浏览器加载完毕。
  • 您可能已经注意到,我只能用HTTP连接dealth。我没有多远我的方法可以转移到HTTPS等。(不是一个专家在这里:O)的测试。
  • 正如你所看到的,我唯一的手段,知道什么时候竟然用我的 AjaxHttpURLConnection 是当相应的URL中包含阿贾克斯= 1 。就我而言,这是不够的。由于我不是太好用HTML和HTTP,但是,我不知道,如果 WebEngine 可以使任何不同的方式Ajax请求(如报头字段?)。如果有疑问,你可以简单地总是返回修改后的URL连接的一个实例,但当然,这将意味着一些开销。
  • 正如开头所述,您可以立即使用数据的工作一旦被检索从输入流中,如果你想这样做。你可以抓住你的 WebEngine 发送以类似的方式请求数据。只是包装的的getOutputStream()函数,并把另一个中间流抢到任何被发送,然后将它传递到原来的输出流。
  • If you are using multiple WebEngine objects, it might be tricky to tell which one actually opened the URLConnection and thus which browser has finished loading.
  • You might have noticed that I only dealth with http connections. I have not tested in how far my approach could be transferred to https etc. (not an expert here :O).
  • As you have seen, my only means to know when to actually use my AjaxHttpURLConnection is when the corresponding url contains ajax=1. In my case, this was sufficient. Since I am not too good with html and http, however, I don't know if the WebEngine can make AJAX requests in any different way (e.g. the header fields?). If in doubt, you could simply always return an instance of our modified url connection, but that would of course mean some overhead.
  • As stated in the beginning, you can immediately work with the data once it has been retrieved from the input stream if you wish to do so. You can grab the request data that your WebEngine sends in a similar way. Just wrap the getOutputStream() function and place another intermediate stream to grab whatever is being sent and then pass it on to the original output stream.

这篇关于JavaFX的WebEngine等待阿贾克斯完成的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆