Easy to Learn Java: Programming Articles, Examples and Tips

Start with Java in a few days with Java Lessons or Lectures

Home

Code Examples

Java Tools

More Java Tools!

Java Forum

All Java Tips

Books

Submit News
Search the site here...
Search...
 

Swing Chapter 16. (Advanced topics) Desktops and Internal Frames. Easy for reading, Click here!

Custom Search
Swing Chapter 16. (Advanced topics) Desktops and Internal Frames. Easy for reading, Click here!

[ Return to Swing (Book) ]

Page: 5/5 



Previous Page Previous Page (4/5)
Subpages: 1. JDesktopPane and JInternalFrame 
2.
Internalizable/Externalizable frames 
3.
Cascading and outline dragging mode 
4. An X windows style desktop environment 
5. A networked multi-user desktop using sockets 

16.5  A basic multi-user desktop environment using sockets

Collaborative environments are becoming more commonplace as the internet flourishes. They will no doubt continue to grow in popularity. Imagine a class taught using an interactive whiteboard or a set of whiteboards each contained in an internal frame.

In this section we show how to construct a basic multi-user JDesktopPane using sockets. We support a server and only one client. Both the server and client-side users can move, resize, and close frames, as well as chat in a console window. (We only allow the client to create frames.) All JInternalFrame actions invoked by one user are sent to the other user's desktop using a lightweight message-passing scheme. What we end up with is the beginnings of a true multi-user desktop environment.

Note: We've tested this environment between South Carolina and New York with satisfatory response times (using 28.8 modems dialed in to typical ISPs).

Before we present the code, it is helpful here to briefly summarize the network-centric classes that this example takes advantage of (see the API docs or the Java tutorial for more thorough coverage).

16.5.1  Socket

class java.net.Socket

A Socket is a connection to a single remote machine. Each Socket has an InputStream and an OutputStream associated with it. Data can be sent to the remote machine by writing to the OutputStream and data is retrieved from the remote machine via the InputStream. Each Socket also has an InetAdress instance associated with it which encapsulates the IP address that it is connected to.

16.5.2  SocketServer

class java.net.SocketServer

A SocketServer is used to establish Socket-to-Socket connections. Usually a SocketServer calls its accept() method to wait for a client to connect. Once a client connects a ServerSocket can return a Socket that the host machine uses for communication with that client.

16.5.3  InetAddress

class java.net.InetAddress

Encapsulates information about an IP address, such as the host name and the address.

This example works by sending messages back and forth between the client and server. Each message is  received and processed identically on each end. Two types of messages can be sent:

Chat messages: messages of this type can be of any length and always begin with "cc".

Internal frame messages: messages of this type are always 29 characters long, are represeted only by numeric characters, and have a distinct six field structure.

Figure 16.7 illustrates our internal frame message structure.

Figure 16.7 Internal frame message structure

<<file figure16-7.gif>>

ID represents the WindowManager method to invoke.

TAG is a unique identifier returned by each internal frame's overridden toString() method.

new X is the new x coordinate of the internal frame (if applicable).

new X is the new y coordinate of the internal frame (if applicable).

new width is the new width of the internal frame (if applicable).

new height is the new height of the internal frame (if applicable).

We will discuss how and when both types of messages are constructed and interpreted after we present the code. The server is implemented as class JavaXServer, the client as JavaXClient. JavaXClient was largely built from JavaXServer. We have highlighted the changes below, and inserted comments to denote where code has been modifed or unchanged.

The WindowManager class from the last section has been completely rebuilt and defined in a separate class file. The WindowWatcher class remains unchanged, using the XXResizeEdge classes in our resize package to allow full resizability. Both JavaXServer and JavaXClient use instances of the new WindowManager to manage their desktop, send messages, and interpret calls invoked by their message receiving mechanism (the processMessage() method).

Figure 16.8 JavaXClient with established connection

<<file figure16-8.gif>>

Figure 16.9 JavaXServer with established connection

<<file figure16-9.gif>>

The Code: JavaXServer.java

see \Chapter16\3

import java.beans.PropertyVetoException;

import javax.swing.*;

import java.awt.event.*;

import java.io.*;

import java.awt.*;

import java.net.*;

public class JavaXServer extends JFrame implements Runnable

{

  protected int m_count;

  protected int m_tencount;

  protected int m_wmX, m_wmY;

  protected JDesktopPane m_desktop;

  protected WindowManager m_wm;

  protected JViewport viewport;

  protected JTextArea m_consoletext, m_consolechat;

  protected JTextField m_chatText;

  protected boolean m_connected;

  protected JLabel m_status;

  protected DataInputStream m_input;

  protected DataOutputStream m_output;

  protected Socket m_client;

  protected ServerSocket m_server;

  protected Thread m_listenThread;

  protected ConThread m_conthread;

