在JavaFX应用程序中使用showAndWait

时间:2016-09-11 22:21:57

标签: java javafx

我正在创建一个使用单点登录的登录。为此,如果用户登录的公司启用了SSO,则将打开JavaFX WebView,允许用户登录。此登录将完全基于Web,因此我不需要真正的数据捕获。

我有两个班级,LoginManagerSSOLoginManager检查公司是否为SSOEnabled,如果是,则转到SSO。在这里,我创建了用户界面,然后使用Platform.runLater(),转到打开webview的方法。但是,我的程序转到initJFXPanel,然后立即返回LoginManager以执行代码,这会导致问题。

我希望能够遍历所有SSO,直到它到达某个网页,然后我使用frame.dispose(),然后返回LoginManager继续。我想我需要使用StageshowAndWait(),但我不确定如何解决这个问题。

我尝试在initJFXPanel内创建一个舞台,但是在它有机会做任何事情之前它回到LoginManager并没有工作,我也尝试在createPopup设置舞台{1}}但我不确定该用作舞台或场景。

这是我的代码:

LoginManager类

public class LoginManager {

    private boolean bSSOEnabled = true;

    private void Login() {

        // get company details

        if(bSSOEnabled) {
            new SSO();
        }

        // once SSO is done, continue with execution
    }
}

SSO课程

public class SSO {

    private JFrame frame;
    private JFXPanel fxPanel;
    private JPanel containerPanel;
    final static String WEBVIEW = "WebView";

    public SSO(){
        createPopup();
    }

    private void createPopup(){

        Dimension size = new Dimension(480, 80);

        frame = new JFrame("Web Login");
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        containerPanel = new JPanel(new CardLayout());

        fxPanel = new JFXPanel();
        fxPanel.setSize(size);

        Platform.runLater(() -> initJFXPanel(frame, fxPanel));

        containerPanel.add(fxPanel, WEBVIEW);

        frame.add(containerPanel, BorderLayout.CENTER);

        frame.setPreferredSize(size);
        frame.setResizable(false);
        frame.getRootPane().setWindowDecorationStyle(JRootPane.NONE);

        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);

    }

    private void initJFXPanel(final JFrame frame, final JFXPanel fxPanel){

        Group group = new Group();
        Scene scene = new Scene(group);
        fxPanel.setScene(scene);

        WebView webview = new WebView();

        group.getChildren().add(webview);
        webview.setMinSize(500, 640);
        webview.setMaxSize(500, 640);

        CookieManager cookieManager = new CookieManager();
        java.net.CookieHandler.setDefault(cookieManager);

        webview.getEngine().load("http://google.com/");


        if(webview.getEngine().getLocation().equalsIgnoreCase("cookie url")) {
            // harvest the cookies
        }
        else if(webview.getEngine().getLocation().equalsIgnoreCase("end url")) {
            frame.dispose();
        }

    }

}

感谢任何帮助。我已经被困在这一整天了,我不知道该怎么做。

1 个答案:

答案 0 :(得分:0)

如果您的应用程序是Swing应用程序,并且您需要使用WebView,那么正确的方法就是您在代码中显示的方法,即使用Swing窗口并嵌入WebViewJFXPanel中。混合来自两个框架的窗口通常是一个坏主意,因此我建议完全避免使用Stage

在用户关闭窗口之前在Swing块中创建代码的方法,即Swing等效于JavaFX Stage.showAndWait()调用,是使用JDialog并使其成为模态。除了阻止任何其他窗口的输入外,这将阻止执行(AWT事件调度线程)直到对话框被解除。

使这有点棘手的是你有三个线程可以使用:AWT事件调度线程,JavaFX应用程序线程和主线程。显示模态对话框将阻止AWT事件调度线程,但您想要(如果我正确理解了问题)要阻止的主线程。此外,您只能在AWT事件调度线程上创建,修改和显示Swing组件,并且您只能在JavaFX应用程序线程上创建,修改和显示JavaFX节点。

