title: Login window freezes
body: <p>I developed a login window because my program connects to a SSH server.</p>
  
  <p>I open this window when starting the program. But I need to reopen it later for another server. The second time I open it, the login window freezes...</p>
  
  <p>For information</p>
  
  <p>Here is the LoginWindow code:</p>
  
  <pre><code>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 &amp;&amp; !login.isEmpty()) {
                  _server.setLogin(login);
                  char[] password = _passwordInput.getPassword();
                  if (password != null &amp;&amp; password.length &gt; 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);
          }
      }
  }
  </code></pre>
  
  <p>I store the parent class to notify it when the user has entered his login and password.</p>
  
  <p>The Server class is very simple:</p>
  
  <pre><code>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 &amp;&amp; !_login.isEmpty() &amp;&amp; _password != null &amp;&amp; !_password.isEmpty();
      }
  }
  </code></pre>
  
  <p>This window is launched by the Controller:</p>
  
  <pre><code>package com.maxbester.test;
  
  public class Controller {
  
      public Controller() {
          Server server = new Server("myserver");
          login(server);
      }
  
      /**
       * &lt;p&gt;This method opens a login window and waits until a signal is receive from
       * that window. When the signal is received, closes the window.&lt;/p&gt;
       * &lt;p&gt;The login window has to update the login and password of 'Server'.&lt;/p&gt;
       * @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;
              }
          }
      }
  
      /**
       * &lt;p&gt;First of all, this method tests if the server is reachable.&lt;/p&gt;
       * &lt;p&gt;If it is, the method opens a LoginWindow in order to ask the user ids.&lt;/p&gt;
       * &lt;p&gt;When we have the information, the function checks if the
       * login and password are correct. If they are not, the function repeats the operation.&lt;/p&gt;
       * @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);
      }
  }
  </code></pre>