Java 登录窗口冻结
我开发了一个登录窗口,因为我的程序连接到SSH服务器 我在启动程序时打开此窗口。但我需要稍后为另一台服务器重新打开它。第二次打开时,登录窗口冻结 供参考 以下是登录窗口代码:Java 登录窗口冻结,java,multithreading,swing,freeze,Java,Multithreading,Swing,Freeze,我开发了一个登录窗口,因为我的程序连接到SSH服务器 我在启动程序时打开此窗口。但我需要稍后为另一台服务器重新打开它。第二次打开时,登录窗口冻结 供参考 以下是登录窗口代码: package com.maxbester.test; import java.awt.BorderLayout; import java.awt.FlowLayout; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import j
package com.maxbester.test;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
import org.apache.log4j.Logger;
import com.maxbester.test.Server;
@SuppressWarnings("serial")
public class LoginWindow extends JFrame {
private static final Logger LOG = Logger.getLogger(LoginWindow.class);
private Server _server;
private Object _parent;
private JPanel _panel;
private JLabel _loginLabel;
private JTextField _loginInput;
private JLabel _passwordLabel;
private JPasswordField _passwordInput;
private JPanel _buttonPanel;
private JButton _okButton;
private JButton _cancelButton;
public LoginWindow(Object parent, Server server) {
_server = server;
_parent = parent;
initComponents();
}
private void initComponents() {
setTitle("Connection window");
setLayout(new BorderLayout());
_loginLabel = new JLabel("Login: ");
_loginInput = new JTextField(System.getProperty("user.name"), 15);
_passwordLabel = new JLabel("Password: ");
_panel = new JPanel(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.gridx = 0;
c.gridy = 0;
// external padding
c.insets = new Insets(5,5,5,5);
_panel.add(_loginLabel,c);
c.gridx = 1;
_panel.add(_loginInput,c);
c.gridy = 1;
_panel.add(_passwordLabel,c);
c.gridx = 0;
_panel.add(getPasswordLabel(),c);
add(_panel, BorderLayout.CENTER);
_okButton = new JButton("Ok");
_okButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
okActionPerformed();
}
});
_cancelButton = new JButton("Cancel");
_cancelButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
System.exit(0);
}
});
_buttonPanel = new JPanel(new FlowLayout());
_buttonPanel.add(_okButton);
_buttonPanel.add(_cancelButton);
add(_buttonPanel, BorderLayout.SOUTH);
pack();
}
/**
* @return the _passwordInput
*/
private JPasswordField getPasswordInput() {
if (LOG.isTraceEnabled()) {
LOG.trace("getPasswordInput()");
}
if (_passwordInput == null) {
_passwordInput = new JPasswordField(15) {
// Give the focus to this field
public void addNotify() {
super.addNotify();
requestFocusInWindow();
}
};
_passwordInput.addKeyListener(new KeyListener() {
@Override public void keyTyped(KeyEvent arg0) {}
@Override
public void keyReleased(KeyEvent keyEvent) {
if (keyEvent.getKeyChar() == KeyEvent.VK_ENTER) {
if (LOG.isTraceEnabled()) {
LOG.trace("Click enter in password field");
}
okActionPerformed();
}
}
@Override public void keyPressed(KeyEvent arg0) {}
});
}
return _passwordInput;
}
protected void okActionPerformed() {
if (LOG.isTraceEnabled()) {
LOG.trace("okActionPerformed()");
}
if (_server != null) {
String login = _loginInput.getText();
if (login != null && !login.isEmpty()) {
_server.setLogin(login);
char[] password = _passwordInput.getPassword();
if (password != null && password.length > 0) {
_server.setPassword(new String(password));
setVisible(false);
synchronized (_parent) {
if (LOG.isTraceEnabled()) {
LOG.trace("syncronized with "+_parent);
}
_parent.notifyAll();
}
} else {
JOptionPane.showMessageDialog(LoginWindow.this,"Please enter a password", "Password required", JOptionPane.WARNING_MESSAGE);
}
} else {
JOptionPane.showMessageDialog(LoginWindow.this,"Please enter your login", "Login required", JOptionPane.WARNING_MESSAGE);
}
} else {
LOG.error("Server is null");
JOptionPane.showMessageDialog(LoginWindow.this,"Server is null.", "An error has occured", JOptionPane.ERROR_MESSAGE);
System.exit(-1);
}
}
}
我存储父类以在用户输入登录名和密码时通知它
服务器类非常简单:
package com.maxbester.test;
public class Server {
private String _url;
private String _login = "root";
private String _password = "";
public Server(String url) {
_url = url;
}
public Server(String url, String login, String password) {
_url = url;
if (login != null) {
_login = login;
}
if (password != null) {
_password = password;
}
}
public String getUrl() {
return _url;
}
public String getLogin() {
return _login;
}
public String getPassword() {
return _password;
}
public void setUrl(String url) {
if (url != null) {
_url = url;
}
}
public void setLogin(String login) {
_login = login;
}
public void setPassword(String password) {
_password = password;
}
public boolean hasLogin() {
return _login != null;
}
public boolean hasPassword() {
return _password != null;
}
public String toString() {
return _url;
}
/**
* Test if the server has a login and a password.
* @return Return true if the server has a login and a password, false otherwise.
*/
public boolean hasConnectionId() {
return _login != null && !_login.isEmpty() && _password != null && !_password.isEmpty();
}
}
此窗口由控制器启动:
package com.maxbester.test;
public class Controller {
public Controller() {
Server server = new Server("myserver");
login(server);
}
/**
* <p>This method opens a login window and waits until a signal is receive from
* that window. When the signal is received, closes the window.</p>
* <p>The login window has to update the login and password of 'Server'.</p>
* @param server
*/
private synchronized void askLoginPassword(final Server server) {
server.setLogin(null);
server.setPassword(null);
while (server.hasConnectionId() == false) {
LoginWindow loginWindow = new LoginWindow(Controller.this, server);
try {
loginWindow.setVisible(true);
wait();
} catch (InterruptedException e) {
LOG.error("Thread exception", e);
JOptionPane.showMessageDialog(loginWindow, "Thread exception", "Error", JOptionPane.ERROR_MESSAGE);
} finally {
loginWindow.dispose();
loginWindow = null;
}
}
}
/**
* <p>First of all, this method tests if the server is reachable.</p>
* <p>If it is, the method opens a LoginWindow in order to ask the user ids.</p>
* <p>When we have the information, the function checks if the
* login and password are correct. If they are not, the function repeats the operation.</p>
* @param server
*/
private void login(Server server) {
boolean badLogin = true;
do {
askLoginPassword(server);
try {
// test if password is OK, if it is not, throw exception
badLogin = false;
} catch (Exception e) {
badLogin = true;
LOG.error(e.getMessage(), e);
JOptionPane.showMessageDialog(null, e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
}
} while(badLogin);
}
}
package com.maxbester.test;
公共类控制器{
公共控制员(){
服务器=新服务器(“myserver”);
登录(服务器);
}
/**
*此方法打开登录窗口并等待,直到收到来自的信号
*接收到信号后,关闭窗口
*登录窗口必须更新“服务器”的登录名和密码
*@param服务器
*/
专用同步void askLoginPassword(最终服务器){
server.setLogin(空);
server.setPassword(空);
while(server.hasConnectionId()==false){
LoginWindow LoginWindow=新的LoginWindow(Controller.this,server);
试一试{
loginWindow.setVisible(true);
等待();
}捕捉(中断异常e){
日志错误(“线程异常”,e);
showMessageDialog(登录窗口,“线程异常”,“错误”,JOptionPane.Error\u消息);
}最后{
loginWindow.dispose();
loginWindow=null;
}
}
}
/**
*首先,此方法测试服务器是否可访问
*如果是,该方法将打开一个LoginWindow以询问用户ID
*当我们获得信息时,函数会检查
*登录名和密码正确。如果不正确,函数将重复该操作
*@param服务器
*/
私有void登录(服务器){
布尔badLogin=true;
做{
askLoginPassword(服务器);
试一试{
//测试密码是否正确,如果不正确,则抛出异常
badLogin=false;
}捕获(例外e){
badLogin=true;
LOG.error(e.getMessage(),e);
showMessageDialog(null,例如getMessage(),“Error”,JOptionPane.Error_MESSAGE);
}
}while(badLogin);
}
}
确切地说,发生了什么(特别是wrt线程)有点不清楚-但我认为您可能应该去看看SwingWorker文档,了解如何在swing中处理长时间运行的任务
很难看出哪种是长时间运行的方法(我想这已经被注释掉了)——但这就是可能需要与GUI线程分开的peice
另外,您希望登录窗口何时消失在收到服务器的响应之后或之前
(根据评论编辑)从哪里从控制器构造函数调用
askLoginPassword
方法。我将把它添加到问题中。对不起,不用担心。问题是,这一切发生在哪条线索上?如果从GUI事件侦听器创建控制器
(并随后调用askLoginPassword
方法),则可能会阻塞事件线程。基本上,控制器是在主线程中创建的。我想我在某处挡住了什么,但我不知道在哪里。。。奇怪的是,它在第一次调用login
时工作,但在第二次调用时冻结。可能最好放弃线程并使用模式对话框,而不是d:长时间运行的方法[…],但这可能是以后需要调用的peice()不,这会(并且确实)阻止EDT。长时间运行的东西必须在后台线程上运行。调用器是将结果传递回EDT。我快速查看了SwingWorker,但我认为这不是我需要的。我的控制器类做了很多事情,并且不是面向HMI的。我想将GUI和业务分开(MVC模式)。对于其他HMI,我实现了观察者模式。我想我也可以为登录窗口做这件事。但是对于这样一个小窗户来说,它听起来相当沉重。。。你怎么认为?