2013-02-06 128 views
0

我开发了一个登录窗口,因为我的程序连接到SSH服务器。登录窗口冻结

我在启动程序时打开此窗口。但我需要稍后重新打开另一台服务器。第二次我打开它,登录窗口冻结...

有关信息

下面是登录窗口代码:

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

从哪里调用'askLoginPassword'方法? – asermax

+0

从Controller构造函数中。我将把它添加到问题中。抱歉。 – Maxbester

+0

不用担心。事情是,在哪个线程发生?如果从GUI事件侦听器创建'Controller'(并随后调用'askLoginPassword'方法),则可能会阻塞事件线程。 – asermax

回答

0

这是一个有点不清楚precisley发生了什么(尤其是WRT线程) - 但我认为,你应该去看看SwingWorker文档,看看如何处理长时间运行的任务,

很难看出哪个是长时间运行方法(我认为这被注释掉了) - 但这是可能需要与GUI线程分开保存的peice。

此外,您预计登录窗口何时会消失? - 在收到服务器响应之前或之后。

(编辑回复评论)

+0

_长时间运行的方法[..],但这是可能需要被调用()稍后的peice_否,那将会(并且确实)阻止EDT。长时间运行的东西必须在后台线程上运行。 InvokeLater将结果传递回EDT。 – kleopatra

+0

我快速浏览了SwingWorker,但我认为这不是我需要的。我的控制器类做了很多事情,不是面向HMI的。我想分离GUI和业务(MVC模式)。对于其他HMI,我实现了Observer模式。我想我也可以为LoginWindow做。但是对于这样一个小窗口听起来相当沉重......你觉得怎么样? – Maxbester