  public JavaXServer() {

    setTitle("JavaX Server");

    m_count = m_tencount = 0;

    m_desktop = new JDesktopPane();

    m_status = new JLabel("No Client");

    JScrollPane scroller = new JScrollPane();

    m_wm = new WindowManager(m_desktop);

    m_desktop.setDesktopManager(m_wm);

    m_desktop.add(m_wm.getWindowWatcher(),

      JLayeredPane.PALETTE_LAYER);

    m_wm.getWindowWatcher().setBounds(555,5,100,100);

    viewport = new JViewport() {

      public void setViewPosition(Point p) {

        super.setViewPosition(p);

        m_wm.getWindowWatcher().setLocation(

          m_wm.getWindowWatcher().getX() +

            (getViewPosition().x-m_wmX),

          m_wm.getWindowWatcher().getY() +

            (getViewPosition().y-m_wmY));

        m_wmX = getViewPosition().x;

        m_wmY = getViewPosition().y;

      }

    };

    viewport.setView(m_desktop);

    scroller.setViewport(viewport);

    ComponentAdapter ca = new ComponentAdapter() {

      JViewport view = viewport;

      public void componentResized(ComponentEvent e) {

        m_wm.getWindowWatcher().setLocation(

          view.getViewPosition().x + view.getWidth()-

            m_wm.getWindowWatcher().getWidth()-15,

          view.getViewPosition().y + 5);

      }

    };

    viewport.addComponentListener(ca);

    getContentPane().setLayout(new BorderLayout());

    getContentPane().add("Center", scroller);

    getContentPane().add("South", m_status);

    setupConsole();

    Dimension dim = getToolkit().getScreenSize();

    setSize(800,600);

    setLocation(dim.width/2-getWidth()/2,

      dim.height/2-getHeight()/2);

    m_desktop.setPreferredSize(new Dimension(1600,1200));

    WindowListener l = new WindowAdapter() {

      public void windowClosing(WindowEvent e) {

        System.exit(0);

      }

    };       

    addWindowListener(l);

    setVisible(true);

  }

  public void setupConsole() {

    JInternalFrame console = new JInternalFrame(

     "JavaX Server Console",

     false, false, false, false) {

      int TAG = m_count;

      public String toString() {

        return "" + TAG;

      }

    };

    m_count++;

    console.setBounds(20, 20, 500, 300);

    JPanel chatPanel = new JPanel();

    JLabel chatLabel = new JLabel(" Chat");

    chatPanel.setLayout(new BorderLayout());

    m_consoletext = new JTextArea();

    m_consoletext.setPreferredSize(new Dimension(500,50));

    m_consoletext.setLineWrap(true);

    m_consoletext.setText("Server Started." +

      "\nWaiting for client...");

    m_consoletext.setEditable(false);

    m_consolechat = new JTextArea();

    m_consolechat.setLineWrap(true);

    m_consolechat.setEditable(false);

    m_chatText = new JTextField();

    m_chatText.addActionListener(new ChatAdapter());

    JButton chatSend = new JButton("Send");

    chatSend.addActionListener(new ChatAdapter());

    JPanel sendPanel = new JPanel();

    sendPanel.setLayout(new BorderLayout());

    sendPanel.add("Center", m_chatText);

    sendPanel.add("West", chatSend);

    JScrollPane cscroller1 = new JScrollPane(m_consoletext);

    JScrollPane cscroller2 = new JScrollPane(m_consolechat);

    chatPanel.add("North", chatLabel);

    chatPanel.add("Center", cscroller2);

    chatPanel.add("South", sendPanel);

    JSplitPane splitter = new JSplitPane(

      JSplitPane.VERTICAL_SPLIT, true, cscroller1, chatPanel);

    console.getContentPane().add(splitter);

    m_desktop.add(console);

    m_wm.getWindowWatcher().repaint();

    try {

      m_server = new ServerSocket(5000,500);

    }

    catch (IOException e) {

      m_consoletext.append("\n" + e);

    }

    m_conthread = new ConThread();

  }

  public void run() {

    while (m_connected) {

      try {

        processMessage(m_input.readUTF());

      }

      catch (IOException e) {

        m_consoletext.append("\n" + e);

        m_connected = false;

      }

    }

  }

  public void newFrame() {

    JInternalFrame jif = new JInternalFrame("Frame " + m_count,

     true, true, false, false) {

      int TAG = m_count;

      public String toString() {

        return "" + TAG;

      }

    };

    jif.setBounds(20*(m_count%10) + m_tencount*80,

      20*(m_count%10), 200, 200);

    m_desktop.add(jif);

    try {            

      jif.setSelected(true);        

    }

    catch (PropertyVetoException pve) {

      System.out.println("Could not select " + jif.getTitle());

    }

    m_count++;

    if (m_count%10 == 0) {

      if (m_tencount < 3)

        m_tencount++;

      else

        m_tencount = 0;

    }

  }

