10

I'm really confused by this: some of my code is not working when i run my program normally in eclipse, but it does wok when i run through each step separately using the debug mode.

Code:

public void showConnectDialog() {
    ConnectDialog connectDialog = new ConnectDialog();
    connectDialog.setVisible(true);
    //Until here, code runs
    while(! connectDialog.getConnected()) {};
    //The next line does only run in debug
    JOptionPane.showMessageDialog(connectDialog, "Connected", "Connected", JOptionPane.INFORMATION_MESSAGE);

}

The connector (is started (as a thread) as soon as the user hits 'connect' in the dialog):

private class ServerConnector implements ActionListener, Runnable {

    @Override
    public void actionPerformed(ActionEvent e) {
        if (! IP_field.getText().equals("")) {
            if (! isConnecting) {
                new Thread(new ServerConnector(), "ServerConnector").start();

            }

        }
        else {
            JOptionPane.showMessageDialog(dialog, 
                                          "Enter an IP address", 
                                          "Enter IP", 
                                          JOptionPane.WARNING_MESSAGE);

        }

    }

    @Override
    public void run() {
        try {
            setConnecting(true);
            Socket socket = connect();
            if (socket != null) {
                ObjectOutputStream oOut = new ObjectOutputStream(socket.getOutputStream());
                ObjectInputStream oIn = new ObjectInputStream(socket.getInputStream());
                if (login(oOut, oIn)) {
                    isConnected = true;
                    setConnecting(false);

                }
                else {
                    socket.close();

                }

                setConnecting(false);

            }

        }
        catch (RSPException e) {
            e.printStackTrace();
            System.exit(1);

        }
        catch (Exception e) {
            //If an exception occurs, setConnecting() will be true. This 
            //not good, so it has to be set to false
            e.printStackTrace();
            setConnecting(false);

        }

    }

    private boolean login(ObjectOutputStream oOut, ObjectInputStream oIn) 
            throws ClassNotFoundException, IOException, RSPException {
        //Send login request action:
        oOut.writeObject(new LoginAction(ActionSender.CLIENT, getID(), 
                                         getPassword()));

        Object obj = oIn.readObject();
        if (obj instanceof LoginActionResult) {
            LoginActionResult result = (LoginActionResult) obj;
            if (result.getResult() == LoginResults.SUCCES) {
                return true;

            }
            else if (result.getResult() == LoginResults.FAIL_ON_ID) {
                JOptionPane.showMessageDialog(dialog, 
                                              "Invalid password or ID", 
                                              "Can't login", 
                                              JOptionPane.ERROR_MESSAGE);
                return false;

            }
            else if (result.getResult() == LoginResults.FAIL_ON_PASSWORD) {
                JOptionPane.showMessageDialog(dialog, 
                                              "Invalid password or ID", 
                                              "Can't login", 
                                              JOptionPane.ERROR_MESSAGE);
                return false;

            }
            else if (result.getResult() == LoginResults.SERVER_FULL) {
                JOptionPane.showMessageDialog(dialog, 
                                              "Couldn't connect: \n" +
                                              "Server is full", 
                                              "Failed to connect", 
                                              JOptionPane.WARNING_MESSAGE);
                return false;

            }
            else {
                return false;

            }

        }
        else {
            System.out.println(obj);
            throw new RSPException("Server is not following the protocol.");

        }

    }

    private void setConnecting(boolean connecting) {
        if (connecting) {
            SwingUtilities.invokeLater(new Runnable() {

                @Override
                public void run() {
                    connectButton.setEnabled(false);

                }
            });
            SwingUtilities.invokeLater(new Runnable() {

                @Override
                public void run() {
                    connectButton.setText("Connecting...");

                }
            });

        }
        else {
            SwingUtilities.invokeLater(new Runnable() {

                @Override
                public void run() {
                    connectButton.setText("Connect");

                }
            });
            SwingUtilities.invokeLater(new Runnable() {

                @Override
                public void run() {
                    connectButton.setEnabled(true);

                }
            });

        }

        isConnecting = connecting;

    }

    private String getAddressFromTextField() {
        return IP_field.getText();

    }

    private InetAddress getInetAddress(String fullAddress) {
        try {
            if (fullAddress.contains(":")) {
                String[] splitAddress = fullAddress.split(":");
                return InetAddress.getByName(splitAddress[0]);

            }
            else {
                return InetAddress.getByName(fullAddress);

            }
        }
        catch (UnknownHostException e) {
            return null;

        }

    }

    private int getPort(String fullAddress) {
        try {
            String[] splittedAddress = fullAddress.split(":");
            return Integer.valueOf(splittedAddress[1]);

        }
        catch (NumberFormatException ex) {
            return -1;

        }
        catch (NullPointerException 
             | ArrayIndexOutOfBoundsException 
             | PatternSyntaxException ex) {
            //Returning default port value: 25566, because no port was given
            return 25566;

        }

    }

