登录对话框窗口不会完全处理 [英] Login dialog window won't dispose completely

查看:64
本文介绍了登录对话框窗口不会完全处理的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个对话框窗口,用于将用户登录到Java主程序。但是,我最近发现,如果用户单击取消按钮或窗口的本机关闭按钮,即使登录窗口本身已被丢弃,该程序仍将运行。我必须强行退出。我的直觉告诉我,它与创建 JXLoginPane 的一部分创建的 LoginService 有关。请看下面我的(有据可查的)代码,并给我您的想法:

I've got a dialog window for logging in a user to a main program in Java. I've recently discovered, however, that if the user clicks the "Cancel" button or the window's native Close button, the program still runs, even if the login window itself has been disposed of. I have to force quit it. My instincts tell me that it has to do with the LoginService that is created as part of creating a JXLoginPane. Please have a look at my (well-documented) code below and give me your thoughts:

package info.chrismcgee.sky;

import info.chrismcgee.beans.Login;
import info.chrismcgee.dbutil.LoginConnectionManager;
import info.chrismcgee.login.PasswordHash;
import info.chrismcgee.tables.LoginManager;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jdesktop.swingx.JXLoginPane;
import org.jdesktop.swingx.auth.LoginService;

public class LoginDialog extends JDialog {

    // For logging!
    static final Logger log = LogManager.getLogger(LoginDialog.class.getName());

    /**
     * Serialize, to keep Eclipse from throwing a warning message.
     */
    private static final long serialVersionUID = 52954843540592996L;
    private final JPanel contentPanel = new JPanel(); // default.
    // The login pane is a field because it needs to be called
    // by the "OK" button later.
    private JXLoginPane loginPane;
    // User bean is a field because two inner methods need to access it,
    // and it would be cheaper to only create one bean.
    private Login bean;