  public void processMessage(String s) {

    if (s.startsWith("cc")) {

      m_consolechat.append("CLIENT: " + s.substring(2) + "\n");

      m_consolechat.setCaretPosition(

        m_consolechat.getText().length());

    }

    else {

      int id = (Integer.valueOf(s.substring(0,2))).intValue();

      m_wm.setPropagate(false);

      if (id == 16) {

        newFrame();

      }

      else {

        Component[] components = m_desktop.getComponentsInLayer(0);

        int index = 0;

        int tag = (Integer.valueOf(s.substring(2,5))).intValue();

        int param1 =

          (Integer.valueOf(s.substring(5,11))).intValue();

        int param2 =

          (Integer.valueOf(s.substring(11,17))).intValue();

        int param3 =

          (Integer.valueOf(s.substring(17,23))).intValue();

        int param4 =

          (Integer.valueOf(s.substring(23))).intValue();

        boolean found = false;

        for (int i=components.length-1; i>-1;i--) {

          if (components[i] instanceof JInternalFrame) {

            if (Integer.valueOf(

             components[i].toString()).intValue() == tag) {

              try {            

                ((JInternalFrame) components[i]).setSelected(true);

                ((JInternalFrame) components[i]).toFront();

                index = i;

                found = true;

                break;       

              }

              catch (PropertyVetoException pve) {

                System.out.println(

                 "Could not select JInternalFrame with tag " + tag);

              }

            }

          }

        }

        if (found == false) return;

        switch (id)

        {

          case 1:

            m_wm.activateFrame((JInternalFrame) components[index]);

            break;

          case 2:

            m_wm.beginDraggingFrame((JComponent) components[index]);

            break;

          case 3:

            m_wm.beginResizingFrame(

             (JComponent) components[index], param1);

            break;

          case 4:

            m_wm.closeFrame((JInternalFrame) components[index]);

            break;

          // case 5: not implemented

          // case 6: not implemented

          case 7:

            m_wm.dragFrame(

              (JComponent)components[index], param1, param2);

            break; 

          case 8:

            m_wm.endDraggingFrame((JComponent) components[index]);

            break;

          case 9:

            m_wm.endResizingFrame((JComponent) components[index]);

            break;

          // case 10: not implemented

          // case 11: not implemented

          // case 12: not implemented

          case 13:

            m_wm.openFrame((JInternalFrame) components[index]);

            break;

          case 14:

            m_wm.resizeFrame(

              (JComponent) components[index], param1,

                param2, param3, param4);

            break;

          case 15:

            m_wm.setBoundsForFrame(

              (JComponent) components[index], param1,

                param2, param3, param4);

            break;

        }

      }

      m_wm.setPropagate(true);

    }

    m_desktop.repaint();

  }

  public static void main(String[] args) {

    new JavaXServer();

  }

  class ChatAdapter implements ActionListener {

    public void actionPerformed(ActionEvent e) {

      m_wm.sendMessage("cc" + m_chatText.getText());

      m_consolechat.append("SERVER: " +

        m_chatText.getText() + "\n");

      m_chatText.setText("");

    }

  }

  class ConThread extends Thread

  {

    ConThread() { start(); }

    public void run() {

      while(true) {

        try {

          m_client = m_server.accept();

          m_connected = true;

          m_input = new DataInputStream(m_client.getInputStream());

          m_output = new DataOutputStream(

            m_client.getOutputStream());

          m_wm.setOutputStream(m_output);

          m_consoletext.append("\nStreams established...");

          m_listenThread = new Thread(JavaXServer.this);

          m_listenThread.start();

          m_status.setText("Client Connected from: "

            + m_client.getInetAddress());

          m_consoletext.append("\nClient Connected from: "

            + m_client.getInetAddress());

        }

        catch (Exception ex) {

          m_consoletext.append("\n" + ex);

        }

      }

    }

  }

}

The Code: WindowManager.java

see \Chapter16\3

import javax.swing.*;

import java.io.*;

import java.awt.*;

public class WindowManager extends DefaultDesktopManager

{

  //ID 16 means new Frame

  protected static final int ACIVATE_ID = 1;

  protected static final int BEGINDRAG_ID = 2;

  protected static final int BEGINRESIZE_ID = 3;

  protected static final int CLOSE_ID = 4;

  protected static final int DEACTIVATE_ID = 5;

  protected static final int DEICONIFY_ID = 6;

  protected static final int DRAG_ID = 7;

  protected static final int ENDDRAG_ID = 8;

  protected static final int ENDRESIZE_ID = 9;

  protected static final int ICONIFY_ID = 10;

  protected static final int MAXIMIZE_ID = 11;

  protected static final int MINIMIZE_ID = 12;

  protected static final int OPEN_ID = 13;

  protected static final int RESIZE_ID = 14;

  protected static final int SETBOUNDS_ID = 15;

  protected WindowWatcher ww;

  protected DataOutputStream m_output;

  protected JDesktopPane m_desktop;

  protected boolean m_prop;

  public WindowManager(JDesktopPane desktop) {

    m_desktop = desktop;

    m_prop = true;

    ww = new WindowWatcher(desktop);

  }

  public WindowWatcher getWindowWatcher() { return ww; }