    @SuppressWarnings("resource")
    private Socket connect() {
        Socket socket = null;

        InetAddress address = null;
        if ((address = getInetAddress(getAddressFromTextField())) == null) {
            return null;

        }
        int port = getPort(getAddressFromTextField());

        try {
            socket = new Socket(address, port);

        }
        catch (ConnectException e ) {
            Socket retrySocket = null;
            if ((retrySocket = retryConnect(address, port)) == null) {
                JOptionPane.showMessageDialog(dialog,
                                              "Connection timed out", 
                                              "Failed to connect", 
                                              JOptionPane.ERROR_MESSAGE);
                setConnecting(false);

            }
            else {
                socket = retrySocket;

            }

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

        }

        return socket;

    }

    private Socket retryConnect(InetAddress address, int port) {
        Thread waitThread = new Thread(new Runnable() {

            @Override
            public void run() {
                try {
                    //Will wait 15(000) (milli)seconds before stopping with
                    //trying to connect.
                    //One second (1000 millis) is for debugging and testing
                    Thread.sleep(1000);

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

                }

            }

        });

        waitThread.start();

        while (waitThread.isAlive()) {
            try {
                return new Socket(address, port);

            }
            catch (ConnectException e) {
                //Do nothing, will re-attempt to connect.

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

            }

        }

        return null;

    }

    private String getID() {
        return ID_field.getText();

    }

    private String getPassword() {
        if (getID().equals("master")) {
            return "masterPassword";

        }
        else {
            return new String(passwordField.getPassword());

        }

    }

}

getConnected() returns true as soon as it's connected to the server. The connector is running on a separate thread.

EDIT: I tried to put code in the getConnected() while block, and then it works. Why does it works then and not else?

cvbattum
  • 811
  • 2
  • 15
  • 32
  • 3
    Could be a race condition. Can you post the code to `ConnectDialog`? – austin Jun 12 '13 at 16:29
  • Do you evaluate any expressions during your break points? Those evaluations can actually change the state of your program (maybe you got lucky and it changed it into a working state?) – David says Reinstate Monica Jun 12 '13 at 16:32
  • @austin The ConnectDialog code is 700 lines long, so i'll only post the code to the actual connector (which is still very long...) Hope your fine with it. – cvbattum Jun 12 '13 at 16:35
  • You could work with `wait` and `notify` here. The `showConnectDialog` waits until the `ServerConnector` thread notifies it. That way you can avoid the `while` loop and your program won't block a cpu core. – Tom May 31 '15 at 08:08
  • You don't show where your `isConnecting` variable is defined... is it defined as `volatile`? https://docs.oracle.com/javase/tutorial/essential/concurrency/atomic.html – Nate Feb 11 '16 at 14:45
  • I confirm, the thread.sleep does work on that case i used it but with 20 milisecs as parameter, thanks a lot for your help ! hope this gonna help someone else in the future – WizziLife Jan 04 '17 at 10:09

4 Answers4

5

I had the same Problem, but with some more specification. The code was working fine in 32bit but I had this issue in 64bit (I am using native library so I need to maintain both).

The solution I found is to add Thread.sleep() in the while loop. I don't know why it works, so your guess is as good as mine.

A better solution would probably to implement an Observer Pattern instead of having an infinite loop. But that would require some re-factoring.

DomLavoie
  • 81
  • 2
  • 4
3

Using Thread.sleep(), as the other answers have suggested, should solve the problem but it is not a very good approach. Instead, we should be using Thread.yield().

Why yield and not sleep?

Refer: Difference between Thread.Sleep(0) and Thread.Yield() and Are Thread.sleep(0) and Thread.yield() statements equivalent?

Why this works?

When we just run the threads, the OS puts them to "idle" state and when it is expected to "wake-up", it does not. On the other hand, in debug mode, we have a controlled environment. The OS has little control over it as everything goes on step-by-step, slowly. If we run the debug a few times without any break-points, after a few successful runs, we should see the same effect.

Community
  • 1
  • 1
vish4071
  • 5,135
  • 4
  • 35
  • 65
2

I had a very similar problem with a "while" loop that wouldn't run and that loop was my main routine. How I got the loop to run was that the very first thing that was done in the loop was a sleep:

    try
        {Thread.sleep(0);}
    catch (Exception e)
        {e.printStackTrace();}

This was enough to get everything going.

tudor balus
  • 149
  • 2
  • 10
1

I had same problem in UIAutomator with UiObject2 wait(Until.findObject(),20) .

Thread.yield() - works for me