Search code examples
javaswingsocketsthread-synchronization

How to update a java swing gui witha server from another thread?


I have this Java Swing application that starts a new thread that uses a executor pool to open a socket server every time a incomming client tries to establish a connection.

The application need two buttons, one to start and another to stop the server. What I want is to show the server status, and disable the opposed button until its status changes.

This is what I have by now, but I don't know how could I communicate with the EDT when the thread stops. What I can do is just check the isRunning() method.

Would it be better to use a SwingWorker?

public class ServerManager implements Runnable {

    private Executor mExecutor          = Executors.newSingleThreadExecutor();
    private ServerSocket mServerSocket  = null;
    private int mDefaultPort            = 43012;    
    private volatile boolean isRunning  = false;

    public ServerManager (int port){
        mDefaultPort = port;        
    }

    @Override
    public void run() {
        try {           
            mServerSocket = new ServerSocket(mDefaultPort);
            isRunning = true;
            while (isRunning){
                mExecutor.execute(new IncomingClientThread(mServerSocket.accept()));
            }
        } catch (IOException e) {           
            e.printStackTrace();
        } finally {
            if(mServerSocket != null){
                stop();
                System.out.println("Server closed");
            }
        }
    }

    public void stop(){
        try {
            mServerSocket.close();
            isRunning = false;
        } catch (IOException e) {
            throw new RuntimeException("Error closing server", e);
        }
    }

    public synchronized boolean isRunning() {
        return isRunning;
    }

    public int getServerPort (){
        return mDefaultPort;
    }
}

And this is what I have in the GUI thread: I'm using just one button and changing its text everytime it's pressed, but if the server disconnects for some reason, the button stays the same.

connectButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                if(mServer.isStopped()){
                    new Thread (mServer).start();
                    connectButton.setText("Desconectar");
                    infoLabel.setText("Servidor online en IP: " + NetworkUtils.getLocalIpAddress()
                            + " puerto: " + mServer.getServerPort());
                    System.out.println(mServer.getIpAddress());
                }else{
                    mServer.stop();
                    connectButton.setText("Conectar");
                    infoLabel.setText("Offline");
                }
            }
        });

Any help is wellcome! Thanks.


Solution

  • One possible solution is to give it part of the functionality of a SwingWorker -- give it a SwingPropertyChangeSupport object, and allow your GUI to listen for and respond to state changes.

    e.g.,

    public class ServerManager implements Runnable {
      public static final String IS_RUNNING = "is running"; // for the Event's name
      private SwingPropertyChangeSupport propChngSupport = new SwingPropertyChangeSupport(this);
      private volatile boolean isRunning  = false;
      // other variables
    
      // addPropertyChangeListener(...) {...} goes here
      // removePropertyChangeListener(...)  {...} goes here
    
      public void setIsRunning(boolean isRunning) {
        boolean newValue = isRunning;
        boolean oldValue = this.isRunning;
        this.isRunning = isRunning;
        propChngSupport.firePropertyChange(IS_RUNNING, oldValue, newValue);    
      }
    
      public void run() {
        // ....
      }
    
      // other methods
    }
    

    The key being to never change the isRunning property outside of its setter method.