  public void setOutputStream(DataOutputStream output) {

    m_output = output;

  }

  public void sendMessage(String s) {

    try {

      if (m_output != null)

        m_output.writeUTF(s);

    }

    catch (IOException e) {}

  }

  public void setPropagate(boolean b) {

    m_prop = b;

  }

  public String getStringIndex(Component f) {

    String s = f.toString();

    while (s.length() < 3)

      s = ("0").concat(s);    

    return s;

  }

  public String getString(int number) {

    String s;

    if(number < 0)

      s = "" + (-number);

    else

      s = "" + number;

    while (s.length() < 6)

      s = ("0").concat(s);    

    if (number < 0)

      s = "-" + s.substring(1,6);

    return s;

  }

  public void activateFrame(JInternalFrame f) {

    String index = getStringIndex(f);

    super.activateFrame(f);

    ww.repaint();

    if (m_prop)

      sendMessage("01" + index + "000000000000000000000000");

  }

  public void beginDraggingFrame(JComponent f) {

    String index = getStringIndex(f);

    super.beginDraggingFrame(f);

    ww.repaint();

    if (m_prop)

      sendMessage("02" + index + "000000000000000000000000");

  }

  public void beginResizingFrame(JComponent f, int direction) {

    String index = getStringIndex(f);

    String dir = getString(direction);

    super.beginResizingFrame(f,direction);

    ww.repaint();

    if (m_prop)

      sendMessage("03" + index + dir + "000000000000000000");

  }

  public void closeFrame(JInternalFrame f) {

    String index = getStringIndex(f);

    super.closeFrame(f);

    ww.repaint();

    if (m_prop)

      sendMessage("04" + index + "000000000000000000000000");

  }

  public void deactivateFrame(JInternalFrame f) {

    super.deactivateFrame(f);

    ww.repaint();

    // ID 05 - not implemented

  }

  public void deiconifyFrame(JInternalFrame f) {

    super.deiconifyFrame(f);

    ww.repaint();

    // ID 06 - not implemented

  }

  public void dragFrame(JComponent f, int newX, int newY) {

    String index = getStringIndex(f);

    String x = getString(newX);

    String y = getString(newY);

    f.setLocation(newX, newY);

    ww.repaint();

    if (m_prop)

      sendMessage("07" + index + x + y +"000000000000");

  }

  public void endDraggingFrame(JComponent f) {

    String index = getStringIndex(f);

    super.endDraggingFrame(f);

    ww.repaint();

    if (m_prop)

      sendMessage("08" + index + "000000000000000000000000");

  }

  public void endResizingFrame(JComponent f) {

    String index = getStringIndex(f);

    super.endResizingFrame(f);

    ww.repaint();

    if (m_prop)

      sendMessage("09" + index + "000000000000000000000000");

  }

  public void iconifyFrame(JInternalFrame f) {

    super.iconifyFrame(f);

    ww.repaint();

    // ID 10 - not implemented

  }

  public void maximizeFrame(JInternalFrame f) {

    String index = getStringIndex(f);

    super.maximizeFrame(f);

    ww.repaint();

    // ID 11 - not implemented

  }

  public void minimizeFrame(JInternalFrame f) {

    super.minimizeFrame(f);

    ww.repaint();

    // ID 12 - not implemented

  }

  public void openFrame(JInternalFrame f) {

    String index = getStringIndex(f);

    super.openFrame(f);

    ww.repaint();    

    if (m_prop)

      sendMessage("13" + index + "000000000000000000000000");

  }

  public void resizeFrame(JComponent f,

   int newX, int newY, int newWidth, int newHeight) {

    String index = getStringIndex(f);

    String x = getString(newX);

    String y = getString(newY);

    String w = getString(newWidth);

    String h = getString(newHeight);

    f.setBounds(newX, newY, newWidth, newHeight);

    ww.repaint();

    if (m_prop)

      sendMessage("14" + index + x + y + w + h);

  }

  public void setBoundsForFrame(JComponent f,

   int newX, int newY, int newWidth, int newHeight) {

    String index = getStringIndex(f);

    String x = getString(newX);

    String y = getString(newY);

    String w = getString(newWidth);

    String h = getString(newHeight);

    if (newWidth > m_desktop.getWidth())

      newWidth = m_desktop.getWidth();

    if (newHeight > m_desktop.getHeight())

      newHeight = m_desktop.getHeight();

    f.setBounds(newX, newY, newWidth, newHeight);

    ww.repaint();

    if (m_prop)

      sendMessage("15" + index + x + y + w + h);

  }

}

Code: JavaXClient.java

see \Chapter16\3

import java.beans.PropertyVetoException;

import javax.swing.*;

import java.awt.event.*;

import java.io.*;

import java.awt.*;

import java.net.*;

public class JavaXClient extends JFrame implements Runnable

{

  protected int m_count;

  protected int m_tencount;

  protected int m_wmX, m_wmY;

  protected JButton m_newFrame;

  protected JDesktopPane m_desktop;

