views:

291

answers:

2

I am making a client-server Java app that is going to be MV(C, P) like. My current problem is once the client get's a Player object it needs to tell the GUI that the Player was changed and it should update everything on GUI. (Not too detailed, so entire update shouldn't be bad)

/*
 * ClientGUI.java
 *
 * Created on Dec 10, 2009, 7:51:31 PM
 */

package inequity;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Administrator
 */
public class ClientGUI extends javax.swing.JFrame
{
  private Socket socClient;
  private ObjectOutputStream socketOut;
  private ObjectInputStream socketIn;
  PriorityBlockingQueue<Command> clientQ = null;
  private Player me;

  public ClientGUI()
  {
    initComponents();
    setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
    clientQ = new PriorityBlockingQueue<Command>(Server.CLIENT_QUEUE_SIZE, new CompObj());
    me = new Player();
  }
  public void initPlayer(String name, String color)
  {
    me.name = name;
    me.color = color;
  }

  public boolean connect(int port)
  {
    try
    {
      socClient = new Socket("127.0.0.1", port);
      if(socClient.isConnected())
        System.out.println("Connected");

      socketOut = new ObjectOutputStream(socClient.getOutputStream());
      socketIn = new ObjectInputStream(socClient.getInputStream());

      socketOut.writeObject(new PlayerUpdate(me, 2));

      new ClientListener(socketIn).start();

    }
    catch(Exception e)
    {
      try
      {
        if(socketOut != null)
        {
          socketOut.flush();
          socketOut.close();
        }

        if(socketIn != null)
        {
          socketIn.close();
        }

        System.out.println("Unable to connect");
      }
      catch (IOException ex)
      {
        System.out.println("IOException caught");
        return false;
      }
    }
    return true;
  }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
  // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
  private void initComponents() {

    txtMsg = new javax.swing.JTextField();
    btnSend = new javax.swing.JButton();
    PlayerName = new javax.swing.JLabel();
    ID = new javax.swing.JLabel();
    chat = new javax.swing.JLabel();

    setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

    txtMsg.setText("Test");
    txtMsg.setName("txtMsg"); // NOI18N
    txtMsg.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        txtMsgActionPerformed(evt);
      }
    });

    btnSend.setText("Send");
    btnSend.setName("btnSend"); // NOI18N
    btnSend.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        btnSendActionPerformed(evt);
      }
    });

    PlayerName.setText("N/A");
    PlayerName.setName("PlayerName"); // NOI18N

    ID.setText("ID:");
    ID.setName("ID"); // NOI18N

    chat.setText("Chatbox");
    chat.setName("chat"); // NOI18N

    javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
    getContentPane().setLayout(layout);
    layout.setHorizontalGroup(
      layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGroup(layout.createSequentialGroup()
        .addContainerGap()
        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
          .addGroup(layout.createSequentialGroup()
            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
              .addComponent(btnSend, javax.swing.GroupLayout.DEFAULT_SIZE, 149, Short.MAX_VALUE)
              .addComponent(txtMsg, javax.swing.GroupLayout.DEFAULT_SIZE, 149, Short.MAX_VALUE)
              .addComponent(PlayerName, javax.swing.GroupLayout.DEFAULT_SIZE, 149, Short.MAX_VALUE))
            .addContainerGap())
          .addGroup(layout.createSequentialGroup()
            .addComponent(ID)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 62, Short.MAX_VALUE)
            .addComponent(chat)
            .addGap(41, 41, 41))))
    );
    layout.setVerticalGroup(
      layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
        .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
          .addComponent(ID)
          .addComponent(chat))
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
        .addComponent(PlayerName)
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
        .addComponent(txtMsg, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
        .addComponent(btnSend)
        .addContainerGap())
    );

    pack();
  }// </editor-fold>                        

    private void txtMsgActionPerformed(java.awt.event.ActionEvent evt) {                                       

    }                                      

    private void btnSendActionPerformed(java.awt.event.ActionEvent evt) {                                        
      try
      {
        if(socketOut != null)
          socketOut.writeObject(new ChatCmd(txtMsg.getText(), 10));

      }
      catch (IOException ex)
      {
        Logger.getLogger(ClientGUI.class.getName()).log(Level.SEVERE, null, ex);
      }

    }                                       

    /**
    * @param args the command line arguments
    */
    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() 
            {
              String name = "";
              String color = "";

              JoinGUI join = new JoinGUI(name, color);
              ClientGUI client = new ClientGUI();
              client.initPlayer(name, color);

              client.setVisible(join.prompt());
              client.connect(1345);
            }
        });
    }

  // Variables declaration - do not modify                     
  private javax.swing.JLabel ID;
  private javax.swing.JLabel PlayerName;
  private javax.swing.JButton btnSend;
  private javax.swing.JLabel chat;
  private javax.swing.JTextField txtMsg;
  // End of variables declaration                   


  // End of variables declaration

  public class ClientListener extends Thread
  {
    private ObjectInputStream recieving;
    public ClientListener(ObjectInputStream in)
    {
      recieving = in;
    }

    @Override
    public void run()
    {
      Command cmd;
      while(true)
      {
        try
        {
          cmd = (Command) recieving.readObject();
          clientQ.add(cmd);
          //temp code!  Put in clientProxy? Drop to clientQ?
          clientQ.take().exec(me);
          ID.setText("" + me.getID());
          PlayerName.setText("" + me.name);
          chat.setText(me.latestChatMsg);
        }
        catch (InterruptedException ex)
        {
          Logger.getLogger(ClientGUI.class.getName()).log(Level.SEVERE, null, ex);
        }
        catch (IOException ex)
        {
          Logger.getLogger(ClientListener.class.getName()).log(Level.SEVERE, null, ex);
        }
        catch (ClassNotFoundException ex)
        {
          Logger.getLogger(ClientListener.class.getName()).log(Level.SEVERE, null, ex);
        }

      }

    }

  }

}