所以我认为你想要这样的东西:

public class SSO {

    private JDialog dialog;
    private JFXPanel fxPanel;
    private JPanel containerPanel;
    final static String WEBVIEW = "WebView";

    public SSO(){
        // creates Swing components: MUST be called on AWT Event thread
        createPopup();
    }

    private void createPopup(){

        Dimension size = new Dimension(480, 80);

        dialog = new JDialog();
        dialog.setTite("Web Login");

        // make dialog modal:
        dialog.setModalityType(ModalityType.APPLICATION_MODAL);

        // oops: don't want this:
        // dialog.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        // but this:
        dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);

        containerPanel = new JPanel(new CardLayout());

        fxPanel = new JFXPanel();
        fxPanel.setSize(size);

        Platform.runLater(() -> initJFXPanel(dialog, fxPanel));

        containerPanel.add(fxPanel, WEBVIEW);

        dialog.add(containerPanel, BorderLayout.CENTER);

        dialog.setPreferredSize(size);
        dialog.setResizable(false);
        dialog.getRootPane().setWindowDecorationStyle(JRootPane.NONE);

        dialog.pack();
        dialog.setLocationRelativeTo(null);
        dialog.setVisible(true);

    }

    private void initJFXPanel(final JDialog dialog, final JFXPanel fxPanel){

        Group group = new Group();
        Scene scene = new Scene(group);
        fxPanel.setScene(scene);

        WebView webview = new WebView();

        group.getChildren().add(webview);
        webview.setMinSize(500, 640);
        webview.setMaxSize(500, 640);

        CookieManager cookieManager = new CookieManager();
        java.net.CookieHandler.setDefault(cookieManager);

        webview.getEngine().load("http://google.com/");


        if(webview.getEngine().getLocation().equalsIgnoreCase("cookie url")) {
            // harvest the cookies
        }
        else if(webview.getEngine().getLocation().equalsIgnoreCase("end url")) {
            // this will hide the dialog and release the event dispatch thread
            // that is waiting for it, but it must be called on the event dispatch thread
            // not sure of your exact requirements, but you may need to do this
            // in the if clause as well?
            SwingUtilities.invokeLater(() -> dialog.setVisible(false));
        }

    }

}

现在,如果您实例化SSO,代码将会阻止,直到对话框被解除。但是,您必须在AWT Event Dispatch线程上执行此操作。等待主线程完成的技巧是将new SSO()的调用表示为FutureTask,将FutureTask提交给SwingUtilities.invokeLater(),然后重新开启主线程,在get()上调用FutureTask:这将阻塞,直到任务完成。你需要在这里小心:如果你无意中在事件调度线程上执行此操作,最终会陷入死锁,因为事件调度线程将阻塞,等待事件调度线程发生某些事情...所以你可能想要一个防止这是安全的。

所以你的代码可能需要看起来像这样:

public class LoginManager {

    private boolean bSSOEnabled = true;

    private void Login() {

        // get company details

        if(bSSOEnabled) {
            if (SwingUtilities.isEventDispatchThread()) {
                // already on EDT, just invoke the SSO which will block 
                // until the dialog is dismissed:
                new SSO();
            } else {
                // create FutureTask to show dialog, etc:
                FutureTask<Void> ssoTask = new FutureTask<>(SSO:new, null);
                // submit task to AWT event dispatch thread:
                SwingUtilities.invokeLater(ssoTask);
                // wait for task to complete, i.e. for dialog to be dismissed:
                try {
                    ssoTask.get();
                } catch (InterruptedException exc) {
                    // thread was interrupted, representing cancelation of some kind:
                    System.out.println("Thread interrupted");
                    Thread.currentThread().interrupt();
                } catch (ExecutionException ee) {
                    // something went wrong...
                    throw new RuntimeException(ee);
                }
            }
        }



    // once SSO is done, continue with execution
    }
}