  protected WindowManager m_wm;

  protected JViewport viewport;

  protected JTextArea m_consoletext, m_consolechat;

  protected JTextField m_text, m_chatText;

  protected boolean m_connected;

  protected JLabel m_status;

  protected DataInputStream m_input;

  protected DataOutputStream m_output;

  protected Socket m_client;

  protected Thread m_listenThread;

  // ServerSocket and ConThread code removed.

  protected JButton m_connect;

  public JavaXClient() {

    setTitle("JavaX Client");

    m_count = m_tencount = 0;

    m_desktop = new JDesktopPane();

    m_status = new JLabel("Not Connected");

    JScrollPane scroller = new JScrollPane();

    m_wm = new WindowManager(m_desktop);

    m_desktop.setDesktopManager(m_wm);

    m_desktop.add(m_wm.getWindowWatcher(),

      JLayeredPane.PALETTE_LAYER);

    m_wm.getWindowWatcher().setBounds(555,5,100,100);

    viewport = new JViewport() {

      //...identical in JavaXServer

    };

    viewport.setView(m_desktop);

    scroller.setViewport(viewport);

    ComponentAdapter ca = new ComponentAdapter() {

      //...identical in JavaXServer

    };

    viewport.addComponentListener(ca);

    m_newFrame = new JButton("New Frame");

    m_newFrame.addActionListener(new ActionListener() {

      public void actionPerformed(ActionEvent e) {

        m_wm.setPropagate(false);

        newFrame();

        if (m_connected)

          m_wm.sendMessage("16000000000000000000000000000");

        m_wm.setPropagate(true);

      }

    });

    m_newFrame.setEnabled(false);

    JPanel topPanel = new JPanel(true);

    topPanel.add(m_newFrame);

    m_connect = new JButton("Connect");

    m_connect.addActionListener(new ActionListener() {

      public void actionPerformed(ActionEvent e) {

        if (m_listenThread == null) {

          Thread connector = new Thread() {

            public void run() {

              try

              {

                m_consoletext.append(

                  "\nTrying " + m_text.getText() + " ...");

                m_client = new Socket(

                  InetAddress.getByName(m_text.getText()),5000);

                m_input = new DataInputStream(

                  m_client.getInputStream());

                m_output = new DataOutputStream(

                  m_client.getOutputStream());

                m_connected = true;

                m_listenThread = new Thread(JavaXClient.this);

                m_listenThread.start();

                m_wm.setOutputStream(m_output);

                m_consoletext.append("\nStreams established...");

                m_status.setText("Connected to " + m_text.getText());

                m_connect.setEnabled(false);

                m_newFrame.setEnabled(true);

              }

              catch (Exception ex) {

                m_consoletext.append("\n" + ex);

                m_newFrame.setEnabled(false);

              }

            }

          };

          connector.start();

        }

      }

    });

    JPanel XPanel = new JPanel();

    XPanel.setLayout(new BorderLayout());

    JLabel hl = new JLabel("Connect to: ", SwingConstants.CENTER);

    m_text = new JTextField(15);

    XPanel.add("North", hl);

    XPanel.add("Center", m_text);

    XPanel.add("East", m_connect);

    JPanel upperPanel = new JPanel();

    upperPanel.setLayout(new BorderLayout());

    upperPanel.add("Center", XPanel);

    upperPanel.add("East",topPanel);

    getContentPane().setLayout(new BorderLayout());

    getContentPane().add("North", upperPanel);

    getContentPane().add("Center", scroller);

    // Unchanged code

  }

  public void setupConsole() {

    JInternalFrame console = new JInternalFrame(

     "JavaX Client Console",

     false, false, false, false) {

      int TAG = m_count;

      public String toString() {

        return "" + TAG;

      }

    };

    m_count++;

    console.setBounds(20, 20, 500, 300);

    JPanel chatPanel = new JPanel();

    JLabel chatLabel = new JLabel(" Chat");

    chatPanel.setLayout(new BorderLayout());

    m_consoletext = new JTextArea();

    m_consoletext.setPreferredSize(new Dimension(500,50));

    m_consoletext.setLineWrap(true);

    m_consoletext.setText("Client Started...");

    m_consoletext.setEditable(false);

    // The remainder of this method is identical

    // to JavaXServer's setupConsole() method.

    // However, we have removed the ServerSocket

    // code from the end.

  }

  public void run() {

    // ...identical to JavaXServer's run() method.

  }

  public void newFrame() {

    // ...identical to JavaXServer's newFrame() method.

  }