package inequity;

import java.io.Serializable;
import java.util.ArrayList;

/**
 *
 * @author Administrator
 */

public class Player implements Serializable
{
  public ArrayList openHandShakes;
  public String latestChatMsg;
  private int ID;
  public String name;
  public String color;

  public Player()
  {
    openHandShakes = new ArrayList();
    ID = -1;
    name = "";
    color = "";
  }
  public void setID(int id)
  {
    ID = id;
  }
  public int getID()
  {
    return ID;
  }
  public void openHandShake(int shakeID)
  {
    openHandShakes.add(shakeID);
  }
  public boolean closeHandShake(int shakeID)
  {
    return true;
  }

  public void latestChat(String message)
  {
    latestChatMsg = message;
  }

}

package inequity;

import javax.swing.Box;
import javax.swing.JTextField;

/**
 *
 * @author Administrator
 */
public class JoinGUI extends javax.swing.JFrame
{
  private JTextField nameBox;
  private JTextField colorBox;

  public JoinGUI(String name, String color)
  {
    //give back name and color to clientgui
    nameBox = new JTextField("Enter Name");
    colorBox = new JTextField("Enter Color");
    Box inputMenu  = Box.createVerticalBox();

    inputMenu.add(nameBox);
    inputMenu.add(colorBox);
    add(inputMenu);
  }
  public boolean prompt()
  {
    setVisible(false);
    //wait for enter to be pressed then return true, as clientGUI is waiting to started
    //then give name and color to client
    return true;
  }
}
/*
 * Server.java
 */

package inequity;

import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import java.net.*;
import java.util.concurrent.PriorityBlockingQueue;

/**
 * The main class of the application.
 */
public class Server extends JFrame
{
  public static final int CLIENT_QUEUE_SIZE = 10;
  public static final int SERVER_QUEUE_SIZE = 10;

  public Server()
  {
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  }

  public PlayerProxy wait4client(int playerID, ServerSocket server, PriorityBlockingQueue<Command> srvQ)
  {
    Socket incoming;
    PriorityBlockingQueue<Command> clientQ = null;
    Runnable rIn;
    Runnable rOut;
    Thread tIn = null;
    Thread tOut = null;

    try
    {
      incoming = server.accept();
      clientQ = new PriorityBlockingQueue<Command>(CLIENT_QUEUE_SIZE, new CompObj());

      rIn = new Net2QRunnable(playerID, incoming, srvQ);
      rOut = new Q2NetRunnable(playerID, incoming, clientQ);

      tIn = new Thread(rIn);
      tOut = new Thread(rOut);

      tIn.start();
      tOut.start();      
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }
    clientQ.add(new HelloMsg());

    return new PlayerProxy(playerID, tIn, tOut, clientQ);
  }

  public static void main(String[] args)
  {
    PriorityBlockingQueue<Command> srvQ = new PriorityBlockingQueue<Command>(SERVER_QUEUE_SIZE, new CompObj());

    try
    {
      Game game = new Game(srvQ);
      Server srv = new Server();
      ServerSocket server = new ServerSocket(1345);

      srv.setVisible(true);

      System.out.println("Lobby");
      while (game.numPlayers() < 2)
      {
        game.addPlayer(srv.wait4client(game.numPlayers(), server, srvQ));
      }

      game.play();

      System.out.println("Game Finished");
    }
    catch (SocketException e)
    {
      System.out.println("Socket already taken, exiting.");
    }
    catch (Exception ex)
    {
      Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
    }

  }
}

I can provide full source if you'd like, but I think that's all that's needed. Again, a Player object get's sent to the client, which accepts and sets it's Player object to the received one. I want client's Player to say, "Hey GUI! I just got modified. Lemme call an update method that extracts/parses all my data!"

Any help would be appreciated.

UPDATE:

  public class ClientListener extends Thread
  {
    private ObjectInputStream recieving;
    public ClientListener(ObjectInputStream in)
    {
      recieving = in;
    }

    @Override
    public void run()
    {
      Command cmd;
      while(true)
      {
        try
        {
          cmd = (Command) recieving.readObject();
          clientQ.add(cmd);

          java.awt.EventQueue.invokeLater(new Runnable()
          {
            public void run()
            {
              try
              {
                clientQ.take().exec(me);
                if(me.changed == true)
                  updateGUI();


              } 
              catch (Exception ex)
              {
                Logger.getLogger(ClientGUI.class.getName()).log(Level.SEVERE, null, ex);
              }
            }
          });
        }
        catch (Exception ex)
        {
          Logger.getLogger(ClientListener.class.getName()).log(Level.SEVERE, null, ex);
        }

      }

    }

  }

Seems like it should be what I want right?

A: 

Just put your update code in a Runnable instance and pass it to SwingUtilities.InvokeLater(). That will put your runnable into the event dispatch queue

Or you could call invokeAndWait to block until the update is complete.

If you try to call code that updates Swing elements in another thread, it can cause instability in the rest of your code.

Chad Okere
So drop an InvokeLater/InvokeAndWait call after ClientListener gets data?
Joel Garboden
A: 

Use SwingUtilities.invokeLater to run a handler in the GUI thread.

nos