    /**
     * Launch the application.
     * Unedited.
     */
    public static void main(String[] args) {

        log.entry("main (LoginDialog)");

        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {

                log.entry("run (LoginDialog)");

                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    LoginDialog dialog = new LoginDialog();
                    dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
                    dialog.setVisible(true);
                } catch (Exception e) {
                    log.error("Error running the LoginDialog", e);
                }

                log.exit("run (LoginDialog)");
            }
        });

        log.exit("main (LoginDialog)");
    }

    /**
     * Create the dialog.
     */
    public LoginDialog() {
        setBounds(100, 100, 450, 300);
        getContentPane().setLayout(new BorderLayout()); // default.
        contentPanel.setLayout(new FlowLayout()); // default.
        contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5)); // default.
        getContentPane().add(contentPanel, BorderLayout.CENTER); // default.
        {
            // Create a JXLoginPane using a LoginService
            // to handle the authentication.
            loginPane = new JXLoginPane(new LoginService() {

                // `authenticate` *must* be overridden.
                // We will not be using the "server" string, however.
                @Override
                public boolean authenticate(String name, char[] password, String server)
                        throws Exception {

                    log.entry("authenticate (LoginDialog)");

                    // With the username entered by the user, get the user information
                    // from the database and store it in a Login bean.
                    bean = LoginManager.getRow(name);

                    // If the user does not exist in the database, the bean will be null.
                    if (bean != null)
                        // Use `PasswordHash`'s `validatePassword` static method
                        // to see if the entered password (after being hashed)
                        // matches the hashed password stored in the bean.
                        // Returns `true` if it matches, `false` if it doesn't.
                        return log.exit(PasswordHash.validatePassword(password, bean.getHashPass()));
                    else
                        // If the user doesn't exist in the database, just return `false`,
                        // as if the password was wrong. This way, the user isn't alerted
                        // as to which of the two pieces of credentials is wrong.
                        return log.exit(false);
                }
            });
            // Add the login pane to the main content panel.
            contentPanel.add(loginPane);
        }
        {
            // Create the button pane for the bottom of the dialog.
            JPanel buttonPane = new JPanel();
            buttonPane.setLayout(new FlowLayout(FlowLayout.RIGHT)); // default.
            getContentPane().add(buttonPane, BorderLayout.SOUTH); // default.
            {
                // Create the "OK" button plus its action listener.
                JButton okButton = new JButton("OK");
                okButton.addActionListener(new ActionListener() {
                    public void actionPerformed(ActionEvent evt) {

                        log.entry("OK button pressed. (LoginDialog)");

                        // Several of these will throw exceptions,
                        // so it's in a `try-catch` block.
                        try {
                            // This `if` statement is what calls the `authenticate`
                            // method to see if the credentials match the database.
                            if (loginPane.getLoginService().authenticate(
                                    loginPane.getUserName().toLowerCase(),
                                    loginPane.getPassword(), null))
                            {
                                // If the credentials are in order, close the connection
                                // to the `logins` database, since they won't be needed anymore.
                                LoginConnectionManager.getInstance().close();
                                // Also close the login window; it won't be needed anymore, either.
                                Window window = SwingUtilities.windowForComponent(contentPanel);
                                window.dispose();

                                log.trace("Running Scheduling with access level of " + bean.getAccessLevel());
                                // And finally run the main `Scheduling.java` program,
                                // passing to it the user's access level.
                                String[] args = {Integer.toString(bean.getAccessLevel())};
                                Scheduling.main(args);
                            }
                            else
                            {
                                // If the login credentials fail, let the user know generically.
                                JOptionPane.showMessageDialog(null, "Incorrect username or password.", "Bad Username or Password", JOptionPane.ERROR_MESSAGE);
                            }
                        } catch (Exception e) {
                            log.error("Exception when hitting the 'OK' button.", e);
                        }

                        log.exit("OK button done.");
                    }
                });
                okButton.setActionCommand("OK"); // default.
                // Add the "OK" button the button pane.
                buttonPane.add(okButton); // default.
                getRootPane().setDefaultButton(okButton); // default.
            }
            {
                // Create the "Cancel" button plus its listener.
                JButton cancelButton = new JButton("Cancel");
                cancelButton.setActionCommand("Cancel"); // default.
                cancelButton.addActionListener(new ActionListener() {
                    public void actionPerformed(ActionEvent e) {

                        log.entry("CANCEL button pressed. (LoginDialog)");

                        // Just close the connection to the database,
                        LoginConnectionManager.getInstance().close();
                        // and close the login window.
                        Window window = SwingUtilities.windowForComponent((Component) e.getSource());
                        window.dispose();

//                      System.exit(0);
                        log.exit("CANCEL button done.");
                    }
                });
                // Add the "Cancel" button to the button pane.
                buttonPane.add(cancelButton); // default.
            }
        }
    }

}

我假设在创建对话框时,LoginService创建了一个线程。如果是这样,结束此线程的正确方法是什么?如果不是这种情况,这是怎么回事,我该如何解决?

I assume that the LoginService created a thread when the dialog was created. What is the proper way to end this thread, if that's the case? And if it's not the case, what's going on and how can I fix it?

14/10/01 2:42 pm编辑:接受了dic19的建议并简化了操作。太好了现在,我根据他的建议创建了一个准系统对话框,即只坚持使用内置的 JXLoginDialog 。它没有实际登录到我的主程序所需的所有方法,但这里是简化的对话框,该对话框足以查看按下取消按钮后程序是否继续运行:

10/01/14 2:42pm I've taken dic19's advice and simplified things. Greatly. I've created a barebones dialog now using his recommendation of just sticking with the built-in JXLoginDialog. It doesn't have all the methods that are needed for actually logging into my main program, but here's that simplified dialog, which should be enough to see if the program continues running after the "Cancel" button is pressed:

package info.chrismcgee.sky;

import info.chrismcgee.beans.Login;
import info.chrismcgee.login.PasswordHash;
import info.chrismcgee.tables.LoginManager;

import javax.swing.JDialog;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jdesktop.swingx.JXLoginPane;
import org.jdesktop.swingx.auth.LoginService;

public class LoginDialog {

    static final Logger log = LogManager.getLogger(LoginDialogOriginal.class.getName());
    private static Login bean;
    private static LoginService loginService = new LoginService() {

        @Override
        public boolean authenticate(String name, char[] password, String server)
                throws Exception {

            log.entry("authenticate (LoginDialogOriginal)");

            bean = LoginManager.getRow(name);

            if (bean != null)
                return log.exit(PasswordHash.validatePassword(password, bean.getHashPass()));
            else
                return log.exit(false);
        }
    };