  public void processMessage(String s) {

    if (s.startsWith("cc")) {

      m_consolechat.append("SERVER: " + s.substring(2) + "\n");

      m_consolechat.setCaretPosition(

        m_consolechat.getText().length());

    }

    else {

    // With the exception of the highlighted code

    // above, this method is identical to JavaXServer's

    // processMessage() method.

  }

  public static void main(String[] args) {

    new JavaXClient();

  }

  class ChatAdapter implements ActionListener {

    public void actionPerformed(ActionEvent e) {

      m_wm.sendMessage("cc" + m_chatText.getText());

      m_consolechat.append("CLIENT: " + m_chatText.getText() + "\n");

      m_chatText.setText("");

    }

  }

  // ConThread inner class removed.

}

Understanding the Code

Class JavaXServer

JavaXServer implements the Runnable interface allowing us to define a separate thread of execution. Several instance variables are necessary:

int m_count, int m_tencount: used for cascading

int m_wmX: keeps track of the most recent x coordinate of the desktop scrollpane's view position.

int m_wmY: keeps track of the most recent y coordinate of the desktop scrollpane's view position.

JDesktopPane m_desktop: our desktop pane.

WindowManager m_wm: our custom DesktopManager implementation.

JViewport viewport:  The viewport of the scrollpane that will contain our desktop.

JTextArea m_consoletext: The console text area used to display server status information.

JTextArea m_consolechat: The console text area used for chatting between server and client.

boolean m_connected: Flag specifying whether a client is connected or not.

JLabel m_status: Status bar used to display the IP address of the connected client.

DataInputStream m_input: The DataInputStream of the client connection Socket.

DataOutputStream m_output: The DataOutputStream of the client connection Socket.

Socket m_client: The Socket created when a client connects.

ServerSocket m_server: Used to wait for an incoming client and establish the client Socket.

Thread m_listenThread: A handle used for creating and starting the JavaXServer thread.

ConThread m_conthred: An instance of our custom Thread extended inner class used to allow the ServerSocket to wait for client connections without hogging our application's thread.

The JavaXServer constructor performs familiar GUI layout tasks, and is very similar to the JavaXWin constructor we studied in the last section. Before the frame is made visible our setupConsole() method is called. This method is responsible for contructing our chat console internal frame (it also acts as a message log for the server). We override this JInternalFrame's toString() method to return a unique TAG which is the value of the m_count variable at the time of its creation. Since the console is the first frame created it will have a TAG of 0. We then increment m_count so that the next frame, created in the newFrame() method (see below), will have a different TAG value. This is how we identify frames when sending internal frame messages.

The console contains two text areas, a "Send" button and a text field. The send button and text field are used for chatting, the upper text area is used to display server status information, and the lower text field is used to display chat text. We attach an instance of our custom ChatAdapater class (see below) to both the send button, chatSend, and the text field, m_chatText.

The setupConsole() method ends by actually starting the server. We create a new ServerSocket on port 5000 with queue length 500. A queue length of 500 represents the maximum amount of messages that can be buffered at any given time by the ServerSocket.

Note: This example uses a fixed port. In professional applications the server would most likely provide the operator with a field to enter the desired port number. Also, note that a maximum queue length of 500 is not really necessary here, as we are only expecting one client to connect through this ServerSocket. However, it does not add any extra overhead, and if for some reason this port gets bombarded with extraneous messages this will give our client a better chance of getting in.

The run() method defines the separate thread of execution that is created and called within the ConThread class (see below). When a client is connected this method continuously reads data from the client Socket, m_client, and sends it to our custom processMessage() method (see below).

Our newFrame() method looks familiar, however, each JInternalFrame created gets assigned a unique TAG, which is the value of the m_count variable at the time of its creation, and its toString() method is overriden to return this tag. This is how we identify destinations of internal frame messages.

The processMessage() method takes a String parameter representing a message from the client. First we check if it is a chat message (remember that all chat messages start with "cc"). If so we simply append the this message (minus the "cc" header) to our chat text area and set the cursor position accordinlgy.

If it is not a chat message then we process it as an internal frame message. First we get the method ID and check if it is 16. (See discussion of the WindowManager class below for an explanation of what method each ID corresponds to):

1. If the id is 16 all we need to do is create a new frame, so the newFrame() method is invoked. (Note that only the client can send 'create new frame' messages.)

2. If the ID is not 16 we first call our WindowManager's custom setPropagate() message to stop processed messages sent to our WindowManager from being sent back to the client (this is effectively how we consume each message). Then we grab an array of all the components in layer 0 and check each JInternalFrame's TAG until we find a match. (Note that all frames are contained at layer 0 in this example. In a more complete example we would search through all components in the desktop, regardless of layer.)  We then extract the internal frame message parameters and if a TAG match is found we send a message to our WindowManager, m_wm, based on the id and extracted parameters (if applicable).

Class JavaXServer.ChatAdapter

ChatAdapter is used as an ActionListener attached to our console's chat text field and send button (see the setupConsole() method). Whenever the user presses enter in the chat input text field or clicks the console's "Send" button, this adapter sends the text field contents as a message to the client, appends it to the console chat text area, m_consolechat, and clears this text field.

Class JavaXServer.ConThread

Inner class ConThread extends Thread. Its constructor calls its start() method, and its start() method calls its run() method. We override the run() method to create an endless loop that starts by invoking accept() on our m_server ServerSocket. This method blocks execution until a client connects. When a client does connect our ServerSocket returns from the accept() method with a Socket, m_client, representing the connection with the client. We then set our m_connected flag to true so that when our main JavaXServer thread is started, it can receive and process messages from the client, as discussed above. We assign m_client's DataInputStream to m_input and its DataOutputStream to m_output. Then we pass m_output to our WindowManager's setOutputStream() method (see discussion of WindowManager below). We print a message in the console to inform the server operator that a connection has been established and then start() the JavaXServer thread (see the run() method--discussed above). Finally we display the IP address the client connected from in the status bar and append this information to our console.

Class WindowManager

The WindowManager class starts by defining 15 self-explanitory int id fields, each corresponding to one of the internal frame methods defined within this class. Four instance variables are used:

WindowWatcher ww: our custom pager component.

DataOutputStrean m_output: The client Socket's output stream used to send messages.

JDesktopPane m_desktop: The desktop we are managing.

boolean m_prop: Used to block messages from being sent back to the sender when being processed.

The setOutputStream() method is called when a client connects. This is used to provide WindowManager with a reference to the client Socket's DataOutputStream for sending messages.

The sendMessage() method takes a String parameter representing a message to be sent to the client and attempts to write it to the cleint's DataOutputStream:

  public void sendMessage(String s) {

    try {

      if (m_output != null) {

        m_output.writeUTF(s);

      }

    }

    catch (IOException e) {}

  }

The setPropagate() method takes a boolean parameter which is used to block messages from being sent back to the client when being processed. This method is called from JavaXServer's processMessage() method.

The getStringIndex() method takes an int parameter, representing the TAG of an internal frame, and converts it to a String 3 characters long by concatenating 0s in front if necessary. This is used to build the TAG field in an internal frame message (which, as we know from our discussion in the beginning of this section, is always 3 characters long) which is returned.

  public String getStringIndex(Component f) {

    String s = f.toString();

    while (s.length() < 3)

      s = ("0").concat(s);    

    return s;

  }

In the getString() method we take an int parameter which can represent any of four possible parameters passed to one of the internal frame methods defined within this class. We then convert this value to a String. If this value was negative we remove the "-" sign. Then we concatenate a number of 0s to the front of the string forcing the length to be 6. We then check if the value passed in was negative one more time. If it was we replace the first character with a "-" sign:

  public String getString(int number) {

    String s;

    if (number < 0)

      s = "" + (-number);

    else

      s = "" + number;

    while (s.length() < 6)

      s = ("0").concat(s);    

    if (number < 0)

      s = "-" + s.substring(1,6);

    return s;

  }

Note: This example assumes that no frame coordinate or dimension will ever be larger than 999999 or smaller than -99999. For all practical purposes, this is a completely safe assumption to make!

Whenever any of the internal frame methods are invoked we first get the TAG of the frame associated with the method call. Then we call the superclass counterpart of that method, repaint our WindowWatcher, and check our message propagation flag, m_prop. If it is set to true we go ahead and construct our message, using the getString() method where applicable, and pass it to our sendMessage method to send to the client. If it is false, no message is sent.

Class JavaXClient

JavaXClient functions very similar to JavaXServer. It sends, receives, and interprets messages identically to JavaXServer. However, unlike JavaXServer, JavaXClient can create new frames and send new frame messages to the server (new frame messages have ID 16)  These messages are constructed and sent within JavaXClient's actionPerformed method:

    if (e.getSource() == m_newFrame) {

      m_wm.setPropagate(false);

      newFrame();

      if (m_connected)

        m_wm.sendMessage("16000000000000000000000000000");

      m_wm.setPropagate(true);

    }

JavaXClient also has some additional GUI components added to the top of its frame. A text field for entering the server's IP address, a "New Frame" button, and a connect button. The ActionListener for this button is wrapped in a thread to allow connection attempts while not blocking the main thread of execution. It attempts to connect to the address specified in the text field by creating a new Socket:

                m_client = new Socket(

                  InetAddress.getByName(m_text.getText()),5000);

If this works it establishes input and output data streams and starts the JavaXClient thread (see its run() method--identical to JavaXServer's run() method) to listen for messages. We then append text to the console, update the status bar, and enable the "New Frame" button. (Note that the client can only create new frames after a connection has been established.)

Running the Code:

Figure 16.8 and 16.9 show JavaXClient and JavaXServer during a collaborative session. Ideally you test this example out with a friend in a remote, far-away place. If this is not possible try using two machines in your network. (If you are not networked you can run both client and the server on your machine by connecting to 127.0.0.1 which is always used as a pointer to your own machine.)

Try chatting and resizing each other's frames. Now is the time to think of other possible applications of such a multi-user desktop environment. Clearly we will begin to see more and more remote interaction and collaboration as the web and its surrounding technologies continue to grow.



[ Return to Swing (Book) ]


Top 10 read Java Articles
 Get free "1000 Java Tips eBook"

 Java Calendar and Date: good to know facts and code examples

 Array vs ArrayList vs LinkedList vs Vector: an excellent overview and examples

 How can I convert any Java Object into byte array? And byte array to file object

 The Java Lesson 1: What is Java?

 How do I compare two dates and times, date between dates, time between times and

 Maven vs Ant or Ant vs Maven?

 How to open, read, write, close file(s) in Java? Examples on move, rename and de

 Java Array

 Java: JLabel font and color


[ More in News Section ]
Java Lessons

The Java Lesson 1:
What is Java?
The Java Lesson 2:
Anatomy of a simple Java program
The Java Lesson 3:
Identifiers and primitive data types
The Java Lesson 4:
Variables, constants, and literals
The Java Lesson 5:
Arithmetic operations, conversions, and casts
The Java Lesson 6:
Boolean expressions and operations
The Java Lesson 7:
Bitwise operations
The Java Lesson 8:
Flow control with if and else
The Java Lesson 9:
switch statements
The Java Lesson 10:
for, while, and do-while statements
The Java Lesson 11:
Using break and continue
The Java Lesson 12:
Class methods and how they are called
The Java Lesson 13:
Using the Math class
The Java Lesson 14:
Creating and calling custom class methods
The Java Lesson 15:
Overloading class methods
The Java Lesson 16:
An introduction to objects and object references
The Java Lesson 17:
The String class
The Java Lesson 18:
The StringBuffer class
The Java Lesson 19:
Initializing and processing arrays of primitives
The Java Lesson 20:
Initializing and processing arrays of objects
The Java Lesson 23:
Inheritance and overriding inherited methods
The Java Lesson 24:
abstract classes and polymorphism
The Java Lesson 25:
Interfaces, instanceof, and object conversion and casting
The Java Lesson 26:
Introduction to graphical programming and the java.awt packa
The Java Lesson 27:
The Component class
The Java Lesson 28:
Containers and simple layout managers
The Java Lesson 29:
The Color and Font classes
The Java Lesson 30:
Drawing geometric shapes
The Java Lesson 31:
Choice, List, and Checkbox controls
The Java Lesson 32:
Using the Scrollbar graphical control
The Java Lesson 33:
Menus and submenus
The Java Lesson 34:
An introduction to applets and the Applet class
The Java Lesson 35:
Essential HTML to launch an applet and pass it parameters
The Java Lesson 36:
Mouse event processing
Java Lesson 37:
Menus and submenus
Java Lesson 38:
The WindowListener interface and the WindowAdapter class
Java Lesson 39:
An introduction to GridBagLayout
Java Lesson 40:
An introduction to the Java Collections API
Java Lesson 41:
Exception handling with try, catch, and finally blocks
Java Lesson 42:
Claiming and throwing exceptions
Java Lesson 43:
Multithreading, the Thread class, and the Runnable interface
Java Lesson 44:
An introduction to I/O and the File and FileDialog classes
Java Lesson 45:
Low-level and high-level stream classes
Java Lesson 46:
Using the RandomAccessFile class
Java Lessons by
Joh Huhtala: Update

Latest articles
 Java Profiler JProbe to Resolve Performance Problems Faster

 SSL with GlassFish v2, page 5

 SSL with GlassFish v2, page 4

 SSL with GlassFish v2, page 3

 SSL with GlassFish v2, page 2

 The Java Lesson 2: Anatomy of a simple Java program, page 2

 New site about Java for robots and robotics: both software and hardware.

 Exceptions -III: What's an exception and why do I care?

 Exceptions -II: What's an exception and why do I care?

 Exceptions: What's an exception and why do I care?

 Double your Java code quality in 10 minutes, here is receipt

 Murach's Java Servlets and JSP

 How to get ascii code from a char in Java?

 Can we just try without catch? Yes!

 Make Tomcat page load faster

 Make your Tomcat More secure - limit network address for certain IP addresses

 New Java book online starts now here...

 Implementing RESTful Web Services in Java

 Firefox trimming from 1 GB to 40 Mb with many tabs opened

 SSL with GlassFish v2

 My request to replublish Tech Tips

 Search JavaFAQ.nu site here

 New Advanced Installer for Java 6.0 brings XML updates and imports 3rd party MSI

 EJB programming restrictions

 Maven vs Ant or Ant vs Maven?

 Why Java does not use default value which it should?

 How to unsign signed bytes in Java - your guide is here

 The Java Lesson 3: Identifiers and primitive data types. Page 2

 The Java Lesson 7: Bitwise operations with good examples, click here! Page 4

 The Java Lesson 7: Bitwise operations with good examples, click here! Page 3


[ More in News Section ]


Home Code Examples Java Forum All Java Tips Books Submit News, Code... Search... Offshore Software Tech Doodling

RSS feed Java FAQ RSS feed Java FAQ News     

    RSS feed Java Forums RSS feed Java Forums

All logos and trademarks in this site are property of their respective owner. The comments are property of their posters, all the rest 1999-2006 by Java FAQs Daily Tips.

Interactive software released under GNU GPL, Code Credits, Privacy Policy