java - 使用 JFXPanel 为 native 应用程序构建 Facebook OAuth 登录流程

标签 java swing javafx

这可能是一个微不足道的问题,但我对 Swing 和 JavaFX 很陌生。

我正在尝试为几乎完全批处理的应用程序构建 FB OAuth 登录表单。我知道我必须打开一个 WebView 来获取“代码”,然后与“ token ”进行交换。为此,我想使用 JFXPanel 作为一个使用 WebEngine 的简单内部 Web 浏览器。

我的是一个简单的数据处理批处理作业,仅在主线程中完成艰苦的工作。该代码必须嵌入到另一代码中,在该代码中我无法控制除主线程之外的线程。为了简单起见,假设我必须使用这个简单的 main 方法调用 WebView:

 public static void main(String[] args) {

        SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                ComponentBrowser browser = new ComponentBrowser();
                browser.setVisible(true);
                browser.loadURL("https://www.facebook.com/connect/login_success.html");
           }    


       });
    }

ComponentBrowser 已定义 here .

我需要 WebView 同步,这是最佳流程:

  • 主线程进入休眠状态
  • 用户导航 WebView,直到获得确认码
  • 代码以某种方式被推回到主线程
  • 主线程恢复运行

我缺少的是如何向调用者返回响应的内容(正文中只有“成功”一词)和重定向后的网址(具有“code”查询字符串参数) 。然后,调用者应该优雅地关闭窗口。

问题是,除了 hibernate/恢复主线程之外,我无法控制主线程。对于具有 main() 代码的类也是如此:该代码是由我必须将代码嵌入其中的数据处理平台自动生成的。因此,我无法修改 main() 本身之外的那部分代码(向类添加方法或监听器是不可能的,但我可以向 main() 代码本身添加语句)

这就是为什么 JavaFX 和批处理代码之间的通信必须尽可能简单,甚至微不足道。

最佳答案

您可以使用 Web 引擎的负载工作线程的状态属性注册监听器:

engine.getLoadWorker().stateProperty().addListener((obs, oldState, newState) -> {
    if (newState == Worker.State.SUCCEEDED) {
        // document is successfully loaded
    }
});

当从服务器收到响应时,将会收到通知。

要检索内容,您可以使用 WebEngine.getDocument(),它返回 org.w3c.dom.Document目的。 dom API 有点笨拙(imo),但我认为在你的情况下你需要的是

String content = engine.getDocument().getDocumentElement().getTextContent();

然后您可以检查它是否“成功”

线程部分变得棘手。如果我理解正确的话,您基本上在这里运行一个几乎完全非 GUI 的应用程序,但您只需要一点 UI 来在启动时验证登录,然后想要返回主线程并执行代码。

如果这完全是 Swing(用于登录验证),我将按如下方式处理:

从主线程中,调用 SwingUtilities.invokeAndWait(...) 并传入一个显示模式对话框的 RunnableinvokeAndWait 将阻止主线程的执行,直到 Runnable(在 AWT 线程上执行)完成。通过使用模态对话框,您可以阻止代码的执行,直到对话框被关闭为止,因此现在 Runnable 直到对话框被关闭才会完成,因此主线程将阻塞,直到对话框被关闭为止。因此,现在您所需要做的就是安排在验证登录时关闭该对话框。

通过在 Swing 对话框中放置一个 JFXPanel,这会变得稍微复杂一些。现在,您需要使用 Platform.runLater(...) 将修改 JavaFX 组件的任何代码传递给 JavaFX 应用程序线程,并且如果您更改任何 Swing 组件(例如关闭对话框),您需要使用 SwingUtilities.runLater(...) 将其交还给 AWT 事件线程。

这里有一个快速演示。我只是在这里使用了一个按钮,按下该按钮会将“成功”加载到 WebView 中。显然,您可以通过加载真实的登录屏幕来替换它。

import java.awt.Window;

import javafx.application.Platform;
import javafx.concurrent.Worker;
import javafx.embed.swing.JFXPanel;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;

import javax.swing.JDialog;
import javax.swing.SwingUtilities;

import org.w3c.dom.Document;


public class WaitingForLogin {

    public WaitingForLogin() throws Exception {
        System.out.println("Showing window...");

        // Run initAndShowUI() on AWT event thread. Block until that is complete:
        SwingUtilities.invokeAndWait(() -> initAndShowUI());

        System.out.println("Now running application");
        for (int i=1; i <=10; i++) {
            System.out.println("Counting: "+i);
            Thread.sleep(500);
        }
    }

    public void initAndShowUI() {
        JDialog dialog = new JDialog((Window)null);
        dialog.setModal(true);
        JFXPanel jfxPanel = new JFXPanel();
        Platform.runLater(() -> initJFX(jfxPanel, dialog));
        dialog.add(jfxPanel);
        dialog.setSize(400, 400);
        dialog.setLocationRelativeTo(null);

        // Since the dialog is modal, this will block execution (of the AWT event thread)
        // until the dialog is closed:
        dialog.setVisible(true);
    }

    private void initJFX(JFXPanel jfxPanel, Window dialog) {

        // Create a web view:
        WebView webView = new WebView();
        WebEngine engine = webView.getEngine();

        // Check for a new document being loaded. If the document just contains the 
        // text "Success", then close the dialog (unblocking all threads waiting for it...)
        engine.getLoadWorker().stateProperty().addListener((obs, oldState, newState) -> {
            if (newState == Worker.State.SUCCEEDED) {
                Document doc = engine.getDocument();
                if ("Success".equals(doc.getDocumentElement().getTextContent())) {
                    // Close dialog: this must be done on the AWT event thread
                    SwingUtilities.invokeLater(() -> dialog.dispose());
                }
            }
        });

        // Just for testing
        // simulate login with simple button:

        Button button = new Button("Login");
        button.setOnAction(event -> engine.loadContent("Success", "text/plain"));
        HBox controls = new HBox(button);
        controls.setAlignment(Pos.CENTER);
        controls.setPadding(new Insets(10));

        jfxPanel.setScene(new Scene(new BorderPane(webView, null, null, controls, null)));
    }

    public static void main(String[] args) throws Exception {
        new WaitingForLogin();

    }

}

关于java - 使用 JFXPanel 为 native 应用程序构建 Facebook OAuth 登录流程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28946938/

相关文章:

java - 使用 JavaFX 围绕球体移动物体

Java 堆空间错误 - 如何增加可保留的最大数量? 【不能超过1505M】

java - 出现断言错误,即使预期值和实际值相同

java - 如何确定屏幕保护程序是否在 Java 中运行?

java - 通过将 jpanel 组件作为参数传递来绘制一个圆圈

java - 行动执行方法的困难

java,组件仅在直接添加到框架时显示

java - 同步 FIFO 缓冲区使用

java - 如何在谓词中对 FilteredList 结果进行优先级排序/排序?

单元格编辑后 Javafx TableView 失去焦点