    /**
     * Launch the application.
     */
    public static void main(String[] args) {
        try {
            JXLoginPane loginPane = new JXLoginPane(loginService);
            JXLoginPane.JXLoginDialog dialog = new JXLoginPane.JXLoginDialog(new JDialog(), loginPane);
            dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
            dialog.setVisible(true);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

I'

遗憾的是,在单击取消时确实清除了该对话框,程序仍在内存中,像以前一样强制退出它。也许PasswordHash中有一个线程或正在发生什么?我或多或少只是从 CrackStation的帖子中删除了代码。

Sadly, while clicking "Cancel" does dispose of the dialog, the program is still in memory and I have to force-quit it, as before. Perhaps there's a thread or something going on in PasswordHash? I more or less just lifted the code from CrackStation's post for that.

14/10/02 3:32 pm编辑:根据dic19建议创建的JFrame,然后显示或删除,我尝试更新该简化程序。根据登录对话框的成功处理。恐怕在用户单击取消后,这仍然不会更改该程序。它仍在记忆中,仍然需要强制退出。以下是对dic19进行修改的更新代码:

10/02/14 3:32pm I've tried updating that simplified program, based on dic19's recommendation of creating a JFrame that is then either shown or disposed of based on the success of the login dialog. I'm afraid this still does not change the program hanging around after the user clicks on "Cancel". It's still in memory and still needs to be force-quitted. Here's the updated code with dic19's modifications:

package info.chrismcgee.sky;

import java.awt.event.WindowEvent;

import info.chrismcgee.beans.Login;
import info.chrismcgee.login.PasswordHash;
import info.chrismcgee.tables.LoginManager;

import javax.swing.JDialog;
import javax.swing.JFrame;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jdesktop.swingx.JXLoginPane;
import org.jdesktop.swingx.auth.LoginService;

public class LoginDialog {

    static final Logger log = LogManager.getLogger(LoginDialogOriginal.class.getName());
    private static Login bean;
    private static LoginService loginService = new LoginService() {

        @Override
        public boolean authenticate(String name, char[] password, String server)
                throws Exception {

            log.entry("authenticate (LoginDialogOriginal)");

            bean = LoginManager.getRow(name);

            if (bean != null)
                return log.exit(PasswordHash.validatePassword(password, bean.getHashPass()));
            else
                return log.exit(false);
        }
    };

    /**
     * Launch the application.
     */
    public static void main(String[] args) {
        try {
            JFrame frame = new JFrame("Welcome!"); // A non-visible JFrame which acts as the parent frame.
            frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            JXLoginPane loginPane = new JXLoginPane(loginService);
            JXLoginPane.JXLoginDialog dialog = new JXLoginPane.JXLoginDialog(frame, loginPane);
            dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
            dialog.setVisible(true);

            if (dialog.getStatus() != JXLoginPane.Status.SUCCEEDED)
                frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING));
            else
                frame.setVisible(true);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

为了简洁起见,我删除了评论。知道为什么这会导致单击取消后仍导致程序在内存中徘徊吗?

Again, I've removed comments for brevity. Any idea why this is still causing the program to hang around in memory after clicking "Cancel"?

推荐答案

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  // To quit the whole GUI.

dialog.dispose(); // After user clicks cancel.

但是由于取消按钮不是您明确创建的,因此无法直接调用dialog.dispose() 。您仍然可以。 3条额外的代码行。
调用dialog.dispose(),在对话框上添加一个窗口侦听器(扩展 java.awt.event.WindowAdapter )。并覆盖windowClosing()方法。并在其主体中写入 dialog.dispose()

But as the cancel button is not explicitly created by you, you cannot call dialog.dispose() directly. BUT you still can. with 3 extra lines of code. to call dialog.dispose(), add a window listener(extend java.awt.event.WindowAdapter) on your dialog. and override the windowClosing() method. and write dialog.dispose() in its body.

代码段:

dialog.addWindowListener(

    new WindowAdapter() {

        @Override
        public void windowClosing(WindowEvent event) {

            dialog.dispose();
        }
    }
);  

这篇关于登录对话框窗口不会完全处理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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